diff --git a/app/hooks/useDraftSocketEvents.ts b/app/hooks/useDraftSocketEvents.ts index 43efa25..489ef93 100644 --- a/app/hooks/useDraftSocketEvents.ts +++ b/app/hooks/useDraftSocketEvents.ts @@ -41,6 +41,7 @@ interface UseDraftSocketEventsParams { setWatchedParticipantIds: (value: Set) => void; setRoomClosed: (value: boolean) => void; setIsSyncing: (value: boolean) => void; + setBracktVorps: (fn: (prev: Map) => Map) => void; } export function useDraftSocketEvents({ @@ -70,6 +71,7 @@ export function useDraftSocketEvents({ setWatchedParticipantIds, setRoomClosed, setIsSyncing, + setBracktVorps, }: UseDraftSocketEventsParams) { useEffect(() => { type PickMadePayload = { @@ -238,6 +240,18 @@ export function useDraftSocketEvents({ setRoomClosed(true); }; + const handleBracktEvsUpdated = (data: { + updates: Array<{ participantId: string; vorpValue: number }>; + }) => { + setBracktVorps((prev) => { + const next = new Map(prev); + for (const { participantId, vorpValue } of data.updates) { + next.set(participantId, vorpValue); + } + return next; + }); + }; + on("pick-made", handlePickMade as (data: unknown) => void); on("timer-update", handleTimerUpdate as (data: unknown) => void); on("draft-paused", handleDraftPaused as (data: unknown) => void); @@ -255,6 +269,7 @@ export function useDraftSocketEvents({ on("draft-state-sync", handleDraftStateSync as (data: unknown) => void); on("watchlist-updated", handleWatchlistUpdated as (data: unknown) => void); on("draft-room-closed", handleDraftRoomClosed as (data: unknown) => void); + on("brackt-evs-updated", handleBracktEvsUpdated as (data: unknown) => void); return () => { off("pick-made", handlePickMade as (data: unknown) => void); @@ -274,6 +289,7 @@ export function useDraftSocketEvents({ off("draft-state-sync", handleDraftStateSync as (data: unknown) => void); off("watchlist-updated", handleWatchlistUpdated as (data: unknown) => void); off("draft-room-closed", handleDraftRoomClosed as (data: unknown) => void); + off("brackt-evs-updated", handleBracktEvsUpdated as (data: unknown) => void); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [on, off, socketVersion]); diff --git a/app/lib/audit-log-display.ts b/app/lib/audit-log-display.ts index 152b08f..231822e 100644 --- a/app/lib/audit-log-display.ts +++ b/app/lib/audit-log-display.ts @@ -17,6 +17,7 @@ export const AUDIT_ACTION_LABELS: Record = { force_manual_pick: "Forced Manual Pick", draft_pick_changed: "Pick Replaced", time_bank_edited: "Time Bank Edited", + brackt_resolved: "Brackt Resolved", }; const SCORING_FIELD_LABELS: Record = { @@ -201,6 +202,9 @@ export function formatAuditDetail(entry: AuditLogEntry): string { return parts.length > 0 ? parts.join(" | ") : "Sports updated"; } + case "brackt_resolved": + return d.trigger === "auto" ? "Brackt resolved automatically" : "Brackt resolution retried"; + default: return (entry.action as string).replace(/_/g, " "); } diff --git a/app/lib/wizard-state.ts b/app/lib/wizard-state.ts index 7c26476..2b01a8c 100644 --- a/app/lib/wizard-state.ts +++ b/app/lib/wizard-state.ts @@ -48,7 +48,9 @@ export function buildFormDataFromSnapshot(s: WizardSnapshot): FormData { const fd = new FormData(); fd.append("name", s.leagueName); fd.append("teamCount", s.teamCount.toString()); - fd.append("templateId", s.selectedTemplate); + if (s.selectedTemplate && s.selectedTemplate !== "customize") { + fd.append("templateId", s.selectedTemplate); + } fd.append("draftRounds", s.draftRounds.toString()); if (s.draftDate && s.draftTime) { fd.append("draftDateTime", new Date(`${s.draftDate}T${s.draftTime}`).toISOString()); diff --git a/app/models/draft-utils.ts b/app/models/draft-utils.ts index 47a844e..517f8fe 100644 --- a/app/models/draft-utils.ts +++ b/app/models/draft-utils.ts @@ -9,6 +9,7 @@ import { getParticipantsForSeasonWithSports } from "./season-participant"; import { getSeasonSportsSimple } from "./season-sport"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; import { getSocketIO, scheduleDraftRoomClosure } from "../../server/socket"; +import { runBracktHarvilleForFantasySeason } from "~/services/brackt.server"; /** * Check if the next team has autodraft enabled and immediately execute their pick @@ -811,6 +812,17 @@ export async function executeAutoPick(params: { logger.error("[AutoPick] Socket.IO events error:", error); } + // Recompute Brackt EV/VORP after this pick is committed so autopick paths + // (force-autopick, timer, user autodraft) are not one pick behind. + try { + const updates = await runBracktHarvilleForFantasySeason(seasonId, db); + if (updates.length > 0) { + getSocketIO().to(`draft-${seasonId}`).emit("brackt-evs-updated", { updates }); + } + } catch (error) { + logger.error("[AutoPick] Error updating Brackt EVs after pick:", error); + } + // Check if next team has autodraft enabled and trigger immediately // Only run the chain from the top-level call to prevent recursion if (!isDraftComplete && params.chainEnabled !== false) { diff --git a/app/models/participant-expected-value.ts b/app/models/participant-expected-value.ts index bd6e670..a3d3775 100644 --- a/app/models/participant-expected-value.ts +++ b/app/models/participant-expected-value.ts @@ -57,10 +57,13 @@ export interface UpdateProbabilityInput { * * Call this after any operation that changes EVs for seasonParticipants in the season. */ -export async function syncVorpForSeason(sportsSeasonId: string): Promise { - const db = database(); +export async function syncVorpForSeason( + sportsSeasonId: string, + providedDb?: ReturnType +): Promise { + const db = providedDb || database(); - const allEvs = await getAllParticipantEVsForSeason(sportsSeasonId); + const allEvs = await getAllParticipantEVsForSeason(sportsSeasonId, db); if (allEvs.length === 0) return; const sorted = [...allEvs].toSorted( @@ -239,9 +242,10 @@ export async function getParticipantEV( * Get all participant EVs for a sports season */ export async function getAllParticipantEVsForSeason( - sportsSeasonId: string + sportsSeasonId: string, + providedDb?: ReturnType ): Promise { - const db = database(); + const db = providedDb || database(); return db .select() .from(seasonParticipantExpectedValues) @@ -280,9 +284,10 @@ export async function deleteParticipantEV( * All records succeed or all fail together. */ export async function batchUpsertParticipantEVs( - inputs: CreateProbabilityInput[] + inputs: CreateProbabilityInput[], + providedDb?: ReturnType ): Promise { - const db = database(); + const db = providedDb || database(); const results: ParticipantEV[] = []; await db.transaction(async (tx) => { @@ -355,7 +360,7 @@ export async function batchUpsertParticipantEVs( // Sync VORP for all affected seasons const uniqueSeasonIds = [...new Set(inputs.map((i) => i.sportsSeasonId))]; - await Promise.all(uniqueSeasonIds.map((id) => syncVorpForSeason(id))); + await Promise.all(uniqueSeasonIds.map((id) => syncVorpForSeason(id, db))); return results; } diff --git a/app/models/scoring-calculator.ts b/app/models/scoring-calculator.ts index 249fc69..06b2701 100644 --- a/app/models/scoring-calculator.ts +++ b/app/models/scoring-calculator.ts @@ -775,6 +775,7 @@ export async function finalizeQualifyingPoints( .update(schema.sportsSeasons) .set({ qualifyingPointsFinalized: true, + status: "completed", updatedAt: new Date(), }) .where(eq(schema.sportsSeasons.id, sportsSeasonId)); @@ -816,10 +817,11 @@ export async function processSeasonStandings( // Group participants by position to handle ties const positionGroups: Record = {}; + const processedParticipantIds = new Set(); for (const result of seasonResults) { const position = result.currentPosition; - if (position === null) continue; // Skip participants without a position + if (position === null) continue; // handled after the loop if (!positionGroups[position]) { positionGroups[position] = []; @@ -838,8 +840,14 @@ export async function processSeasonStandings( for (const position of positions) { const participantIds = positionGroups[position]; - // Stop if we've gone beyond the top 8 fantasy placements - if (currentPlacement > 8) break; + if (currentPlacement > 8) { + // Beyond top 8 — assign 0 to all remaining positioned participants + for (const participantId of participantIds) { + processedParticipantIds.add(participantId); + await upsertParticipantResult(participantId, sportsSeasonId, 0, db); + } + continue; + } // Calculate how many placements this group shares const participantsInGroup = participantIds.length; @@ -848,47 +856,33 @@ export async function processSeasonStandings( const maxPlacementForGroup = Math.min(8, currentPlacement + participantsInGroup - 1); const actualParticipantsScoring = maxPlacementForGroup - currentPlacement + 1; - // Determine if we should assign placements to this group - if (currentPlacement <= 8) { - // All participants tied at this position get the first placement in the range - // (e.g., if 4 people tie for 5th place, they all get placement 5) - // The scoring system will handle averaging the points for positions 5, 6, 7, 8 - for (let i = 0; i < Math.min(participantIds.length, actualParticipantsScoring); i++) { - const participantId = participantIds[i]; - await upsertParticipantResult( - participantId, - sportsSeasonId, - currentPlacement, - db - ); - } + // All participants tied at this position get the first placement in the range + // (e.g., if 4 people tie for 5th place, they all get placement 5) + // The scoring system will handle averaging the points for positions 5, 6, 7, 8 + for (let i = 0; i < Math.min(participantIds.length, actualParticipantsScoring); i++) { + const participantId = participantIds[i]; + processedParticipantIds.add(participantId); + await upsertParticipantResult(participantId, sportsSeasonId, currentPlacement, db); + } - // If there are more participants than scoring positions, assign 0 to the rest - for (let i = actualParticipantsScoring; i < participantIds.length; i++) { - const participantId = participantIds[i]; - await upsertParticipantResult( - participantId, - sportsSeasonId, - 0, // Beyond top 8 - db - ); - } - } else { - // Position is beyond top 8, assign 0 points - for (const participantId of participantIds) { - await upsertParticipantResult( - participantId, - sportsSeasonId, - 0, - db - ); - } + // If there are more participants than scoring positions, assign 0 to the rest + for (let i = actualParticipantsScoring; i < participantIds.length; i++) { + const participantId = participantIds[i]; + processedParticipantIds.add(participantId); + await upsertParticipantResult(participantId, sportsSeasonId, 0, db); } // Move to next placement group currentPlacement += participantsInGroup; } + // Assign 0 to participants with no championship position recorded + for (const result of seasonResults) { + if (!processedParticipantIds.has(result.participantId)) { + await upsertParticipantResult(result.participantId, sportsSeasonId, 0, db); + } + } + // Trigger recalculation for all affected leagues await recalculateAffectedLeagues(sportsSeasonId, db, { eventName: "Season Complete" }); diff --git a/app/models/sports-season.ts b/app/models/sports-season.ts index 463a908..97467fc 100644 --- a/app/models/sports-season.ts +++ b/app/models/sports-season.ts @@ -1,7 +1,8 @@ -import { eq, inArray, sql, lte, gte } from "drizzle-orm"; +import { eq, inArray, isNull, sql, lte, gte } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { getQPConfig, updateQPConfig } from "~/models/qualifying-points"; +import { maybeResolveCompletedBracktForSportsSeason } from "~/services/brackt.server"; export async function countSportsSeasons(): Promise { const db = database(); @@ -122,6 +123,12 @@ export async function findAllSportsSeasons(): Promise { }); } +export async function findAllAdminSportsSeasons(): Promise { + const seasons = await findAllSportsSeasons(); + // Hide per-league private copies (fantasySeasonId IS NOT NULL); show the global template + return seasons.filter((season) => season.fantasySeasonId === null); +} + export async function findDraftableSportsSeasons() { const db = database(); const today = sql`CURRENT_DATE`; @@ -129,7 +136,8 @@ export async function findDraftableSportsSeasons() { where: (ss, { and }) => and( lte(ss.draftOn, today), - gte(ss.draftOff, today) + gte(ss.draftOff, today), + isNull(ss.fantasySeasonId) ), orderBy: (sportsSeasons, { desc, asc }) => [desc(sportsSeasons.year), asc(sportsSeasons.name)], with: { @@ -148,11 +156,20 @@ export async function updateSportsSeason( data: Partial ): Promise { const db = database(); + const previous = await db.query.sportsSeasons.findFirst({ + where: eq(schema.sportsSeasons.id, id), + columns: { status: true }, + }); const [sportsSeason] = await db .update(schema.sportsSeasons) .set({ ...data, updatedAt: new Date() }) .where(eq(schema.sportsSeasons.id, id)) .returning(); + + if (previous && previous.status !== "completed" && sportsSeason.status === "completed") { + await maybeResolveCompletedBracktForSportsSeason(sportsSeason.id, db); + } + return sportsSeason; } diff --git a/app/routes/admin._index.tsx b/app/routes/admin._index.tsx index e9f5c8e..0438a5e 100644 --- a/app/routes/admin._index.tsx +++ b/app/routes/admin._index.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router"; +import { Form, Link } from "react-router"; import { useState } from "react"; import type { Route } from "./+types/admin._index"; @@ -17,6 +17,16 @@ import { import { Button } from "~/components/ui/button"; import { Badge } from "~/components/ui/badge"; import { Trophy, Calendar, FolderKanban, ArrowRight, CheckCircle2, Clock, ExternalLink, Users } from "lucide-react"; +import { seedOrRepairBracktTemplate } from "~/services/brackt.server"; + +export async function action({ request }: Route.ActionArgs) { + const formData = await request.formData(); + if (formData.get("intent") === "seed-brackt") { + await seedOrRepairBracktTemplate(); + return { success: true, message: "Brackt sport and template repaired." }; + } + return { error: "Invalid action" }; +} export function meta(): Route.MetaDescriptors { return [{ title: "Admin - Brackt" }]; @@ -250,7 +260,7 @@ function GamesToScore({ ); } -export default function AdminDashboard({ loaderData }: Route.ComponentProps) { +export default function AdminDashboard({ loaderData, actionData }: Route.ComponentProps) { const { stats, seasonStatusCounts, upcomingEvents, today, tomorrow } = loaderData; // Serialize dates to strings for the client component @@ -374,6 +384,16 @@ export default function AdminDashboard({ loaderData }: Route.ComponentProps) { +
+ + +
+ {actionData && "message" in actionData && ( +

{actionData.message}

+ )} diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts index f401485..e6d97a4 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts @@ -52,6 +52,7 @@ import { logger } from "~/lib/logger"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq } from "drizzle-orm"; +import { maybeResolveCompletedBracktForSportsSeason } from "~/services/brackt.server"; export async function loader({ params }: Route.LoaderArgs) { const sportsSeason = await findSportsSeasonById(params.id); @@ -723,6 +724,11 @@ export async function action({ request, params }: Route.ActionArgs) { .set({ isComplete: true, completedAt: new Date(), updatedAt: new Date() }) .where(eq(schema.scoringEvents.id, params.eventId)); + await db + .update(schema.sportsSeasons) + .set({ status: "completed", updatedAt: new Date() }) + .where(eq(schema.sportsSeasons.id, params.id)); + // Recalculate standings for all affected fantasy seasons const seasonSports = await findSeasonSportsBySportsSeasonId(params.id); @@ -730,6 +736,7 @@ export async function action({ request, params }: Route.ActionArgs) { await recalculateStandings(seasonSport.seasonId, db); await createDailySnapshot(seasonSport.seasonId, db); } + await maybeResolveCompletedBracktForSportsSeason(params.id, db); return { success: `Bracket finalized! All placements calculated and standings updated.`, diff --git a/app/routes/admin.sports-seasons.$id.events.server.ts b/app/routes/admin.sports-seasons.$id.events.server.ts index 63e421e..d0bb526 100644 --- a/app/routes/admin.sports-seasons.$id.events.server.ts +++ b/app/routes/admin.sports-seasons.$id.events.server.ts @@ -11,6 +11,7 @@ import { } from "~/models/scoring-event"; import { getQPStandings } from "~/models/qualifying-points"; import { finalizeQualifyingPoints } from "~/models/scoring-calculator"; +import { maybeResolveCompletedBracktForSportsSeason } from "~/services/brackt.server"; import { upsertTournament, findTournamentsBySport, getTournamentById } from "~/models/tournament"; import { extractTournamentIdentity } from "~/lib/tournament-identity"; @@ -87,6 +88,7 @@ export async function action({ request, params }: Route.ActionArgs) { if (intent === "finalize-qp") { try { await finalizeQualifyingPoints(params.id); + await maybeResolveCompletedBracktForSportsSeason(params.id); return { success: "Qualifying points finalized and fantasy placements assigned to top 8!" }; } catch (error) { logger.error("Error finalizing qualifying points:", error); diff --git a/app/routes/admin.sports-seasons.tsx b/app/routes/admin.sports-seasons.tsx index 95c0000..87ac14b 100644 --- a/app/routes/admin.sports-seasons.tsx +++ b/app/routes/admin.sports-seasons.tsx @@ -1,7 +1,7 @@ import { Link } from "react-router"; import type { Route } from "./+types/admin.sports-seasons"; -import { findAllSportsSeasons } from "~/models/sports-season"; +import { findAllAdminSportsSeasons } from "~/models/sports-season"; import { Card, CardContent, @@ -26,7 +26,7 @@ export function meta(): Route.MetaDescriptors { } export async function loader() { - const sportsSeasons = await findAllSportsSeasons(); + const sportsSeasons = await findAllAdminSportsSeasons(); return { sportsSeasons }; } diff --git a/app/routes/api/draft.force-manual-pick.ts b/app/routes/api/draft.force-manual-pick.ts index 8ffe099..f72e639 100644 --- a/app/routes/api/draft.force-manual-pick.ts +++ b/app/routes/api/draft.force-manual-pick.ts @@ -11,6 +11,7 @@ import { calculatePickInfo, checkAndTriggerNextAutodraft } from "~/models/draft- import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO, scheduleDraftRoomClosure } from "../../../server/socket"; import { logger } from "~/lib/logger"; +import { runBracktHarvilleForFantasySeason } from "~/services/brackt.server"; import type { ActionFunctionArgs } from "react-router"; export async function action(args: ActionFunctionArgs) { @@ -280,6 +281,15 @@ export async function action(args: ActionFunctionArgs) { // Check if next team has autodraft enabled and trigger immediately if (!isDraftComplete) { + try { + const updates = await runBracktHarvilleForFantasySeason(seasonId, db); + if (updates.length > 0) { + getSocketIO().to(`draft-${seasonId}`).emit("brackt-evs-updated", { updates }); + } + } catch (error) { + logger.error("Brackt EV update after forced manual pick failed:", error); + } + const freshSeason = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId) }); if (!freshSeason?.draftPaused) { await checkAndTriggerNextAutodraft({ diff --git a/app/routes/api/draft.make-pick.ts b/app/routes/api/draft.make-pick.ts index 65db0a0..b7a9bf9 100644 --- a/app/routes/api/draft.make-pick.ts +++ b/app/routes/api/draft.make-pick.ts @@ -10,6 +10,7 @@ import { getSeasonSportsSimple } from "~/models/season-sport"; import { calculatePickInfo, checkAndTriggerNextAutodraft, pruneIneligibleQueueItems } from "~/models/draft-utils"; import { getSocketIO, scheduleDraftRoomClosure } from "../../../server/socket"; import { logger } from "~/lib/logger"; +import { runBracktHarvilleForFantasySeason } from "~/services/brackt.server"; import type { ActionFunctionArgs } from "react-router"; export async function action(args: ActionFunctionArgs) { @@ -302,6 +303,15 @@ export async function action(args: ActionFunctionArgs) { // Check if next team has autodraft enabled and trigger immediately if (!isDraftComplete) { + try { + const updates = await runBracktHarvilleForFantasySeason(seasonId, db); + if (updates.length > 0) { + getSocketIO().to(`draft-${seasonId}`).emit("brackt-evs-updated", { updates }); + } + } catch (error) { + logger.error("Brackt EV update after pick failed:", error); + } + const freshSeason = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId) }); if (!freshSeason?.draftPaused) { await checkAndTriggerNextAutodraft({ diff --git a/app/routes/api/draft.rollback.ts b/app/routes/api/draft.rollback.ts index f6e3d18..656b718 100644 --- a/app/routes/api/draft.rollback.ts +++ b/app/routes/api/draft.rollback.ts @@ -6,6 +6,7 @@ import { isCommissioner } from "~/models/commissioner"; import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; +import { runBracktHarvilleForFantasySeason } from "~/services/brackt.server"; import type { ActionFunctionArgs } from "react-router"; export async function action(args: ActionFunctionArgs) { @@ -97,11 +98,8 @@ export async function action(args: ActionFunctionArgs) { }, }); - // Emit socket events try { - const io = getSocketIO(); - - io.to(`draft-${seasonId}`).emit("draft-rolled-back", { + getSocketIO().to(`draft-${seasonId}`).emit("draft-rolled-back", { seasonId, pickNumber, teamId: rollbackSlot?.teamId, @@ -110,5 +108,14 @@ export async function action(args: ActionFunctionArgs) { logger.error("Socket.IO error:", error); } + try { + const updates = await runBracktHarvilleForFantasySeason(seasonId, db); + if (updates.length > 0) { + getSocketIO().to(`draft-${seasonId}`).emit("brackt-evs-updated", { updates }); + } + } catch (error) { + logger.error("[Rollback] Brackt EV update failed:", error); + } + return Response.json({ success: true, pickNumber }); } diff --git a/app/routes/how-to-play.tsx b/app/routes/how-to-play.tsx index 426e3b4..c6bb607 100644 --- a/app/routes/how-to-play.tsx +++ b/app/routes/how-to-play.tsx @@ -195,6 +195,30 @@ export default function HowToPlay() { + {/* The Meta Game */} +
+

The Meta Game: Brackt

+

+ Some leagues include Brackt as + an optional meta-sport. Instead of drafting an athlete or team, you draft{" "} + another manager from your own league. + At the end of the season, their final overall fantasy ranking — 1st through 8th across + all the other sports — earns you the standard placement points. +

+
+

Strategic angles:

+
    +
  • Draft timing matters — managers who pick strong early show higher projected value.
  • +
  • Drafting yourself is allowed and is a valid strategy.
  • +
  • Brackt scores are revealed at season end, after all other sports finalize.
  • +
+
+

+ Brackt is resolved automatically once every other sport concludes. It's the last piece + of the puzzle — and sometimes the most dramatic. +

+
+ {/* During the Season */}

After the Draft

diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index 1c5bdeb..9b630a7 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -179,7 +179,7 @@ export default function DraftRoom() { season, draftSlots, draftPicks, - availableParticipants, + availableParticipants: initialAvailableParticipants, userTeam, userQueue, userWatchlist, @@ -191,6 +191,17 @@ export default function DraftRoom() { ownerMap, teamTimezoneMap, } = useLoaderData(); + const availableParticipants = initialAvailableParticipants; + const [bracktVorps, setBracktVorps] = useState>(() => new Map()); + const sortedAvailableParticipants = useMemo(() => { + if (bracktVorps.size === 0) return availableParticipants; + return availableParticipants.toSorted((a, b) => { + const aVorp = bracktVorps.get(a.id) ?? parseFloat(a.vorpValue ?? "0"); + const bVorp = bracktVorps.get(b.id) ?? parseFloat(b.vorpValue ?? "0"); + const diff = bVorp - aVorp; + return diff !== 0 ? diff : a.name.localeCompare(b.name); + }); + }, [availableParticipants, bracktVorps]); const navigate = useNavigate(); const { revalidate, state: revalidatorState } = useRevalidator(); const { isConnected, connectionError, isReconnecting, reconnectCount, socketVersion, on, off } = useDraftSocket(season.id, userTeam?.id); @@ -383,13 +394,13 @@ export default function DraftRoom() { const participantRanks = useMemo(() => { const ranks = new Map(); const sportCounters = new Map(); - availableParticipants.forEach((p, i) => { + sortedAvailableParticipants.forEach((p, i) => { const sportIdx = (sportCounters.get(p.sport.id) ?? 0) + 1; sportCounters.set(p.sport.id, sportIdx); ranks.set(p.id, { overallRank: i + 1, sportRank: sportIdx }); }); return ranks; - }, [availableParticipants]); + }, [sortedAvailableParticipants]); // Calculate draft eligibility for current user's team const eligibility = useMemo(() => { @@ -472,6 +483,7 @@ export default function DraftRoom() { setWatchedParticipantIds, setRoomClosed, setIsSyncing, + setBracktVorps, }); // Persist sidebar collapsed state @@ -1063,7 +1075,7 @@ export default function DraftRoom() { // Filter participants based on search, sport, drafted status, and eligibility const filteredParticipants = useMemo(() => { const sportFilterSet = new Set(sportFilters); - return availableParticipants.filter((participant) => { + return sortedAvailableParticipants.filter((participant) => { // Drafted filter - hide drafted participants by default if (hideDrafted && draftedParticipantIds.has(participant.id)) { return false; @@ -1096,7 +1108,7 @@ export default function DraftRoom() { } return true; }); - }, [availableParticipants, hideDrafted, hideIneligible, hideCompletedSports, eligibility, draftedParticipantIds, userDraftedSportIds, searchQuery, sportFilters, showOnlyWatched, watchedParticipantIds]); + }, [sortedAvailableParticipants, hideDrafted, hideIneligible, hideCompletedSports, eligibility, draftedParticipantIds, userDraftedSportIds, searchQuery, sportFilters, showOnlyWatched, watchedParticipantIds]); // Shared component props — defined once to avoid duplication between desktop and mobile layouts const handleHideCompletedSportsChange = useCallback((hide: boolean) => { @@ -1203,7 +1215,7 @@ export default function DraftRoom() { const queueSectionProps = userTeam ? { queue, - availableParticipants, + availableParticipants: sortedAvailableParticipants, canPick, onRemoveFromQueue: handleRemoveFromQueue, onReorder: handleReorderQueue, @@ -1601,7 +1613,7 @@ export default function DraftRoom() { }} title="Force Manual Pick" description={`Select a participant to draft for Pick #${selectedPickSlot?.pickNumber}`} - participants={availableParticipants} + participants={sortedAvailableParticipants} draftedParticipantIds={draftedParticipantIds} eligibility={forcePickEligibility} uniqueSports={uniqueSports} @@ -1616,7 +1628,7 @@ export default function DraftRoom() { }} title="Replace Pick" description={`Select a participant to replace the current pick at slot #${replacePickSlot?.pickNumber}`} - participants={availableParticipants} + participants={sortedAvailableParticipants} draftedParticipantIds={draftedParticipantIds} allowParticipantId={replacePickSlot?.oldParticipantId} currentParticipantId={replacePickSlot?.oldParticipantId} diff --git a/app/routes/leagues/$leagueId.settings.tsx b/app/routes/leagues/$leagueId.settings.tsx index ff012d9..f9e573a 100644 --- a/app/routes/leagues/$leagueId.settings.tsx +++ b/app/routes/leagues/$leagueId.settings.tsx @@ -72,6 +72,11 @@ import { DraftSpeedPicker } from "~/components/league/DraftSpeedPicker"; import { OvernightPauseSettings } from "~/components/league/OvernightPauseSettings"; import { ScoringPresetPicker, OMNIFANTASY_SCORING } from "~/components/league/ScoringPresetPicker"; import { toast } from "sonner"; +import { + applyBracktSportsForSeason, + seedOrRepairBracktTemplate, + syncPrivateBracktParticipants, +} from "~/services/brackt.server"; function isValidHHMM(s: string): boolean { return /^\d{2}:\d{2}$/.test(s); @@ -81,6 +86,8 @@ export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Settings — ${data?.league?.name ?? "League"} - Brackt` }]; } +const BRACKT_SLUG = "brackt"; + export async function loader(args: Route.LoaderArgs) { const session = await auth.api.getSession({ headers: args.request.headers }); const userId = session?.user.id ?? null; @@ -108,6 +115,8 @@ export async function loader(args: Route.LoaderArgs) { }); } + await seedOrRepairBracktTemplate(); + // Get current season with sports and draftable seasons in parallel const [season, draftableSportsSeasons] = await Promise.all([ findCurrentSeasonWithSports(leagueId), @@ -120,7 +129,10 @@ export async function loader(args: Route.LoaderArgs) { const draftableIds = new Set(draftableSportsSeasons.map((s) => s.id)); const linkedButNotDraftable = (season?.seasonSports ?? []) .map((s) => s.sportsSeason) - .filter((ss) => !draftableIds.has(ss.id) && (ss.draftOff < today || ss.draftOn > today)); + .filter((ss) => { + const fantasySeasonId = (ss as typeof ss & { fantasySeasonId?: string | null }).fantasySeasonId; + return !draftableIds.has(ss.id) && (fantasySeasonId || ss.draftOff < today || ss.draftOn > today); + }); const allSportsSeasons = [...draftableSportsSeasons, ...linkedButNotDraftable]; const draftSlots = season ? await findDraftSlotsBySeasonId(season.id) : []; @@ -173,7 +185,10 @@ export async function loader(args: Route.LoaderArgs) { teams, teamCount: teams.length, teamsWithOwners, - allSportsSeasons: allSportsSeasons as Array, + allSportsSeasons: allSportsSeasons as Array, draftSlots, isAdmin, allUsers, @@ -672,11 +687,12 @@ export async function action(args: Route.ActionArgs) { // Handle sports changes (only valid in pre_draft) if (season.status === "pre_draft") { - const selectedSports = formData.getAll("sportsSeasons"); + const selectedSports = formData.getAll("sportsSeasons").map(String); const currentSportIds = new Set( season.seasonSports?.map((s) => s.sportsSeason.id) || [] ); - const newSportIds = new Set(selectedSports as string[]); + const sportsToApply = await applyBracktSportsForSeason(season.id, selectedSports); + const newSportIds = new Set(sportsToApply); for (const sportId of currentSportIds) { if (!newSportIds.has(sportId)) { @@ -689,7 +705,7 @@ export async function action(args: Route.ActionArgs) { if (!currentSportIds.has(sportId)) { sportsToAdd.push({ seasonId: season.id, - sportsSeasonId: sportId as string, + sportsSeasonId: sportId, }); } } @@ -769,15 +785,20 @@ export async function action(args: Route.ActionArgs) { })) ); }); + await syncPrivateBracktParticipants(season.id); } else if (newTeamCount < currentTeamCount) { + if (season.status !== "pre_draft") { + return { error: "Teams cannot be removed after the draft has started" }; + } // Remove teams without owners (from the end) const teamsToRemove = teams .filter(t => t.ownerId === null) .slice(-(currentTeamCount - newTeamCount)); - + for (const team of teamsToRemove) { await deleteTeam(team.id); } + await syncPrivateBracktParticipants(season.id); } } } @@ -886,7 +907,24 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone return "standard"; }); const [selectedSports, setSelectedSports] = useState>( - new Set(season?.seasonSports?.map((s) => s.sportsSeason.id) || []) + () => { + const linkedSeasons = season?.seasonSports?.map((s) => s.sportsSeason) ?? []; + const linkedIds = linkedSeasons.map((ss) => ss.id); + const templateBrackt = allSportsSeasons.find((ss) => { + const fantasySeasonId = (ss as typeof ss & { fantasySeasonId?: string | null }).fantasySeasonId; + return ss.sport.slug === BRACKT_SLUG && !fantasySeasonId; + }); + const privateBrackt = linkedSeasons.find((ss) => { + const fantasySeasonId = (ss as typeof ss & { fantasySeasonId?: string | null }).fantasySeasonId; + return ss.sport.slug === BRACKT_SLUG && !!fantasySeasonId; + }); + + if (templateBrackt && privateBrackt) { + return new Set(linkedIds.map((id) => (id === privateBrackt.id ? templateBrackt.id : id))); + } + + return new Set(linkedIds); + } ); const [draftOrderTeams, setDraftOrderTeams] = useState( draftSlots.length > 0 @@ -937,6 +975,18 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone // The form submission will handle the actual deletion }; + const displaySportsSeasons = (() => { + const templateBrackt = allSportsSeasons.find((ss) => { + const fantasySeasonId = (ss as typeof ss & { fantasySeasonId?: string | null }).fantasySeasonId; + return ss.sport.slug === BRACKT_SLUG && !fantasySeasonId; + }); + if (!templateBrackt) return allSportsSeasons; + return allSportsSeasons.filter((ss) => { + const fantasySeasonId = (ss as typeof ss & { fantasySeasonId?: string | null }).fantasySeasonId; + return !(ss.sport.slug === BRACKT_SLUG && !!fantasySeasonId); + }); + })(); + const moveDraftSlot = (fromIndex: number, toIndex: number) => { const newOrder = [...draftOrderTeams]; const [movedTeam] = newOrder.splice(fromIndex, 1); @@ -1311,8 +1361,8 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone Sports Seasons ({selectedSports.size} selected)
- {allSportsSeasons.length > 0 ? ( - allSportsSeasons.map((ss) => ( + {displaySportsSeasons.length > 0 ? ( + displaySportsSeasons.map((ss) => (
- {ss.sport.name} - {ss.name} ({ss.year}) + {ss.sport.slug === BRACKT_SLUG ? ( + Brackt + ) : ( + <> + {ss.sport.name} - {ss.name} ({ss.year}) + + )} {ss.scoringType.replace("_", " ")} diff --git a/app/routes/leagues/new.tsx b/app/routes/leagues/new.tsx index b952755..801427e 100644 --- a/app/routes/leagues/new.tsx +++ b/app/routes/leagues/new.tsx @@ -15,7 +15,7 @@ import { findDraftableSportsSeasons } from "~/models/sports-season"; import { findUserById, updateUser } from "~/models/user"; import { generateUniqueTeamNames, prependOwnerToTeamName } from "~/utils/team-names"; import { format } from "date-fns"; -import { CalendarClock, Check, Star, Swords, Trophy } from "lucide-react"; +import { CalendarClock, Check, Info, Star, Swords, Trophy } from "lucide-react"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Card, CardContent } from "~/components/ui/card"; @@ -36,6 +36,10 @@ import { WizardStepper } from "~/components/league/WizardStepper"; import { ReviewSection } from "~/components/league/ReviewSection"; import { StepperInput } from "~/components/league/StepperInput"; import { WizardAuthForm } from "~/components/league/WizardAuthForm"; +import { + applyBracktSportsForSeason, + seedOrRepairBracktTemplate, +} from "~/services/brackt.server"; // ─── Types ──────────────────────────────────────────────────────────────────── @@ -44,6 +48,7 @@ type SportSeason = { name: string; year: number; scoringType: string; + fantasySeasonId: string | null; sport: { id: string; name: string; type: string; slug: string; iconUrl: string | null }; participantCount: number; }; @@ -56,12 +61,24 @@ type Template = { sportsSeasonIds: string[]; }; -// ─── Constants ──────────────────────────────────────────────────────────────── +const BRACKT_SPORT_TOOLTIP = + "Draft managers from your own league. They score based on how their teams perform across the other sports."; -const CATEGORY_ORDER = [ - "Basketball", "Football", "Baseball", "Hockey", - "Soccer", "Motorsport", "Golf", "Tennis", "Other", -]; +function isBracktSport(sport: { slug: string }) { + return sport.slug === "brackt"; +} + +function BracktInfoIcon() { + return ( + + + + ); +} function defaultRounds(count: number): number { if (count <= 0) return 3; @@ -69,22 +86,6 @@ function defaultRounds(count: number): number { return Math.max(count + 3, Math.ceil(count * pct)); } - - - -function getSportCategory(sportName: string): string { - const n = sportName.toLowerCase(); - if (n.includes("basketball") || n === "nba" || n === "wnba" || n === "euroleague" || n.includes("ncaa")) return "Basketball"; - if (n === "nfl" || n === "cfl" || n === "xfl" || (n.includes("football") && !n.includes("soccer"))) return "Football"; - if (n === "mlb" || n.includes("baseball") || n === "kbo" || n === "npb" || n.includes("little league")) return "Baseball"; - if (n === "nhl" || n === "ahl" || n === "pwhl" || n.includes("hockey") || n.includes("iihf")) return "Hockey"; - if (n.includes("soccer") || n.includes("mls") || n.includes("premier") || n.includes("champions")) return "Soccer"; - if (n === "f1" || n.includes("formula") || n.includes("indy") || n.includes("nascar") || n.includes("racing")) return "Motorsport"; - if (n.includes("golf") || n.includes("pga")) return "Golf"; - if (n.includes("tennis") || n.includes("atp") || n.includes("wta")) return "Tennis"; - return "Other"; -} - // ─── Meta ───────────────────────────────────────────────────────────────────── export function meta(): Route.MetaDescriptors { @@ -97,6 +98,8 @@ export async function loader(args: Route.LoaderArgs) { const session = await auth.api.getSession({ headers: args.request.headers }); const userId = session?.user.id ?? null; + await seedOrRepairBracktTemplate(); + const templates = await findActiveSeasonTemplates(); const allSportsSeasons = await findDraftableSportsSeasons(); @@ -213,7 +216,9 @@ export async function action(args: Route.ActionArgs) { leagueId: league.id, year: currentYear, status: "pre_draft", - templateId: typeof templateId === "string" && templateId !== "" ? templateId : null, + templateId: typeof templateId === "string" && templateId !== "" && templateId !== "customize" + ? templateId + : null, draftRounds: draftRoundsNum, draftDateTime: typeof draftDateTime === "string" && draftDateTime ? new Date(draftDateTime) : null, draftInitialTime: draftInitialTimeNum, @@ -228,7 +233,7 @@ export async function action(args: Route.ActionArgs) { await setCurrentSeason(league.id, season.id); - const selectedSports = formData.getAll("sportsSeasons"); + const selectedSports = formData.getAll("sportsSeasons").map(String); if (selectedSports.length > draftRoundsNum) { return { @@ -236,15 +241,6 @@ export async function action(args: Route.ActionArgs) { }; } - if (selectedSports.length > 0) { - await linkMultipleSportsToSeason( - selectedSports.map((id) => ({ - seasonId: season.id, - sportsSeasonId: id as string, - })) - ); - } - const joinAsPlayer = formData.get("joinAsPlayer") === "on"; const teamNames = generateUniqueTeamNames(teamCountNum); const creator = await findUserById(userId); @@ -259,6 +255,16 @@ export async function action(args: Route.ActionArgs) { await createManyTeams(teams); + const sportsToLink = await applyBracktSportsForSeason(season.id, selectedSports); + if (sportsToLink.length > 0) { + await linkMultipleSportsToSeason( + sportsToLink.map((id) => ({ + seasonId: season.id, + sportsSeasonId: id, + })) + ); + } + const userTimezone = formData.get("userTimezone"); if (typeof userTimezone === "string" && userTimezone) { await updateUser(userId, { timezone: userTimezone }); @@ -401,20 +407,18 @@ function Step2Sports({ // When a named template is selected, only show that template's seasons // Always exclude seasons with fewer participants than the league's team count const activeTemplate = templates.find((t) => t.id === selectedTemplate); - const eligibleSeasons = allSportsSeasons.filter((s) => s.participantCount >= teamCount); + const eligibleSeasons = allSportsSeasons.filter( + (s) => s.participantCount >= teamCount || (s.sport.slug === "brackt" && s.fantasySeasonId === null) + ); const displaySeasons = selectedTemplate === "customize" || !activeTemplate ? eligibleSeasons : eligibleSeasons.filter((s) => activeTemplate.sportsSeasonIds.includes(s.id)); - // Group sports by category - const grouped: { category: string; seasons: SportSeason[] }[] = CATEGORY_ORDER - .map((cat) => ({ - category: cat, - seasons: displaySeasons.filter((s) => getSportCategory(s.sport.name) === cat), - })) - .filter(({ seasons }) => - seasons.length > 0 - ); + const sortedDisplaySeasons = displaySeasons.toSorted((a, b) => { + const sportNameDiff = a.sport.name.localeCompare(b.sport.name); + if (sportNameDiff !== 0) return sportNameDiff; + return b.year - a.year; + }); return (
@@ -486,9 +490,14 @@ function Step2Sports({ return acc; }, {}) ).toSorted((a, b) => a.name.localeCompare(b.name)).map((sport) => ( -
  • +
  • {sport.name} + {isBracktSport(sport) && }
  • ))} @@ -540,44 +549,36 @@ function Step2Sports({ )} {/* Full sport picker */} -
    -
    - {grouped.map(({ category, seasons }) => ( -
    -

    - {category} -

    -
    - {seasons.map((s) => { - const selected = selectedSports.has(s.id); - return ( - - ); - })} -
    -
    - ))} - {grouped.length === 0 && ( +
    +
    + {sortedDisplaySeasons.map((s) => { + const selected = selectedSports.has(s.id); + return ( + + ); + })} + {sortedDisplaySeasons.length === 0 && (

    No sports seasons available.

    )}
    -
    )} diff --git a/app/routes/rules.tsx b/app/routes/rules.tsx index e993522..c61684d 100644 --- a/app/routes/rules.tsx +++ b/app/routes/rules.tsx @@ -173,6 +173,32 @@ export default function Rules() {
    + {/* Brackt */} +
    +

    Brackt — Draft a Manager

    +
    +
    +

    + Commissioners may add Brackt as + an optional meta-sport. When included, each team drafts exactly one manager from + their own league — including themselves. +

    +

    + At season end, the drafted manager's final overall fantasy ranking across all other + sports determines how many points their drafter earns, using the same 1st–8th + placement point values as every other sport. +

    +

    + Brackt adds one required draft round to every league that includes it. Self-drafting + is explicitly allowed and is a valid strategy. +

    +

    + Brackt is resolved automatically once every other sport in the league has been + finalized. +

    +
    +
    + {/* The Draft */}

    The Draft

    diff --git a/app/routes/teams/$teamId.settings.tsx b/app/routes/teams/$teamId.settings.tsx index 58552c6..f923d4e 100644 --- a/app/routes/teams/$teamId.settings.tsx +++ b/app/routes/teams/$teamId.settings.tsx @@ -29,6 +29,7 @@ import { AlertDialogTitle, AlertDialogTrigger, } from "~/components/ui/alert-dialog"; +import { syncPrivateBracktParticipants } from "~/services/brackt.server"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Team Settings — ${data?.team?.name ?? "Team"} - Brackt` }]; @@ -100,6 +101,7 @@ export async function action(args: Route.ActionArgs) { name: name.trim(), logoUrl: logoUrl && typeof logoUrl === "string" ? logoUrl.trim() : undefined, }); + await syncPrivateBracktParticipants(team.seasonId); const season = await findSeasonById(team.seasonId); return redirect(`/leagues/${season?.leagueId}?updated=true`); diff --git a/app/services/brackt.server.ts b/app/services/brackt.server.ts new file mode 100644 index 0000000..cc7b163 --- /dev/null +++ b/app/services/brackt.server.ts @@ -0,0 +1,350 @@ +import { and, eq, inArray, isNull } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import { batchUpsertParticipantEVs, syncVorpForSeason } from "~/models/participant-expected-value"; +import { recalculateStandings } from "~/models/scoring-calculator"; +import { createDailySnapshot } from "~/models/standings"; +import { BracktSimulator } from "~/services/simulations/brackt-simulator"; + +export const BRACKT_SLUG = "brackt"; + +type Db = ReturnType; + +function isBracktSport(sport: { slug: string } | null | undefined) { + return sport?.slug === BRACKT_SLUG; +} + +export async function seedOrRepairBracktTemplate(providedDb?: Db) { + const db = providedDb || database(); + return await db.transaction(async (tx) => { + const existingSport = await tx.query.sports.findFirst({ + where: eq(schema.sports.slug, BRACKT_SLUG), + }); + + const [sport] = existingSport + ? await tx + .update(schema.sports) + .set({ name: "Brackt", type: "team", simulatorType: "brackt", updatedAt: new Date() }) + .where(eq(schema.sports.id, existingSport.id)) + .returning() + : await tx + .insert(schema.sports) + .values({ + name: "Brackt", + type: "team", + slug: BRACKT_SLUG, + description: "League meta-sport based on final Brackt standings", + simulatorType: "brackt", + }) + .returning(); + + const existingTemplate = await tx.query.sportsSeasons.findFirst({ + where: and(eq(schema.sportsSeasons.sportId, sport.id), isNull(schema.sportsSeasons.fantasySeasonId)), + orderBy: (sportsSeasons, { desc }) => [desc(sportsSeasons.year)], + }); + + const templateValues = { + sportId: sport.id, + name: "Brackt", + year: 2026, + startDate: "2026-01-01", + endDate: "2099-12-31", + status: "upcoming" as const, + scoringType: "regular_season" as const, + scoringPattern: "season_standings" as const, + draftOn: "2026-01-01", + draftOff: "2099-12-31", + fantasySeasonId: null, + updatedAt: new Date(), + }; + + const [template] = existingTemplate + ? await tx + .update(schema.sportsSeasons) + .set(templateValues) + .where(eq(schema.sportsSeasons.id, existingTemplate.id)) + .returning() + : await tx.insert(schema.sportsSeasons).values(templateValues).returning(); + + return { sport, template }; + }); +} + +export async function findPrivateBracktSeason(fantasySeasonId: string, providedDb?: Db) { + const db = providedDb || database(); + return db.query.sportsSeasons.findFirst({ + where: eq(schema.sportsSeasons.fantasySeasonId, fantasySeasonId), + with: { sport: true }, + }).then((season) => (isBracktSport(season?.sport) ? season : undefined)); +} + +async function ensurePrivateBracktSeason(fantasySeasonId: string, templateId: string, db: Db) { + const existing = await findPrivateBracktSeason(fantasySeasonId, db); + if (existing) return existing; + + const template = await db.query.sportsSeasons.findFirst({ + where: eq(schema.sportsSeasons.id, templateId), + with: { sport: true }, + }); + if (!template || !isBracktSport(template.sport)) { + throw new Error("Brackt template sports season not found"); + } + + const fantasySeason = await db.query.seasons.findFirst({ + where: eq(schema.seasons.id, fantasySeasonId), + with: { league: true }, + }); + + const [privateSeason] = await db + .insert(schema.sportsSeasons) + .values({ + sportId: template.sportId, + fantasySeasonId, + name: `${fantasySeason?.league.name ?? "League"} Brackt`, + year: fantasySeason?.year ?? template.year, + startDate: template.startDate, + endDate: template.endDate, + status: "upcoming", + scoringType: "regular_season", + scoringPattern: "season_standings", + draftOn: template.draftOn, + draftOff: template.draftOff, + }) + .returning(); + + return { ...privateSeason, sport: template.sport }; +} + +export async function syncPrivateBracktParticipants(fantasySeasonId: string, providedDb?: Db) { + const db = providedDb || database(); + const bracktSeason = await findPrivateBracktSeason(fantasySeasonId, db); + if (!bracktSeason) return null; + + const teams = await db.query.teams.findMany({ + where: eq(schema.teams.seasonId, fantasySeasonId), + orderBy: (teamsTable, { asc }) => [asc(teamsTable.name)], + }); + const participants = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, bracktSeason.id), + }); + + const participantByTeamId = new Map(participants.map((p) => [p.externalId, p])); + const teamIds = new Set(teams.map((team) => team.id)); + + for (const team of teams) { + const participant = participantByTeamId.get(team.id); + if (participant) { + if (participant.name !== team.name || participant.shortName !== team.name) { + await db + .update(schema.seasonParticipants) + .set({ name: team.name, shortName: team.name, updatedAt: new Date() }) + .where(eq(schema.seasonParticipants.id, participant.id)); + } + } else { + await db.insert(schema.seasonParticipants).values({ + sportsSeasonId: bracktSeason.id, + name: team.name, + shortName: team.name, + externalId: team.id, + }); + } + } + + const obsolete = participants.filter((p) => p.externalId && !teamIds.has(p.externalId)); + if (obsolete.length > 0) { + await db + .delete(schema.seasonParticipants) + .where(inArray(schema.seasonParticipants.id, obsolete.map((p) => p.id))); + } + + await runBracktHarvilleForFantasySeason(fantasySeasonId, db); + return bracktSeason; +} + +export async function applyBracktSportsForSeason( + fantasySeasonId: string, + selectedSportsSeasonIds: string[], + providedDb?: Db +): Promise { + const db = providedDb || database(); + const selected = [...new Set(selectedSportsSeasonIds)]; + if (selected.length === 0) return selected; + + const sportsSeasons = await db.query.sportsSeasons.findMany({ + where: inArray(schema.sportsSeasons.id, selected), + with: { sport: true }, + }); + const bracktTemplate = sportsSeasons.find((ss) => isBracktSport(ss.sport) && ss.fantasySeasonId === null); + const existingPrivate = await findPrivateBracktSeason(fantasySeasonId, db); + const nonBracktIds = selected.filter((id) => { + const ss = sportsSeasons.find((s) => s.id === id); + return !ss || !isBracktSport(ss.sport); + }); + + if (!bracktTemplate && existingPrivate && !selected.includes(existingPrivate.id)) { + await db.delete(schema.sportsSeasons).where(eq(schema.sportsSeasons.id, existingPrivate.id)); + return nonBracktIds; + } + + if (!bracktTemplate) return selected; + + const privateSeason = await ensurePrivateBracktSeason(fantasySeasonId, bracktTemplate.id, db); + await syncPrivateBracktParticipants(fantasySeasonId, db); + return [...nonBracktIds, privateSeason.id]; +} + +export async function runBracktHarvilleForFantasySeason(fantasySeasonId: string, db: Db) { + const bracktSeason = await findPrivateBracktSeason(fantasySeasonId, db); + if (!bracktSeason) return []; + + const fantasySeason = await db.query.seasons.findFirst({ + where: eq(schema.seasons.id, fantasySeasonId), + }); + if (!fantasySeason) return []; + + const simulator = new BracktSimulator(db); + const results = await simulator.simulate(bracktSeason.id); + await batchUpsertParticipantEVs( + results.map((result) => ({ + participantId: result.participantId, + sportsSeasonId: bracktSeason.id, + probabilities: result.probabilities, + scoringRules: { + pointsFor1st: fantasySeason.pointsFor1st, + pointsFor2nd: fantasySeason.pointsFor2nd, + pointsFor3rd: fantasySeason.pointsFor3rd, + pointsFor4th: fantasySeason.pointsFor4th, + pointsFor5th: fantasySeason.pointsFor5th, + pointsFor6th: fantasySeason.pointsFor6th, + pointsFor7th: fantasySeason.pointsFor7th, + pointsFor8th: fantasySeason.pointsFor8th, + }, + source: "performance_model", + })), + db + ); + await syncVorpForSeason(bracktSeason.id, db); + + const participants = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, bracktSeason.id), + }); + return participants.map((p) => ({ + participantId: p.id, + expectedValue: Number(p.expectedValue ?? 0), + vorpValue: Number(p.vorpValue ?? 0), + })); +} + +async function resolveBracktForFantasySeason( + fantasySeasonId: string, + db: Db, + trigger: "auto" | "manual", + completedSportsSeasonId?: string +) { + const bracktSeason = await findPrivateBracktSeason(fantasySeasonId, db); + if (!bracktSeason) return { status: "skipped" as const, reason: "no_brackt" }; + + const picks = await db.query.draftPicks.findMany({ + where: eq(schema.draftPicks.seasonId, fantasySeasonId), + with: { participant: { with: { sportsSeason: { with: { sport: true } }, results: true } } }, + }); + const nonBracktPicks = picks.filter((pick) => !isBracktSport(pick.participant.sportsSeason.sport)); + const ready = nonBracktPicks.every((pick) => { + const hasFinalizedResult = pick.participant.results.some( + (r) => r.finalPosition !== null && !r.isPartialScore + ); + return hasFinalizedResult || pick.participant.sportsSeason.status === "completed"; + }); + if (!ready) return { status: "skipped" as const, reason: "incomplete" }; + + const [standings, bracktParticipants, fantasySeason] = await Promise.all([ + db.query.teamStandings.findMany({ where: eq(schema.teamStandings.seasonId, fantasySeasonId) }), + db.query.seasonParticipants.findMany({ where: eq(schema.seasonParticipants.sportsSeasonId, bracktSeason.id) }), + db.query.seasons.findFirst({ where: eq(schema.seasons.id, fantasySeasonId), with: { league: true } }), + ]); + if (standings.length === 0 || !fantasySeason) { + return { status: "skipped" as const, reason: "missing_standings" }; + } + + const participantByTeamId = new Map(bracktParticipants.map((p) => [p.externalId, p])); + const desired = standings.map((standing) => { + const participant = participantByTeamId.get(standing.teamId); + if (!participant) throw new Error(`Missing Brackt participant for team ${standing.teamId}`); + return { participantId: participant.id, finalPosition: standing.currentRank }; + }); + + const existingResults = await db.query.seasonParticipantResults.findMany({ + where: eq(schema.seasonParticipantResults.sportsSeasonId, bracktSeason.id), + }); + if (existingResults.length > 0) { + const existingByParticipantId = new Map(existingResults.map((r) => [r.participantId, r])); + const differs = desired.some((placement) => { + const existing = existingByParticipantId.get(placement.participantId); + return existing && existing.finalPosition !== placement.finalPosition; + }); + if (differs) { + return { status: "blocked" as const, reason: "existing_results_differ" }; + } + } + + const now = new Date(); + const existingByParticipantId = new Map(existingResults.map((r) => [r.participantId, r])); + for (const placement of desired) { + const existing = existingByParticipantId.get(placement.participantId); + if (existing) { + await db + .update(schema.seasonParticipantResults) + .set({ + finalPosition: placement.finalPosition, + isPartialScore: false, + updatedAt: now, + }) + .where(eq(schema.seasonParticipantResults.id, existing.id)); + } else { + await db.insert(schema.seasonParticipantResults).values({ + participantId: placement.participantId, + sportsSeasonId: bracktSeason.id, + finalPosition: placement.finalPosition, + isPartialScore: false, + }); + } + } + + await db + .update(schema.sportsSeasons) + .set({ status: "completed", updatedAt: now }) + .where(eq(schema.sportsSeasons.id, bracktSeason.id)); + await recalculateStandings(fantasySeasonId, db); + await createDailySnapshot(fantasySeasonId, db); + await db.insert(schema.commissionerAuditLog).values({ + seasonId: fantasySeasonId, + leagueId: fantasySeason.leagueId, + actorUserId: trigger === "auto" ? "system" : "commissioner", + actorDisplayName: trigger === "auto" ? "System" : "Commissioner", + action: "brackt_resolved", + affectedTeamIds: standings.map((s) => s.teamId), + details: { trigger, completedSportsSeasonId }, + }); + + return { status: "resolved" as const }; +} + +export async function maybeResolveCompletedBracktForSportsSeason(completedSportsSeasonId: string, providedDb?: Db) { + const db = providedDb || database(); + const completedSeason = await db.query.sportsSeasons.findFirst({ + where: eq(schema.sportsSeasons.id, completedSportsSeasonId), + with: { sport: true }, + }); + if (!completedSeason || isBracktSport(completedSeason.sport)) return []; + + const links = await db.query.seasonSports.findMany({ + where: eq(schema.seasonSports.sportsSeasonId, completedSportsSeasonId), + }); + + const results = []; + for (const link of links) { + results.push(await resolveBracktForFantasySeason(link.seasonId, db, "auto", completedSportsSeasonId)); + } + return results; +} + diff --git a/app/services/simulations/__tests__/brackt-simulator.test.ts b/app/services/simulations/__tests__/brackt-simulator.test.ts new file mode 100644 index 0000000..f50c58b --- /dev/null +++ b/app/services/simulations/__tests__/brackt-simulator.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; +import { harvillePlacementProbabilities } from "../brackt-simulator"; + +describe("BracktSimulator", () => { + it("returns uniform placement probabilities when all weights are zero", () => { + const probabilities = harvillePlacementProbabilities([0, 0, 0, 0, 0, 0]); + + expect(probabilities).toHaveLength(6); + for (const row of probabilities) { + expect(row).toHaveLength(6); + for (const probability of row) { + expect(probability).toBeCloseTo(1 / 6, 5); + } + } + }); + + it("gives higher first-place probability to higher projected weights", () => { + const probabilities = harvillePlacementProbabilities([10, 20, 40]); + + expect(probabilities[2][0]).toBeGreaterThan(probabilities[1][0]); + expect(probabilities[1][0]).toBeGreaterThan(probabilities[0][0]); + expect(probabilities[0][0] + probabilities[1][0] + probabilities[2][0]).toBeCloseTo(1, 5); + }); +}); diff --git a/app/services/simulations/brackt-simulator.ts b/app/services/simulations/brackt-simulator.ts new file mode 100644 index 0000000..fe93990 --- /dev/null +++ b/app/services/simulations/brackt-simulator.ts @@ -0,0 +1,176 @@ +import { and, eq, notInArray } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import type { Simulator, SimulationProbabilities, SimulationResult } from "./types"; + +const PROB_KEYS = [ + "probFirst", + "probSecond", + "probThird", + "probFourth", + "probFifth", + "probSixth", + "probSeventh", + "probEighth", +] as const; + +function emptyProbabilities(): SimulationProbabilities { + return { + probFirst: 0, + probSecond: 0, + probThird: 0, + probFourth: 0, + probFifth: 0, + probSixth: 0, + probSeventh: 0, + probEighth: 0, + }; +} + +export function harvillePlacementProbabilities(weights: number[]): number[][] { + const n = weights.length; + const maxPlaces = Math.min(8, n); + if (n === 0) return []; + + if (weights.every((w) => w <= 0)) { + return weights.map(() => + Array.from({ length: maxPlaces }, () => Number((1 / n).toFixed(6))) + ); + } + + const probs = weights.map(() => Array.from({ length: maxPlaces }, () => 0)); + let states = new Map([[0, 1]]); + + for (let place = 0; place < maxPlaces; place++) { + const next = new Map(); + for (const [mask, stateProb] of states) { + const remaining = weights + .map((weight, index) => ({ weight: Math.max(0, weight), index })) + .filter(({ index }) => (mask & (1 << index)) === 0); + const total = remaining.reduce((sum, item) => sum + item.weight, 0); + + for (const item of remaining) { + const pickProb = total > 0 ? item.weight / total : 1 / remaining.length; + const probability = stateProb * pickProb; + probs[item.index][place] += probability; + const nextMask = mask | (1 << item.index); + next.set(nextMask, (next.get(nextMask) ?? 0) + probability); + } + } + states = next; + } + + return probs; +} + +export class BracktSimulator implements Simulator { + private readonly db: ReturnType; + + constructor(providedDb?: ReturnType) { + this.db = providedDb ?? database(); + } + + async simulate(sportsSeasonId: string): Promise { + const db = this.db; + const bracktSeason = await db.query.sportsSeasons.findFirst({ + where: eq(schema.sportsSeasons.id, sportsSeasonId), + with: { sport: true }, + }); + + if (!bracktSeason?.fantasySeasonId) { + throw new Error("Brackt simulation requires a private sports season linked to a fantasy season"); + } + + const fantasySeasonId = bracktSeason.fantasySeasonId; + const [teams, bracktParticipants, seasonSports, draftPicks] = await Promise.all([ + db.query.teams.findMany({ + where: eq(schema.teams.seasonId, fantasySeasonId), + orderBy: (teamsTable, { asc }) => [asc(teamsTable.name)], + }), + db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), + }), + db.query.seasonSports.findMany({ + where: eq(schema.seasonSports.seasonId, fantasySeasonId), + with: { sportsSeason: { with: { sport: true } } }, + }), + db.query.draftPicks.findMany({ + where: eq(schema.draftPicks.seasonId, fantasySeasonId), + with: { participant: { with: { sportsSeason: { with: { sport: true } } } } }, + }), + ]); + + if (teams.length < 6 || teams.length > 16) { + throw new Error("Brackt supports leagues with 6 to 16 teams"); + } + + const nonBracktSeasonIds = seasonSports + .filter((link) => link.sportsSeason.sport.slug !== "brackt") + .map((link) => link.sportsSeasonId); + + const draftedIds = draftPicks.map((pick) => pick.participantId); + const availableBySportsSeason = new Map(); + + if (nonBracktSeasonIds.length > 0) { + for (const nonBracktSeasonId of nonBracktSeasonIds) { + const whereClause = draftedIds.length > 0 + ? and( + eq(schema.seasonParticipants.sportsSeasonId, nonBracktSeasonId), + notInArray(schema.seasonParticipants.id, draftedIds) + ) + : eq(schema.seasonParticipants.sportsSeasonId, nonBracktSeasonId); + const available = await db.query.seasonParticipants.findMany({ where: whereClause }); + const average = available.length > 0 + ? available.reduce((sum, p) => sum + Number(p.expectedValue ?? 0), 0) / available.length + : 0; + availableBySportsSeason.set(nonBracktSeasonId, average); + } + } + + const picksByTeam = new Map(); + for (const pick of draftPicks) { + const existing = picksByTeam.get(pick.teamId) ?? []; + existing.push(pick); + picksByTeam.set(pick.teamId, existing); + } + + const weights = teams.map((team) => { + const teamPicks = picksByTeam.get(team.id) ?? []; + const draftedBySportsSeason = new Set(); + let projection = 0; + + for (const pick of teamPicks) { + if (pick.participant.sportsSeason.sport.slug === "brackt") continue; + draftedBySportsSeason.add(pick.participant.sportsSeasonId); + projection += Number(pick.participant.expectedValue ?? 0); + } + + for (const nonBracktSeasonId of nonBracktSeasonIds) { + if (!draftedBySportsSeason.has(nonBracktSeasonId)) { + projection += availableBySportsSeason.get(nonBracktSeasonId) ?? 0; + } + } + + return Math.max(0, projection); + }); + + const placementProbabilities = harvillePlacementProbabilities(weights); + const participantByTeamId = new Map(bracktParticipants.map((p) => [p.externalId, p])); + + return teams.map((team, teamIndex) => { + const participant = participantByTeamId.get(team.id); + if (!participant) { + throw new Error(`Missing Brackt participant for team ${team.name}`); + } + const probabilities = emptyProbabilities(); + for (let i = 0; i < Math.min(PROB_KEYS.length, placementProbabilities[teamIndex].length); i++) { + probabilities[PROB_KEYS[i]] = Number(placementProbabilities[teamIndex][i].toFixed(4)); + } + return { + participantId: participant.id, + probabilities, + source: "brackt_harville", + }; + }); + } +} diff --git a/app/services/simulations/registry.ts b/app/services/simulations/registry.ts index fd8fd77..14e4f6b 100644 --- a/app/services/simulations/registry.ts +++ b/app/services/simulations/registry.ts @@ -26,6 +26,7 @@ import { WNBASimulator } from "./wnba-simulator"; import { WorldCupSimulator } from "./world-cup-simulator"; import { NCAAFootballSimulator } from "./ncaa-football-simulator"; import { LLWSSimulator } from "./llws-simulator"; +import { BracktSimulator } from "./brackt-simulator"; export const SIMULATOR_TYPES = [ "f1_standings", @@ -48,6 +49,7 @@ export const SIMULATOR_TYPES = [ "cs2_major_qualifying_points", "ncaa_football_bracket", "llws_bracket", + "brackt", ] as const; export type SimulatorType = typeof SIMULATOR_TYPES[number]; @@ -158,6 +160,13 @@ const REGISTRY: Record Simul }, create: () => new LLWSSimulator(), }, + brackt: { + info: { + name: "Brackt Harville Model", + description: "Projects league manager standings from drafted non-Brackt EVs and simulates final league placement probabilities.", + }, + create: () => new BracktSimulator(), + }, }; export function getSimulator(simulatorType: SimulatorType): Simulator { @@ -174,4 +183,3 @@ export function getSimulator(simulatorType: SimulatorType): Simulator { export function getSimulatorInfo(simulatorType: SimulatorType): SimulatorInfo | null { return REGISTRY[simulatorType]?.info ?? null; } - diff --git a/app/services/simulations/simulator-config.ts b/app/services/simulations/simulator-config.ts index ad921b3..5e9733c 100644 --- a/app/services/simulations/simulator-config.ts +++ b/app/services/simulations/simulator-config.ts @@ -71,6 +71,7 @@ const CONFIG: Record = { cs2_major_qualifying_points: null, ncaa_football_bracket: null, llws_bracket: null, + brackt: null, }; /** diff --git a/database/schema.ts b/database/schema.ts index f5046db..fe4e862 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -148,6 +148,7 @@ export const simulatorTypeEnum = pgEnum("simulator_type", [ "cs2_major_qualifying_points", "ncaa_football_bracket", "llws_bracket", + "brackt", ]); export const tournamentStatusEnum = pgEnum("tournament_status", [ @@ -354,6 +355,9 @@ export const sportsSeasons = pgTable("sports_seasons", { sportId: uuid("sport_id") .notNull() .references(() => sports.id, { onDelete: "cascade" }), + fantasySeasonId: uuid("fantasy_season_id").references(() => seasons.id, { + onDelete: "cascade", + }), name: varchar("name", { length: 255 }).notNull(), year: integer("year").notNull(), startDate: date("start_date"), @@ -904,6 +908,10 @@ export const sportsSeasonsRelations = relations(sportsSeasons, ({ one, many }) = fields: [sportsSeasons.sportId], references: [sports.id], }), + fantasySeason: one(seasons, { + fields: [sportsSeasons.fantasySeasonId], + references: [seasons.id], + }), participants: many(seasonParticipants), seasonTemplateSports: many(seasonTemplateSports), seasonSports: many(seasonSports), @@ -999,6 +1007,7 @@ export const seasonsRelations = relations(seasons, ({ one, many }) => ({ }), teams: many(teams), seasonSports: many(seasonSports), + privateSportsSeasons: many(sportsSeasons), })); export const seasonSportsRelations = relations(seasonSports, ({ one }) => ({ @@ -1514,6 +1523,7 @@ export const auditActionEnum = pgEnum("audit_action", [ "force_manual_pick", "draft_pick_changed", "time_bank_edited", + "brackt_resolved", ]); export const commissionerAuditLog = pgTable("commissioner_audit_log", { diff --git a/docs/agents/brackt-sport-implementation-plan.md b/docs/agents/brackt-sport-implementation-plan.md new file mode 100644 index 0000000..6b822cd --- /dev/null +++ b/docs/agents/brackt-sport-implementation-plan.md @@ -0,0 +1,105 @@ +# Brackt as a Draftable Sport Implementation Plan + +This plan is the implementation source of truth for making Brackt a league-private meta-sport. The design reference is [Brackt sport](brackt-sport.md). + +## Summary + +Implement Brackt as a league-private meta-sport with automatic final resolution. When any non-Brackt sports season is marked completed, the app checks linked fantasy seasons. If every drafted non-Brackt participant is finalized, it resolves that league's private Brackt season automatically. Commissioners still get a manual fallback action for repair and idempotent retry. + +## Key Changes + +- Add schema support: + - Add `sportsSeasons.fantasySeasonId`. + - Add `brackt` to `simulatorTypeEnum`. + - Add `brackt_resolved` to `auditActionEnum`. + - Generate migration with `npm run db:generate`. +- Add an admin seed/repair UI action: + - Idempotently creates or repairs the global `Brackt` sport and global template sports season. + - Template has no participants and a long draft window. +- Update draftable sports selection: + - Hide per-league Brackt copies from all sport pickers with `fantasySeasonId IS NULL`. + - Allow the global Brackt template through league creation/settings even with zero participants. +- Add Brackt lifecycle helpers: + - On league creation or pre-draft settings update, replace the Brackt template link with a private sports season copy. + - Insert manager participants directly into `seasonParticipants` with `externalId = team.id`. + - Auto-sync private Brackt participants when pre-draft teams are added, removed, or renamed. + - Remove the private Brackt season if Brackt is removed before draft start. + +## EV And Draft Behavior + +- Add `BracktSimulator`: + - Supports all current league sizes, 6-16 teams. + - Uses Harville probabilities for `N` teams and maps only available scoring tiers. + - If all projected weights are zero, returns uniform placement probabilities. + - Projects each team from drafted non-Brackt EV plus average available EV for each unpicked non-Brackt sport. +- Add `runBracktHarvilleForFantasySeason(fantasySeasonId, db)`: + - Requires an explicit DB instance. + - Runs the simulator, batch-upserts EVs, syncs VORP, rereads stored VORP, and returns `{ participantId, expectedValue, vorpValue }`. +- Refactor only the needed EV path to accept an optional provided DB: + - `batchUpsertParticipantEVs(inputs, db?)` + - `syncVorpForSeason(sportsSeasonId, db?)` +- Run Brackt Harville: + - Await before autopick participant selection. + - Fire-and-forget after manual picks commit. + - Await after private Brackt season creation/sync. +- Emit `brackt-evs-updated` to the draft room with updated `expectedValue` and `vorpValue`. +- Draft room client: + - Patches Brackt VORPs from socket updates. + - Sorts by effective server VORP. + - Never computes Brackt VORP client-side. + +## Automatic Resolution + +- Add a centralized service: + - `maybeResolveCompletedBracktForSportsSeason(completedSportsSeasonId, db)` + - Called after any non-Brackt sports season status transitions to `completed`. +- Hook all completion paths through this helper: + - Admin sports-season edit when status becomes `completed`. + - `finalize-standings`. + - Bracket/qualifying finalization paths that mark or imply sports-season completion. + - Any future model helper for status updates should call this from one shared status-transition function. +- Automatic resolution flow: + - Find fantasy seasons linked to the completed non-Brackt sports season. + - For each fantasy season, find its private Brackt season. + - Skip leagues without Brackt or already resolved Brackt. + - Require every drafted non-Brackt participant to have a finalized, non-partial result. + - If ready, resolve Brackt from `teamStandings.currentRank`, mapping team ID to Brackt participant via `externalId`. + - Preserve tied ranks as tied `finalPosition` values. + - Mark the private Brackt sports season `completed`. + - Recalculate standings and create a daily snapshot. + - Write audit log `brackt_resolved` with `{ trigger: "auto", completedSportsSeasonId }`. +- Manual fallback: + - Keep a commissioner-only league settings action to retry resolution. + - Re-run is idempotent only: allow if placements match existing Brackt results; block if they would differ. + +## Test Plan + +- Unit tests: + - Brackt Harville zero-weight uniform probabilities. + - Brackt projection from drafted EV plus average available EV. + - Private Brackt creation and team participant sync. + - Picker hides private copies and allows the zero-participant global template. + - EV/VORP path works with provided DB. + - Automatic resolver skips incomplete leagues and resolves ready leagues. + - Automatic resolver ignores leagues without Brackt and ignores Brackt-triggered recursion. + - Tied-rank resolution and idempotent re-run behavior. +- Route/action tests: + - League creation with Brackt creates private season and participants. + - League settings add/remove/sync Brackt pre-draft. + - Admin seed/repair action is idempotent. + - Sports-season completion triggers automatic Brackt check. + - Manual commissioner retry works only when idempotent or unresolved. +- Draft tests: + - Autopick updates Brackt EV before top-VORP selection. + - Manual pick emits Brackt EV update after commit. + - Client socket update patches Brackt VORP sorting. +- Verification: + - Run `npm run typecheck`, `npm run lint`, and targeted `npm run test:run`. + +## Assumptions + +- Brackt supports 6-16 team leagues. +- Brackt template remains a normal sport picker option with a Brackt-specific zero-participant exception. +- Pre-draft team edits auto-sync Brackt participants. +- Final tied standings remain tied for Brackt scoring. +- Automatic resolution is primary; commissioner action is fallback/repair. diff --git a/drizzle/0094_curved_nick_fury.sql b/drizzle/0094_curved_nick_fury.sql new file mode 100644 index 0000000..adf0861 --- /dev/null +++ b/drizzle/0094_curved_nick_fury.sql @@ -0,0 +1,8 @@ +ALTER TYPE "public"."audit_action" ADD VALUE 'brackt_resolved';--> statement-breakpoint +ALTER TYPE "public"."simulator_type" ADD VALUE 'brackt';--> statement-breakpoint +ALTER TABLE "sports_seasons" ADD COLUMN "fantasy_season_id" uuid;--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "sports_seasons" ADD CONSTRAINT "sports_seasons_fantasy_season_id_seasons_id_fk" FOREIGN KEY ("fantasy_season_id") REFERENCES "public"."seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/meta/0094_snapshot.json b/drizzle/meta/0094_snapshot.json new file mode 100644 index 0000000..85563b2 --- /dev/null +++ b/drizzle/meta/0094_snapshot.json @@ -0,0 +1,5723 @@ +{ + "id": "dd058b7e-a8e6-4f2e-b1b9-102046a6a494", + "prevId": "07e7bc0b-3c8a-48e0-94c8-62174b36d59e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "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": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.autodraft_settings": { + "name": "autodraft_settings", + "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 + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "mode": { + "name": "mode", + "type": "autodraft_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'next_pick'" + }, + "queue_only": { + "name": "queue_only", + "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": { + "autodraft_settings_season_id_seasons_id_fk": { + "name": "autodraft_settings_season_id_seasons_id_fk", + "tableFrom": "autodraft_settings", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "autodraft_settings_team_id_teams_id_fk": { + "name": "autodraft_settings_team_id_teams_id_fk", + "tableFrom": "autodraft_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commissioner_audit_log": { + "name": "commissioner_audit_log", + "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 + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "actor_display_name": { + "name": "actor_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "audit_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "affected_team_ids": { + "name": "affected_team_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_log_season_id_idx": { + "name": "audit_log_season_id_idx", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commissioner_audit_log_season_id_seasons_id_fk": { + "name": "commissioner_audit_log_season_id_seasons_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commissioner_audit_log_league_id_leagues_id_fk": { + "name": "commissioner_audit_log_league_id_leagues_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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.cs2_major_stage_results": { + "name": "cs2_major_stage_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_entry": { + "name": "stage_entry", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stage_eliminated": { + "name": "stage_eliminated", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "stage_eliminated_wins": { + "name": "stage_eliminated_wins", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "final_placement": { + "name": "final_placement", + "type": "integer", + "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": { + "cs2_major_stage_results_unique": { + "name": "cs2_major_stage_results_unique", + "columns": [ + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk": { + "name": "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cs2_major_stage_results_participant_id_season_participants_id_fk": { + "name": "cs2_major_stage_results_participant_id_season_participants_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_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": { + "draft_picks_season_pick_unique": { + "name": "draft_picks_season_pick_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pick_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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_season_participants_id_fk": { + "name": "draft_picks_participant_id_season_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "season_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_season_participants_id_fk": { + "name": "draft_queue_participant_id_season_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "season_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.event_results": { + "name": "event_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_participant_id": { + "name": "season_participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "qualifying_points_awarded": { + "name": "qualifying_points_awarded", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "eliminated": { + "name": "eliminated", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "raw_score": { + "name": "raw_score", + "type": "numeric(10, 2)", + "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": { + "event_results_event_participant_unique": { + "name": "event_results_event_participant_unique", + "columns": [ + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "event_results_scoring_event_id_scoring_events_id_fk": { + "name": "event_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "event_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "event_results_season_participant_id_season_participants_id_fk": { + "name": "event_results_season_participant_id_season_participants_id_fk", + "tableFrom": "event_results", + "tableTo": "season_participants", + "columnsFrom": [ + "season_participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.group_stage_matches": { + "name": "group_stage_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_score": { + "name": "participant1_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "matchday": { + "name": "matchday", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "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": { + "group_stage_matches_group_pair_unique": { + "name": "group_stage_matches_group_pair_unique", + "columns": [ + { + "expression": "tournament_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant1_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant2_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "group_stage_matches_tournament_group_id_tournament_groups_id_fk": { + "name": "group_stage_matches_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "group_stage_matches_participant1_id_season_participants_id_fk": { + "name": "group_stage_matches_participant1_id_season_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "group_stage_matches_participant2_id_season_participants_id_fk": { + "name": "group_stage_matches_participant2_id_season_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "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 + }, + "discord_webhook_url": { + "name": "discord_webhook_url", + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_ev_snapshots": { + "name": "participant_ev_snapshots", + "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 + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "calculated_ev": { + "name": "calculated_ev", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "varchar(100)", + "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": { + "participant_ev_snapshots_unique": { + "name": "participant_ev_snapshots_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_ev_snapshots_participant_id_season_participants_id_fk": { + "name": "participant_ev_snapshots_participant_id_season_participants_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_ev_snapshots_sports_season_id_sports_seasons_id_fk": { + "name": "participant_ev_snapshots_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_golf_skills": { + "name": "participant_golf_skills", + "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 + }, + "sg_total": { + "name": "sg_total", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "datagolf_rank": { + "name": "datagolf_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "masters_odds": { + "name": "masters_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "us_open_odds": { + "name": "us_open_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "open_championship_odds": { + "name": "open_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pga_championship_odds": { + "name": "pga_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_golf_skills_participant_unique": { + "name": "participant_golf_skills_participant_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_golf_skills_participant_id_participants_id_fk": { + "name": "participant_golf_skills_participant_id_participants_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_season_results": { + "name": "participant_season_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 + }, + "current_points": { + "name": "current_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_position": { + "name": "current_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_season_results_participant_id_season_participants_id_fk": { + "name": "participant_season_results_participant_id_season_participants_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_season_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_season_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_surface_elos": { + "name": "participant_surface_elos", + "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 + }, + "world_ranking": { + "name": "world_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_hard": { + "name": "elo_hard", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_clay": { + "name": "elo_clay", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_grass": { + "name": "elo_grass", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_surface_elos_participant_unique": { + "name": "participant_surface_elos_participant_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_surface_elos_participant_id_participants_id_fk": { + "name": "participant_surface_elos_participant_id_participants_id_fk", + "tableFrom": "participant_surface_elos", + "tableTo": "participants", + "columnsFrom": [ + "participant_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()" + }, + "sport_id": { + "name": "sport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "external_key": { + "name": "external_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "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": { + "participants_sport_name_unique": { + "name": "participants_sport_name_unique", + "columns": [ + { + "expression": "sport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participants_sport_id_sports_id_fk": { + "name": "participants_sport_id_sports_id_fk", + "tableFrom": "participants", + "tableTo": "sports", + "columnsFrom": [ + "sport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_standings_mappings": { + "name": "pending_standings_mappings", + "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 + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "standing_data": { + "name": "standing_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "psm_season_external_id_idx": { + "name": "psm_season_external_id_idx", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_standings_mappings_sports_season_id_sports_seasons_id_fk": { + "name": "pending_standings_mappings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "pending_standings_mappings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_games": { + "name": "playoff_match_games", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_number": { + "name": "game_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "playoff_match_game_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "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": { + "playoff_match_games_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_games_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_games_winner_id_season_participants_id_fk": { + "name": "playoff_match_games_winner_id_season_participants_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "season_participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_odds": { + "name": "playoff_match_odds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "moneyline_odds": { + "name": "moneyline_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "implied_probability": { + "name": "implied_probability", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": false + }, + "odds_source": { + "name": "odds_source", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "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": { + "playoff_match_odds_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_odds_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_odds_participant_id_season_participants_id_fk": { + "name": "playoff_match_odds_participant_id_season_participants_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_matches": { + "name": "playoff_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "match_number": { + "name": "match_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "loser_id": { + "name": "loser_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "is_scoring": { + "name": "is_scoring", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "template_round": { + "name": "template_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "seed_info": { + "name": "seed_info", + "type": "varchar(50)", + "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": { + "playoff_matches_scoring_event_id_scoring_events_id_fk": { + "name": "playoff_matches_scoring_event_id_scoring_events_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_matches_participant1_id_season_participants_id_fk": { + "name": "playoff_matches_participant1_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_participant2_id_season_participants_id_fk": { + "name": "playoff_matches_participant2_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_winner_id_season_participants_id_fk": { + "name": "playoff_matches_winner_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_loser_id_season_participants_id_fk": { + "name": "playoff_matches_loser_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "loser_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.qualifying_point_config": { + "name": "qualifying_point_config", + "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 + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "points": { + "name": "points", + "type": "numeric(10, 2)", + "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": { + "qualifying_point_config_sports_season_id_sports_seasons_id_fk": { + "name": "qualifying_point_config_sports_season_id_sports_seasons_id_fk", + "tableFrom": "qualifying_point_config", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.regular_season_standings": { + "name": "regular_season_standings", + "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 + }, + "wins": { + "name": "wins", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "losses": { + "name": "losses", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ot_losses": { + "name": "ot_losses", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ties": { + "name": "ties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "win_pct": { + "name": "win_pct", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false + }, + "games_played": { + "name": "games_played", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "games_back": { + "name": "games_back", + "type": "numeric(5, 1)", + "primaryKey": false, + "notNull": false + }, + "conference": { + "name": "conference", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "division": { + "name": "division", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "conference_rank": { + "name": "conference_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "division_rank": { + "name": "division_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "league_rank": { + "name": "league_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "streak": { + "name": "streak", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_ten": { + "name": "last_ten", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "home_record": { + "name": "home_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "away_record": { + "name": "away_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "srs": { + "name": "srs", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp", + "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": { + "rss_participant_season_idx": { + "name": "rss_participant_season_idx", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "regular_season_standings_participant_id_season_participants_id_fk": { + "name": "regular_season_standings_participant_id_season_participants_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "regular_season_standings_sports_season_id_sports_seasons_id_fk": { + "name": "regular_season_standings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scoring_events": { + "name": "scoring_events", + "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 + }, + "tournament_id": { + "name": "tournament_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "event_date": { + "name": "event_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "event_starts_at": { + "name": "event_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "playoff_round": { + "name": "playoff_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "is_qualifying_event": { + "name": "is_qualifying_event", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "bracket_template_id": { + "name": "bracket_template_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "scoring_starts_at_round": { + "name": "scoring_starts_at_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "bracket_region_config": { + "name": "bracket_region_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "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": { + "scoring_events_sports_season_id_sports_seasons_id_fk": { + "name": "scoring_events_sports_season_id_sports_seasons_id_fk", + "tableFrom": "scoring_events", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scoring_events_tournament_id_tournaments_id_fk": { + "name": "scoring_events_tournament_id_tournaments_id_fk", + "tableFrom": "scoring_events", + "tableTo": "tournaments", + "columnsFrom": [ + "tournament_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "scoring_events_qualifying_require_tournament": { + "name": "scoring_events_qualifying_require_tournament", + "value": "NOT \"scoring_events\".\"is_qualifying_event\" OR \"scoring_events\".\"tournament_id\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.season_participant_expected_values": { + "name": "season_participant_expected_values", + "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 + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "expected_value": { + "name": "expected_value", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "probability_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'manual'" + }, + "source_odds": { + "name": "source_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "source_elo": { + "name": "source_elo", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "world_ranking": { + "name": "world_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_participant_season_unique": { + "name": "participant_ev_participant_season_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "season_participant_expected_values_participant_id_season_participants_id_fk": { + "name": "season_participant_expected_values_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_expected_values", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_expected_values_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_expected_values_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_expected_values", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_golf_skills": { + "name": "season_participant_golf_skills", + "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 + }, + "sg_total": { + "name": "sg_total", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "datagolf_rank": { + "name": "datagolf_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "masters_odds": { + "name": "masters_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "us_open_odds": { + "name": "us_open_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "open_championship_odds": { + "name": "open_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pga_championship_odds": { + "name": "pga_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "season_participant_golf_skills_unique": { + "name": "season_participant_golf_skills_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "season_participant_golf_skills_participant_id_season_participants_id_fk": { + "name": "season_participant_golf_skills_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_golf_skills", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_golf_skills_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_golf_skills_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_golf_skills", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_qualifying_totals": { + "name": "season_participant_qualifying_totals", + "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 + }, + "total_qualifying_points": { + "name": "total_qualifying_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "events_scored": { + "name": "events_scored", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "final_ranking": { + "name": "final_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_participant_qualifying_totals_participant_id_season_participants_id_fk": { + "name": "season_participant_qualifying_totals_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_qualifying_totals", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_qualifying_totals", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_results": { + "name": "season_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 + }, + "is_partial_score": { + "name": "is_partial_score", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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": { + "season_participant_results_participant_id_season_participants_id_fk": { + "name": "season_participant_results_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participants": { + "name": "season_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 + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "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": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "vorp_value": { + "name": "vorp_value", + "type": "numeric(10, 4)", + "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": { + "participants_sports_season_name_unique": { + "name": "participants_sports_season_name_unique", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "season_participants_sports_season_id_sports_seasons_id_fk": { + "name": "season_participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participants", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participants_participant_id_participants_id_fk": { + "name": "season_participants_participant_id_participants_id_fk", + "tableFrom": "season_participants", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "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 + }, + "draft_timer_mode": { + "name": "draft_timer_mode", + "type": "draft_timer_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'chess_clock'" + }, + "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_completed_at": { + "name": "draft_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "overnight_pause_mode": { + "name": "overnight_pause_mode", + "type": "overnight_pause_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "overnight_pause_start": { + "name": "overnight_pause_start", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "overnight_pause_end": { + "name": "overnight_pause_end", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "overnight_pause_timezone": { + "name": "overnight_pause_timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "invite_code": { + "name": "invite_code", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "points_for_1st": { + "name": "points_for_1st", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "points_for_2nd": { + "name": "points_for_2nd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 70 + }, + "points_for_3rd": { + "name": "points_for_3rd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "points_for_4th": { + "name": "points_for_4th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 40 + }, + "points_for_5th": { + "name": "points_for_5th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "points_for_6th": { + "name": "points_for_6th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "points_for_7th": { + "name": "points_for_7th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 15 + }, + "points_for_8th": { + "name": "points_for_8th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 15 + }, + "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.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "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()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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 + }, + "simulator_type": { + "name": "simulator_type", + "type": "simulator_type", + "typeSchema": "public", + "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 + }, + "fantasy_season_id": { + "name": "fantasy_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "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 + }, + "scoring_pattern": { + "name": "scoring_pattern", + "type": "scoring_pattern", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "total_majors": { + "name": "total_majors", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "majors_completed": { + "name": "majors_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qualifying_points_finalized": { + "name": "qualifying_points_finalized", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "elo_calibration_exponent": { + "name": "elo_calibration_exponent", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "elo_min_rating": { + "name": "elo_min_rating", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1250 + }, + "elo_max_rating": { + "name": "elo_max_rating", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1750 + }, + "simulation_status": { + "name": "simulation_status", + "type": "simulation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "draft_on": { + "name": "draft_on", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "draft_off": { + "name": "draft_off", + "type": "date", + "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" + }, + "sports_seasons_fantasy_season_id_seasons_id_fk": { + "name": "sports_seasons_fantasy_season_id_seasons_id_fk", + "tableFrom": "sports_seasons", + "tableTo": "seasons", + "columnsFrom": [ + "fantasy_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_ev_snapshots": { + "name": "team_ev_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_ev_snapshots_unique": { + "name": "team_ev_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_ev_snapshots_team_id_teams_id_fk": { + "name": "team_ev_snapshots_team_id_teams_id_fk", + "tableFrom": "team_ev_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_ev_snapshots_season_id_seasons_id_fk": { + "name": "team_ev_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_ev_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_score_events": { + "name": "team_score_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scoring_event_name": { + "name": "scoring_event_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "sport_name": { + "name": "sport_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "match_id": { + "name": "match_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "participant_ids": { + "name": "participant_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "points_delta": { + "name": "points_delta", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_score_events_match_unique": { + "name": "team_score_events_match_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "match_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "match_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_score_events_event_unique": { + "name": "team_score_events_event_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "match_id IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_score_events_team_id_teams_id_fk": { + "name": "team_score_events_team_id_teams_id_fk", + "tableFrom": "team_score_events", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_score_events_season_id_seasons_id_fk": { + "name": "team_score_events_season_id_seasons_id_fk", + "tableFrom": "team_score_events", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_score_events_scoring_event_id_scoring_events_id_fk": { + "name": "team_score_events_scoring_event_id_scoring_events_id_fk", + "tableFrom": "team_score_events", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "team_score_events_match_id_playoff_matches_id_fk": { + "name": "team_score_events_match_id_playoff_matches_id_fk", + "tableFrom": "team_score_events", + "tableTo": "playoff_matches", + "columnsFrom": [ + "match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_sport_scores": { + "name": "team_sport_scores", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "participants_completed": { + "name": "participants_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_total": { + "name": "participants_total", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_sport_scores_team_id_teams_id_fk": { + "name": "team_sport_scores_team_id_teams_id_fk", + "tableFrom": "team_sport_scores", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_sport_scores_sports_season_id_sports_seasons_id_fk": { + "name": "team_sport_scores_sports_season_id_sports_seasons_id_fk", + "tableFrom": "team_sport_scores", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_standings": { + "name": "team_standings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_rank": { + "name": "current_rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "previous_rank": { + "name": "previous_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "first_place_count": { + "name": "first_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "second_place_count": { + "name": "second_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "third_place_count": { + "name": "third_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fourth_place_count": { + "name": "fourth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fifth_place_count": { + "name": "fifth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sixth_place_count": { + "name": "sixth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "seventh_place_count": { + "name": "seventh_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "eighth_place_count": { + "name": "eighth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_remaining": { + "name": "participants_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participants_finished": { + "name": "participants_finished", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_standings_team_id_teams_id_fk": { + "name": "team_standings_team_id_teams_id_fk", + "tableFrom": "team_standings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_season_id_seasons_id_fk": { + "name": "team_standings_season_id_seasons_id_fk", + "tableFrom": "team_standings", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_standings_snapshots": { + "name": "team_standings_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "rank": { + "name": "rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "first_place_count": { + "name": "first_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "second_place_count": { + "name": "second_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "third_place_count": { + "name": "third_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fourth_place_count": { + "name": "fourth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fifth_place_count": { + "name": "fifth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sixth_place_count": { + "name": "sixth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "seventh_place_count": { + "name": "seventh_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "eighth_place_count": { + "name": "eighth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_remaining": { + "name": "participants_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participants_finished": { + "name": "participants_finished", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_standings_snapshots_unique": { + "name": "team_standings_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_standings_snapshots_team_id_teams_id_fk": { + "name": "team_standings_snapshots_team_id_teams_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_snapshots_season_id_seasons_id_fk": { + "name": "team_standings_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_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.tournament_group_members": { + "name": "tournament_group_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "eliminated": { + "name": "eliminated", + "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": { + "tournament_group_members_tournament_group_id_tournament_groups_id_fk": { + "name": "tournament_group_members_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tournament_group_members_participant_id_season_participants_id_fk": { + "name": "tournament_group_members_participant_id_season_participants_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_groups": { + "name": "tournament_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "tournament_groups_scoring_event_id_scoring_events_id_fk": { + "name": "tournament_groups_scoring_event_id_scoring_events_id_fk", + "tableFrom": "tournament_groups", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_results": { + "name": "tournament_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_id": { + "name": "tournament_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "raw_score": { + "name": "raw_score", + "type": "numeric(10, 2)", + "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": { + "tournament_results_tournament_participant_unique": { + "name": "tournament_results_tournament_participant_unique", + "columns": [ + { + "expression": "tournament_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tournament_results_tournament_id_tournaments_id_fk": { + "name": "tournament_results_tournament_id_tournaments_id_fk", + "tableFrom": "tournament_results", + "tableTo": "tournaments", + "columnsFrom": [ + "tournament_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tournament_results_participant_id_participants_id_fk": { + "name": "tournament_results_participant_id_participants_id_fk", + "tableFrom": "tournament_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournaments": { + "name": "tournaments", + "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 + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "tournament_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "external_key": { + "name": "external_key", + "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": { + "tournaments_sport_name_year_unique": { + "name": "tournaments_sport_name_year_unique", + "columns": [ + { + "expression": "sport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "year", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tournaments_sport_id_sports_id_fk": { + "name": "tournaments_sport_id_sports_id_fk", + "tableFrom": "tournaments", + "tableTo": "sports", + "columnsFrom": [ + "sport_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": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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 + }, + "timezone": { + "name": "timezone", + "type": "varchar(50)", + "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": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verifications": { + "name": "verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.watchlist": { + "name": "watchlist", + "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 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "watchlist_season_team_participant_unique": { + "name": "watchlist_season_team_participant_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "watchlist_season_id_seasons_id_fk": { + "name": "watchlist_season_id_seasons_id_fk", + "tableFrom": "watchlist", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "watchlist_team_id_teams_id_fk": { + "name": "watchlist_team_id_teams_id_fk", + "tableFrom": "watchlist", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "watchlist_participant_id_season_participants_id_fk": { + "name": "watchlist_participant_id_season_participants_id_fk", + "tableFrom": "watchlist", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.audit_action": { + "name": "audit_action", + "schema": "public", + "values": [ + "league_settings_changed", + "draft_settings_changed", + "scoring_rules_changed", + "sports_changed", + "draft_order_set", + "draft_order_randomized", + "draft_started", + "draft_paused", + "draft_resumed", + "draft_reset", + "draft_rollback", + "force_autopick", + "force_manual_pick", + "draft_pick_changed", + "time_bank_edited", + "brackt_resolved" + ] + }, + "public.autodraft_mode": { + "name": "autodraft_mode", + "schema": "public", + "values": [ + "next_pick", + "while_on" + ] + }, + "public.draft_timer_mode": { + "name": "draft_timer_mode", + "schema": "public", + "values": [ + "chess_clock", + "standard" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "playoff_game", + "major_tournament", + "final_standings", + "schedule_event" + ] + }, + "public.overnight_pause_mode": { + "name": "overnight_pause_mode", + "schema": "public", + "values": [ + "none", + "league", + "per_user" + ] + }, + "public.picked_by_type": { + "name": "picked_by_type", + "schema": "public", + "values": [ + "owner", + "commissioner", + "admin", + "auto" + ] + }, + "public.playoff_match_game_status": { + "name": "playoff_match_game_status", + "schema": "public", + "values": [ + "scheduled", + "complete", + "postponed" + ] + }, + "public.probability_source": { + "name": "probability_source", + "schema": "public", + "values": [ + "manual", + "futures_odds", + "elo_simulation", + "performance_model" + ] + }, + "public.scoring_pattern": { + "name": "scoring_pattern", + "schema": "public", + "values": [ + "playoff_bracket", + "season_standings", + "qualifying_points" + ] + }, + "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.simulation_status": { + "name": "simulation_status", + "schema": "public", + "values": [ + "idle", + "running", + "failed" + ] + }, + "public.simulator_type": { + "name": "simulator_type", + "schema": "public", + "values": [ + "f1_standings", + "indycar_standings", + "golf_qualifying_points", + "playoff_bracket", + "ucl_bracket", + "ncaam_bracket", + "ncaaw_bracket", + "nba_bracket", + "nhl_bracket", + "nfl_bracket", + "afl_bracket", + "snooker_bracket", + "tennis_qualifying_points", + "mlb_bracket", + "wnba_bracket", + "world_cup", + "darts_bracket", + "cs2_major_qualifying_points", + "ncaa_football_bracket", + "llws_bracket", + "brackt" + ] + }, + "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" + ] + }, + "public.tournament_status": { + "name": "tournament_status", + "schema": "public", + "values": [ + "scheduled", + "in_progress", + "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 33ea263..ee8577f 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -659,6 +659,13 @@ "when": 1777783789103, "tag": "0093_ambiguous_hellcat", "breakpoints": true + }, + { + "idx": 94, + "version": "7", + "when": 1777920441672, + "tag": "0094_curved_nick_fury", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/socket.ts b/server/socket.ts index a8b32e1..4fbb51e 100644 --- a/server/socket.ts +++ b/server/socket.ts @@ -39,6 +39,9 @@ interface ServerToClientEvents { "participant-removed-from-queues": (data: { participantId: string }) => void; "queue-updated": (data: { queue: Array<{ id: string; teamId: string; seasonId: string; participantId: string; queuePosition: number }> }) => void; "watchlist-updated": (data: { participantIds: string[] }) => void; + "brackt-evs-updated": (data: { + updates: Array<{ participantId: string; expectedValue: number; vorpValue: number }>; + }) => void; "draft-state-sync": (data: { currentPickNumber: number; isPaused: boolean;