From 0277c4d25e430a96c4fa30e9c02088828b392899 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Sun, 15 Mar 2026 21:52:47 -0700 Subject: [PATCH] Add per-event configurable region config for NCAA-style brackets (#149) (#149) Replaces the hardcoded 2026 Men's region structure with a per-event configurable system so the same ncaa_68 template works for Women's March Madness (different region names and play-in placements each year). - Add `bracket_region_config jsonb` column to `scoring_events` - Export `ALL_16_SEEDS` and `BracketRegion`/`BracketPlayIn` interfaces from bracket-templates.ts so both server and client share the type - Admin bracket entry form now shows a "Configure Regions" section: editable region names + add/remove play-in seed selectors (ShadCN Select) per region; slot map updates live as config changes - Server action parses region config from form, validates total = 68, stores it on the event, and passes it to bracket generation - `advanceFirstFourWinner` reads stored `bracketRegionConfig` from the event row first, falls back to template.regions for existing events - Fix opponentSeed formula (was `seedNum <= 8 ? 17-n : n`, always `17-n`) - Remove duplicate ALL_SEEDS inline definitions (now a shared constant) Co-authored-by: Claude Sonnet 4.6 --- app/lib/bracket-templates.ts | 160 + app/models/__tests__/ncaa-68-bracket.test.ts | 184 +- app/models/playoff-match.ts | 270 +- app/models/scoring-event.ts | 4 + ...sons.$id.events.$eventId.bracket.server.ts | 39 +- ...ts-seasons.$id.events.$eventId.bracket.tsx | 252 +- database/schema.ts | 4 +- drizzle/0043_demonic_vanisher.sql | 1 + drizzle/meta/0043_snapshot.json | 3534 +++++++++++++++++ drizzle/meta/_journal.json | 7 + package-lock.json | 62 +- package.json | 2 +- 12 files changed, 4298 insertions(+), 221 deletions(-) create mode 100644 drizzle/0043_demonic_vanisher.sql create mode 100644 drizzle/meta/0043_snapshot.json diff --git a/app/lib/bracket-templates.ts b/app/lib/bracket-templates.ts index def8817..0399a14 100644 --- a/app/lib/bracket-templates.ts +++ b/app/lib/bracket-templates.ts @@ -25,6 +25,37 @@ export interface GroupStageConfig { groupLabels: string[]; } +/** + * A play-in game within a bracket region (e.g., NCAA First Four). + * Two teams compete; the winner fills a specific seed slot in the main bracket. + */ +export interface BracketPlayIn { + /** Which seed slot the winner fills (e.g., 11 or 16) */ + seedSlot: number; + /** Number of teams in this play-in game (always 2) */ + teams: 2; +} + +/** + * A named region within a bracket (e.g., NCAA "East", "South"). + * Each region contributes exactly 16 teams to the Round of 64. + */ +export interface BracketRegion { + /** Display name (e.g., "East", "South") */ + name: string; + /** + * Seed numbers that enter the region directly (no play-in). + * Must be ascending and contain every seed 1–16 except those covered by playIns. + * Length = 16 - playIns.length. + */ + directSeeds: number[]; + /** + * Play-in games for this region. Order here determines the order their teams + * appear in the participant array (after all direct-seed teams across all regions). + */ + playIns: BracketPlayIn[]; +} + export interface BracketTemplate { /** Unique identifier for this template */ id: string; @@ -40,6 +71,90 @@ export interface BracketTemplate { groupStage?: GroupStageConfig; /** Number of teams advancing to knockout stage (when groupStage is defined) */ knockoutTeams?: number; + /** + * Optional named region config. When present, the participant array for bracket + * generation is structured as: all direct seeds region-by-region in order, then + * all play-in teams grouped by region (and play-in order within each region). + */ + regions?: BracketRegion[]; +} + +/** All seed numbers in a standard 16-team regional bracket (1–16) */ +export const ALL_16_SEEDS: number[] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]; + +// Standard seeding order for a 16-team regional bracket +// Each entry is [higherSeed, lowerSeed] (lower number = better seed) +export const STANDARD_BRACKET_SEEDING: [number, number][] = [ + [1, 16], [8, 9], [5, 12], [4, 13], [6, 11], [3, 14], [7, 10], [2, 15], +]; + +/** + * Returns the 0-indexed position within the standard 8-match bracket order + * for a given seed slot. Used to compute which Round of 64 match a play-in + * winner advances to. + * + * Standard order: 1v16, 8v9, 5v12, 4v13, 6v11, 3v14, 7v10, 2v15 + */ +export function matchIndexForSeedSlot(seedSlot: number): number { + for (let i = 0; i < STANDARD_BRACKET_SEEDING.length; i++) { + if (STANDARD_BRACKET_SEEDING[i].includes(seedSlot)) return i; + } + throw new Error(`Seed slot ${seedSlot} not found in standard bracket seeding`); +} + +export interface NCAA68SlotMap { + /** Start index in participantIds for each region's direct seeds */ + directOffsets: number[]; + /** Ordered list of all play-ins, with participant index info and region context */ + playInOffsets: Array<{ + regionIndex: number; + playInIndex: number; + /** Index of the first team in this play-in within participantIds */ + startIndex: number; + seedSlot: number; + }>; + totalDirect: number; + totalPlayIn: number; +} + +/** + * Computes the participant array slot mapping for an NCAA-style bracket with regions. + * + * Participant array layout: + * [region 0 direct seeds] [region 1 direct seeds] ... [region N direct seeds] + * [region 0 play-in 0 team A, team B] [region 0 play-in 1 team A, team B] ... + * [region 1 play-in 0 team A, team B] ... + * + * This layout is shared between the server (bracket generation) and the client + * (form rendering) so both sides agree on which participant{i} maps where. + */ +export function buildNCAA68SlotMap(regions: BracketRegion[]): NCAA68SlotMap { + let cursor = 0; + const directOffsets: number[] = []; + + for (const region of regions) { + directOffsets.push(cursor); + cursor += region.directSeeds.length; + } + + const totalDirect = cursor; + const playInOffsets: NCAA68SlotMap["playInOffsets"] = []; + + for (let r = 0; r < regions.length; r++) { + for (let p = 0; p < regions[r].playIns.length; p++) { + playInOffsets.push({ + regionIndex: r, + playInIndex: p, + startIndex: cursor, + seedSlot: regions[r].playIns[p].seedSlot, + }); + cursor += 2; + } + } + + const totalPlayIn = cursor - totalDirect; + + return { directOffsets, playInOffsets, totalDirect, totalPlayIn }; } /** @@ -186,6 +301,22 @@ export const SIMPLE_32: BracketTemplate = { * NCAA March Madness (68 teams) * First Four (play-in) → Round of 64 → Round of 32 → Sweet Sixteen → Elite Eight → Final Four → Championship * Only Elite Eight and beyond score points per Q18 + * + * 2026 region config: + * East — 16 direct seeds, no play-ins + * South — 15 direct seeds, 16-seed play-in + * West — 15 direct seeds, 11-seed play-in + * Midwest — 14 direct seeds, 11-seed play-in + 16-seed play-in + * + * Participant array layout (68 slots): + * [0–15] East seeds 1–16 + * [16–30] South seeds 1–15 + * [31–45] West seeds 1–10, 12–16 + * [46–59] Midwest seeds 1–10, 12–15 + * [60–61] South 16-seed play-in (2 teams) + * [62–63] West 11-seed play-in (2 teams) + * [64–65] Midwest 11-seed play-in (2 teams) + * [66–67] Midwest 16-seed play-in (2 teams) */ export const NCAA_68: BracketTemplate = { id: "ncaa_68", @@ -236,6 +367,35 @@ export const NCAA_68: BracketTemplate = { isScoring: true, // Winner 1st, Loser 2nd }, ], + regions: [ + { + // East: all 16 seeds enter directly — no play-in games + name: "East", + directSeeds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + playIns: [], + }, + { + // South: seeds 1–15 direct; 16-seed determined by First Four play-in + name: "South", + directSeeds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + playIns: [{ seedSlot: 16, teams: 2 }], + }, + { + // West: seeds 1–10 and 12–16 direct; 11-seed determined by First Four play-in + name: "West", + directSeeds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16], + playIns: [{ seedSlot: 11, teams: 2 }], + }, + { + // Midwest: seeds 1–10 and 12–15 direct; both 11-seed and 16-seed via play-ins + name: "Midwest", + directSeeds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15], + playIns: [ + { seedSlot: 11, teams: 2 }, + { seedSlot: 16, teams: 2 }, + ], + }, + ], }; /** diff --git a/app/models/__tests__/ncaa-68-bracket.test.ts b/app/models/__tests__/ncaa-68-bracket.test.ts index 497241e..4aba622 100644 --- a/app/models/__tests__/ncaa-68-bracket.test.ts +++ b/app/models/__tests__/ncaa-68-bracket.test.ts @@ -1,5 +1,11 @@ import { describe, it, expect } from "vitest"; -import { NCAA_68, getScoringRoundType } from "~/lib/bracket-templates"; +import { + NCAA_68, + getScoringRoundType, + buildNCAA68SlotMap, + matchIndexForSeedSlot, + STANDARD_BRACKET_SEEDING, +} from "~/lib/bracket-templates"; /** * NCAA 68 Bracket Unit Tests - Phase 2.8 @@ -202,4 +208,180 @@ describe("NCAA 68 Bracket Template - Phase 2.8", () => { expect(directSeeds + firstFourTeams).toBe(68); }); }); + + describe("Region Config (2026)", () => { + it("has 4 named regions", () => { + expect(NCAA_68.regions).toBeDefined(); + expect(NCAA_68.regions).toHaveLength(4); + }); + + it("has regions in order: East, South, West, Midwest", () => { + const names = NCAA_68.regions!.map((r) => r.name); + expect(names).toEqual(["East", "South", "West", "Midwest"]); + }); + + it("East has 16 direct seeds and no play-ins", () => { + const east = NCAA_68.regions![0]; + expect(east.directSeeds).toHaveLength(16); + expect(east.playIns).toHaveLength(0); + }); + + it("South has 15 direct seeds and one 16-seed play-in", () => { + const south = NCAA_68.regions![1]; + expect(south.directSeeds).toHaveLength(15); + expect(south.playIns).toHaveLength(1); + expect(south.playIns[0].seedSlot).toBe(16); + }); + + it("West has 15 direct seeds and one 11-seed play-in", () => { + const west = NCAA_68.regions![2]; + expect(west.directSeeds).toHaveLength(15); + expect(west.playIns).toHaveLength(1); + expect(west.playIns[0].seedSlot).toBe(11); + }); + + it("Midwest has 14 direct seeds and two play-ins (11 and 16)", () => { + const midwest = NCAA_68.regions![3]; + expect(midwest.directSeeds).toHaveLength(14); + expect(midwest.playIns).toHaveLength(2); + expect(midwest.playIns[0].seedSlot).toBe(11); + expect(midwest.playIns[1].seedSlot).toBe(16); + }); + + it("total direct seeds across all regions equals 60", () => { + const total = NCAA_68.regions!.reduce((sum, r) => sum + r.directSeeds.length, 0); + expect(total).toBe(60); + }); + + it("total play-in teams across all regions equals 8", () => { + const total = NCAA_68.regions!.reduce( + (sum, r) => sum + r.playIns.reduce((s, pi) => s + pi.teams, 0), + 0 + ); + expect(total).toBe(8); + }); + + it("direct + play-in teams sum to 68", () => { + const direct = NCAA_68.regions!.reduce((sum, r) => sum + r.directSeeds.length, 0); + const playIn = NCAA_68.regions!.reduce( + (sum, r) => sum + r.playIns.reduce((s, pi) => s + pi.teams, 0), + 0 + ); + expect(direct + playIn).toBe(68); + }); + }); + + describe("buildNCAA68SlotMap", () => { + const slotMap = buildNCAA68SlotMap(NCAA_68.regions!); + + it("East starts at index 0", () => { + expect(slotMap.directOffsets[0]).toBe(0); + }); + + it("South starts at index 16 (after East's 16 direct seeds)", () => { + expect(slotMap.directOffsets[1]).toBe(16); + }); + + it("West starts at index 31 (after East 16 + South 15)", () => { + expect(slotMap.directOffsets[2]).toBe(31); + }); + + it("Midwest starts at index 46 (after East 16 + South 15 + West 15)", () => { + expect(slotMap.directOffsets[3]).toBe(46); + }); + + it("totalDirect is 60", () => { + expect(slotMap.totalDirect).toBe(60); + }); + + it("totalPlayIn is 8", () => { + expect(slotMap.totalPlayIn).toBe(8); + }); + + it("has 4 play-in offsets", () => { + expect(slotMap.playInOffsets).toHaveLength(4); + }); + + it("FF1 (South 16-seed) starts at index 60", () => { + const ff1 = slotMap.playInOffsets[0]; + expect(ff1.regionIndex).toBe(1); // South + expect(ff1.seedSlot).toBe(16); + expect(ff1.startIndex).toBe(60); + }); + + it("FF2 (West 11-seed) starts at index 62", () => { + const ff2 = slotMap.playInOffsets[1]; + expect(ff2.regionIndex).toBe(2); // West + expect(ff2.seedSlot).toBe(11); + expect(ff2.startIndex).toBe(62); + }); + + it("FF3 (Midwest 11-seed) starts at index 64", () => { + const ff3 = slotMap.playInOffsets[2]; + expect(ff3.regionIndex).toBe(3); // Midwest + expect(ff3.seedSlot).toBe(11); + expect(ff3.startIndex).toBe(64); + }); + + it("FF4 (Midwest 16-seed) starts at index 66", () => { + const ff4 = slotMap.playInOffsets[3]; + expect(ff4.regionIndex).toBe(3); // Midwest + expect(ff4.seedSlot).toBe(16); + expect(ff4.startIndex).toBe(66); + }); + }); + + describe("matchIndexForSeedSlot", () => { + it("seed 16 is at match index 0 (1v16)", () => { + expect(matchIndexForSeedSlot(16)).toBe(0); + }); + + it("seed 1 is at match index 0 (1v16)", () => { + expect(matchIndexForSeedSlot(1)).toBe(0); + }); + + it("seed 11 is at match index 4 (6v11)", () => { + expect(matchIndexForSeedSlot(11)).toBe(4); + }); + + it("seed 6 is at match index 4 (6v11)", () => { + expect(matchIndexForSeedSlot(6)).toBe(4); + }); + + it("all 8 standard seeding pairs are covered", () => { + for (const [hi, lo] of STANDARD_BRACKET_SEEDING) { + expect(() => matchIndexForSeedSlot(hi)).not.toThrow(); + expect(() => matchIndexForSeedSlot(lo)).not.toThrow(); + } + }); + }); + + describe("First Four → Round of 64 match number derivation", () => { + const slotMap = buildNCAA68SlotMap(NCAA_68.regions!); + + // r64MatchNumber = regionIndex * 8 + matchIndexForSeedSlot(seedSlot) + 1 + it("FF1 (South region index 1, seed 16) → R64 match 9", () => { + const ff1 = slotMap.playInOffsets[0]; // South 16-seed + const r64 = ff1.regionIndex * 8 + matchIndexForSeedSlot(ff1.seedSlot) + 1; + expect(r64).toBe(9); // East occupies matches 1-8; South match index 0 → 9 + }); + + it("FF2 (West region index 2, seed 11) → R64 match 21", () => { + const ff2 = slotMap.playInOffsets[1]; // West 11-seed + const r64 = ff2.regionIndex * 8 + matchIndexForSeedSlot(ff2.seedSlot) + 1; + expect(r64).toBe(21); // West starts at 17; match index 4 → 17+4=21 + }); + + it("FF3 (Midwest region index 3, seed 11) → R64 match 29", () => { + const ff3 = slotMap.playInOffsets[2]; // Midwest 11-seed + const r64 = ff3.regionIndex * 8 + matchIndexForSeedSlot(ff3.seedSlot) + 1; + expect(r64).toBe(29); // Midwest starts at 25; match index 4 → 25+4=29 + }); + + it("FF4 (Midwest region index 3, seed 16) → R64 match 25", () => { + const ff4 = slotMap.playInOffsets[3]; // Midwest 16-seed + const r64 = ff4.regionIndex * 8 + matchIndexForSeedSlot(ff4.seedSlot) + 1; + expect(r64).toBe(25); // Midwest starts at 25; match index 0 → 25 + }); + }); }); diff --git a/app/models/playoff-match.ts b/app/models/playoff-match.ts index f2436ef..52e3a5d 100644 --- a/app/models/playoff-match.ts +++ b/app/models/playoff-match.ts @@ -1,8 +1,13 @@ import { eq, and } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; -import type { BracketTemplate } from "~/lib/bracket-templates"; -import { getBracketTemplate } from "~/lib/bracket-templates"; +import type { BracketTemplate, BracketRegion } from "~/lib/bracket-templates"; +import { + getBracketTemplate, + buildNCAA68SlotMap, + matchIndexForSeedSlot, + STANDARD_BRACKET_SEEDING, +} from "~/lib/bracket-templates"; export type PlayoffMatch = typeof schema.playoffMatches.$inferSelect; export type NewPlayoffMatch = typeof schema.playoffMatches.$inferInsert; @@ -256,87 +261,6 @@ export async function advanceWinner( await updatePlayoffMatch(nextMatch.id, { [participantSlot]: winnerId }); } -/** - * NCAA 68 First Four seeding - * Returns matchups for the 4 play-in games - * - * First Four structure (using 0-indexed participant array): - * - Games 1-2: Participants 64-67 (16 seeds, lowest auto-qualifiers) - * - Winners are 16 seeds and face 1 seeds in Round of 64 - * - Games 3-4: Participants 60-63 (11/12 seeds, last at-large bids) - * - Winners are 11 seeds and face 6 seeds in Round of 64 - */ -function generateNCAA68FirstFourSeeding(): [number, number][] { - return [ - [64, 65], // First Four Game 1: 16-seed play-in A (winner is 16 seed, faces 1 seed) - [66, 67], // First Four Game 2: 16-seed play-in B (winner is 16 seed, faces 1 seed) - [60, 61], // First Four Game 3: 11/12-seed play-in A (winner is 11 seed, faces 6 seed) - [62, 63], // First Four Game 4: 11/12-seed play-in B (winner is 11 seed, faces 6 seed) - ]; -} - -/** - * NCAA 68 Round of 64 seeding - * Returns matchups for the Round of 64, with 4 TBD slots from First Four - * - * 68 teams total: 60 directly seeded (0-59) + 8 in First Four (60-67) - * Round of 64 needs 64 participants: 60 direct + 4 First Four winners - * - * Distribution: Each region has 15 direct seeds + 1 FF winner = 16 per region - * - Region 1: Seeds 0-14 + FF1 (FF1 winner is 16 seed, faces 1 seed) - * - Region 2: Seeds 15-29 + FF2 (FF2 winner is 16 seed, faces 1 seed) - * - Region 3: Seeds 30-44 + FF3 (FF3 winner is 11 seed, faces 6 seed) - * - Region 4: Seeds 45-59 + FF4 (FF4 winner is 11 seed, faces 6 seed) - * - * @returns Array of [seed1, seed2 or "FF{gameNum}"] pairs for 32 matches - */ -function generateNCAA68RoundOf64Seeding(): ([number, number] | [number, string] | [string, number])[] { - const matchups: ([number, number] | [number, string] | [string, number])[] = []; - - // Region 1: Seeds 0-14 + FF1 (16 total) - // Standard bracket order: 1v16, 8v9, 5v12, 4v13, 6v11, 3v14, 7v10, 2v15 - matchups.push([0, "FF1"]); // #1 vs #16 (FF1 winner) - matchups.push([7, 8]); // #8 vs #9 - matchups.push([4, 11]); // #5 vs #12 - matchups.push([3, 12]); // #4 vs #13 - matchups.push([5, 10]); // #6 vs #11 - matchups.push([2, 13]); // #3 vs #14 - matchups.push([6, 9]); // #7 vs #10 - matchups.push([1, 14]); // #2 vs #15 - - // Region 2: Seeds 15-29 + FF2 (16 total) - matchups.push([15, "FF2"]); // #1 vs #16 (FF2 winner) - matchups.push([22, 23]); // #8 vs #9 - matchups.push([19, 26]); // #5 vs #12 - matchups.push([18, 27]); // #4 vs #13 - matchups.push([20, 25]); // #6 vs #11 - matchups.push([17, 28]); // #3 vs #14 - matchups.push([21, 24]); // #7 vs #10 - matchups.push([16, 29]); // #2 vs #15 - - // Region 3: Seeds 30-44 + FF3 (16 total) - matchups.push([30, 44]); // #1 vs #16 - matchups.push([37, 38]); // #8 vs #9 - matchups.push([34, 41]); // #5 vs #12 - matchups.push([33, 42]); // #4 vs #13 - matchups.push([35, "FF3"]); // #6 vs #11 (FF3 winner = 11 seed) - matchups.push([32, 43]); // #3 vs #14 - matchups.push([36, 39]); // #7 vs #10 - matchups.push([31, 40]); // #2 vs #15 - - // Region 4: Seeds 45-59 + FF4 (16 total) - matchups.push([45, 59]); // #1 vs #16 - matchups.push([52, 53]); // #8 vs #9 - matchups.push([49, 56]); // #5 vs #12 - matchups.push([48, 57]); // #4 vs #13 - matchups.push([50, "FF4"]); // #6 vs #11 (FF4 winner = 11 seed) - matchups.push([47, 58]); // #3 vs #14 - matchups.push([51, 54]); // #7 vs #10 - matchups.push([46, 55]); // #2 vs #15 - - return matchups; -} - /** * Generate standard tournament seeding matchups * Returns pairs of seed indices for proper bracket balance @@ -395,7 +319,8 @@ function generateStandardSeeding(teamCount: number): [number, number][] { export async function generateBracketFromTemplate( eventId: string, templateId: string, - participantIds?: string[] + participantIds?: string[], + regionOverride?: BracketRegion[] ): Promise { const template = getBracketTemplate(templateId); if (!template) { @@ -415,8 +340,12 @@ export async function generateBracketFromTemplate( } // NCAA 68 requires special handling for First Four and Round of 64 + // Use regionOverride if supplied (per-event config), otherwise fall back to template.regions if (templateId === "ncaa_68") { - return await generateNCAA68Bracket(eventId, template, participantIds); + const effectiveTemplate = regionOverride + ? { ...template, regions: regionOverride } + : template; + return await generateNCAA68Bracket(eventId, effectiveTemplate, participantIds); } // NFL 14 requires special handling for bye weeks @@ -473,70 +402,96 @@ export async function generateBracketFromTemplate( } /** - * Generate NCAA 68 bracket with First Four and Round of 64 - * Phase 2.8: Special handling for First Four play-in games + * Generate NCAA 68 bracket using the template's regions config. + * + * Participant array layout (driven by buildNCAA68SlotMap): + * [region 0 direct seeds] [region 1 direct seeds] ... + * [play-in teams in region/play-in order, 2 per game] */ async function generateNCAA68Bracket( eventId: string, template: BracketTemplate, participantIds?: string[] ): Promise { - const matches: NewPlayoffMatch[] = []; + if (!template.regions || template.regions.length === 0) { + throw new Error("NCAA 68 template requires a regions config"); + } - // First Four: 4 play-in games - const firstFourSeeding = generateNCAA68FirstFourSeeding(); - for (let i = 0; i < firstFourSeeding.length; i++) { - const [seed1, seed2] = firstFourSeeding[i]; + const matches: NewPlayoffMatch[] = []; + const slotMap = buildNCAA68SlotMap(template.regions); + + // Per-region map: region index → FF labels for that region's play-ins (e.g. "FF1") + const regionFFLabels = new Map(); + for (let i = 0; i < slotMap.playInOffsets.length; i++) { + const { regionIndex, playInIndex } = slotMap.playInOffsets[i]; + if (!regionFFLabels.has(regionIndex)) regionFFLabels.set(regionIndex, []); + regionFFLabels.get(regionIndex)![playInIndex] = `FF${i + 1}`; + } + + // ── First Four ──────────────────────────────────────────────────────────── + for (let i = 0; i < slotMap.playInOffsets.length; i++) { + const { startIndex, seedSlot, regionIndex } = slotMap.playInOffsets[i]; + const regionName = template.regions[regionIndex].name; matches.push({ scoringEventId: eventId, round: "First Four", matchNumber: i + 1, - participant1Id: participantIds ? participantIds[seed1] : null, - participant2Id: participantIds ? participantIds[seed2] : null, + participant1Id: participantIds ? (participantIds[startIndex] ?? null) : null, + participant2Id: participantIds ? (participantIds[startIndex + 1] ?? null) : null, isComplete: false, - isScoring: false, // First Four doesn't score fantasy points + isScoring: false, templateRound: "First Four", - seedInfo: `${seed1 + 1} vs ${seed2 + 1}`, + seedInfo: `${regionName} ${seedSlot}-seed play-in`, }); } - // Round of 64: 32 games with some TBD slots from First Four - const roundOf64Seeding = generateNCAA68RoundOf64Seeding(); - for (let i = 0; i < roundOf64Seeding.length; i++) { - const matchup = roundOf64Seeding[i]; - let participant1Id: string | null = null; - let participant2Id: string | null = null; - let seedInfo: string | null = null; + // ── Round of 64: 8 matches per region ──────────────────────────────────── + let r64MatchNumber = 1; + for (let r = 0; r < template.regions.length; r++) { + const region = template.regions[r]; + const directOffset = slotMap.directOffsets[r]; + const labels = regionFFLabels.get(r) ?? []; - if (participantIds) { - const [seed1, seed2] = matchup; - - // Handle TBD slots (FF1-FF4 placeholders) - if (typeof seed1 === "number") { - participant1Id = participantIds[seed1]; - } // else it's "FF1", "FF2", etc. - will be filled by First Four winner - - if (typeof seed2 === "number") { - participant2Id = participantIds[seed2]; - } - - seedInfo = `${typeof seed1 === "number" ? seed1 + 1 : seed1} vs ${typeof seed2 === "number" ? seed2 + 1 : seed2}`; + // Map seed rank → participant array index or FF label + const seedToSlot = new Map(); + for (let i = 0; i < region.directSeeds.length; i++) { + seedToSlot.set(region.directSeeds[i], directOffset + i); + } + for (let p = 0; p < region.playIns.length; p++) { + seedToSlot.set(region.playIns[p].seedSlot, labels[p]); } - matches.push({ - scoringEventId: eventId, - round: "Round of 64", - matchNumber: i + 1, - participant1Id, - participant2Id, - isComplete: false, - isScoring: false, // Round of 64 doesn't score - templateRound: "Round of 64", - seedInfo, - }); + for (const [hiSeed, loSeed] of STANDARD_BRACKET_SEEDING) { + const slot1 = seedToSlot.get(hiSeed); + const slot2 = seedToSlot.get(loSeed); + + if (slot1 === undefined || slot2 === undefined) { + throw new Error( + `Region "${region.name}" missing seed ${slot1 === undefined ? hiSeed : loSeed}` + ); + } + + matches.push({ + scoringEventId: eventId, + round: "Round of 64", + matchNumber: r64MatchNumber++, + participant1Id: + participantIds && typeof slot1 === "number" + ? (participantIds[slot1] ?? null) + : null, + participant2Id: + participantIds && typeof slot2 === "number" + ? (participantIds[slot2] ?? null) + : null, + isComplete: false, + isScoring: false, + templateRound: "Round of 64", + seedInfo: `${region.name}: ${hiSeed} vs ${typeof slot2 === "string" ? slot2 : loSeed}`, + }); + } } - // Generate remaining rounds (Round of 32, Sweet Sixteen, Elite Eight, Final Four, Championship) + // ── Round of 32 → Championship (empty match shells) ────────────────────── for (let roundIndex = 2; roundIndex < template.rounds.length; roundIndex++) { const round = template.rounds[roundIndex]; for (let i = 0; i < round.matchCount; i++) { @@ -931,9 +886,9 @@ export async function advanceWinnerTemplate( } // Special handling for NCAA 68 First Four - // Phase 2.8: First Four winners advance to specific Round of 64 slots + // First Four winners advance to Round of 64 slots derived from the regions config if (template.id === "ncaa_68" && match.round === "First Four") { - return await advanceFirstFourWinner(match, winnerId); + return await advanceFirstFourWinner(match, winnerId, template); } // Find current round in template @@ -986,49 +941,58 @@ export async function advanceWinnerTemplate( } /** - * Advance First Four winner to Round of 64 - * Phase 2.8: NCAA 68 specific logic + * Advance a First Four winner to their target Round of 64 slot. * - * First Four match → Round of 64 match mapping: - * - FF Match 1 (16-seed) → R64 Match 1 (Region 1 vs #1 seed), participant2Id - * - FF Match 2 (16-seed) → R64 Match 9 (Region 2 vs #1 seed), participant2Id - * - FF Match 3 (11-seed) → R64 Match 21 (Region 3 vs #6 seed), participant2Id - * - FF Match 4 (11-seed) → R64 Match 29 (Region 4 vs #6 seed), participant2Id + * The target match number is computed dynamically from the template's regions config: + * r64MatchNumber = regionIndex * 8 + matchIndexForSeedSlot(seedSlot) + 1 + * + * The winner always fills participant2Id (they are the lower seed in their matchup). */ async function advanceFirstFourWinner( firstFourMatch: PlayoffMatch, - winnerId: string + winnerId: string, + template: BracketTemplate ): Promise { - // Map First Four match numbers to Round of 64 match numbers - const firstFourToRoundOf64: Record = { - 1: 1, // FF Game 1 (16-seed) → R64 Match 1 (vs 1 seed) - 2: 9, // FF Game 2 (16-seed) → R64 Match 9 (vs 1 seed) - 3: 21, // FF Game 3 (11-seed) → R64 Match 21 (vs 6 seed) - 4: 29, // FF Game 4 (11-seed) → R64 Match 29 (vs 6 seed) - }; + // Prefer the per-event stored region config (set at bracket generation time); + // fall back to the template's built-in regions for backwards compatibility. + const db = database(); + const eventRow = await db.query.scoringEvents.findFirst({ + where: eq(schema.scoringEvents.id, firstFourMatch.scoringEventId), + columns: { bracketRegionConfig: true }, + }); + const regions = + (eventRow?.bracketRegionConfig as BracketRegion[] | null) ?? template.regions; - const targetMatchNumber = firstFourToRoundOf64[firstFourMatch.matchNumber]; - if (!targetMatchNumber) { + if (!regions) { + throw new Error("NCAA 68 template requires regions config for First Four advancement"); + } + + const slotMap = buildNCAA68SlotMap(regions); + const ffIndex = firstFourMatch.matchNumber - 1; // 0-indexed + + if (ffIndex < 0 || ffIndex >= slotMap.playInOffsets.length) { throw new Error(`Invalid First Four match number: ${firstFourMatch.matchNumber}`); } - // Find the target Round of 64 match + const { regionIndex, seedSlot } = slotMap.playInOffsets[ffIndex]; + const matchIndexInRegion = matchIndexForSeedSlot(seedSlot); + // Each region contributes exactly 8 matches to the Round of 64, in region order + const r64MatchNumber = regionIndex * 8 + matchIndexInRegion + 1; + const roundOf64Matches = await findPlayoffMatchesByEventIdAndRound( firstFourMatch.scoringEventId, "Round of 64" ); - const targetMatch = roundOf64Matches.find((m) => m.matchNumber === targetMatchNumber); + const targetMatch = roundOf64Matches.find((m) => m.matchNumber === r64MatchNumber); if (!targetMatch) { - throw new Error(`Round of 64 match ${targetMatchNumber} not found`); + throw new Error(`Round of 64 match ${r64MatchNumber} not found`); } - // Check if participant2Id is already filled if (targetMatch.participant2Id) { - throw new Error(`Round of 64 match ${targetMatchNumber} participant2Id is already filled`); + throw new Error(`Round of 64 match ${r64MatchNumber} participant2Id is already filled`); } - // Fill the slot await updatePlayoffMatch(targetMatch.id, { participant2Id: winnerId }); } diff --git a/app/models/scoring-event.ts b/app/models/scoring-event.ts index b2f16e6..78b8a04 100644 --- a/app/models/scoring-event.ts +++ b/app/models/scoring-event.ts @@ -1,6 +1,7 @@ import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq, and, desc, asc, gte, lte, or, inArray, isNotNull } from "drizzle-orm"; +import type { BracketRegion } from "~/lib/bracket-templates"; import { recalculateAffectedLeagues } from "./scoring-calculator"; import { recalculateParticipantQP } from "./qualifying-points"; @@ -36,6 +37,8 @@ export interface UpdateScoringEventData { completedAt?: Date; bracketTemplateId?: string; scoringStartsAtRound?: string; + /** Per-event region config for NCAA-style brackets (overrides template defaults) */ + bracketRegionConfig?: BracketRegion[] | null; } /** @@ -165,6 +168,7 @@ export async function updateScoringEvent( if (data.completedAt !== undefined) updateData.completedAt = data.completedAt; if (data.bracketTemplateId !== undefined) updateData.bracketTemplateId = data.bracketTemplateId; if (data.scoringStartsAtRound !== undefined) updateData.scoringStartsAtRound = data.scoringStartsAtRound; + if (data.bracketRegionConfig !== undefined) updateData.bracketRegionConfig = data.bracketRegionConfig; const [updated] = await db .update(schema.scoringEvents) 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 0afcfde..38a3c83 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 @@ -22,7 +22,7 @@ import { deleteOddsForParticipant, } from "~/models/playoff-match-odds"; import { processPlayoffEvent } from "~/models/scoring-calculator"; -import { getBracketTemplate } from "~/lib/bracket-templates"; +import { getBracketTemplate, ALL_16_SEEDS, type BracketRegion } from "~/lib/bracket-templates"; import { createGroupsForEvent, addMembersToGroup, @@ -91,6 +91,38 @@ export async function action({ request, params }: Route.ActionArgs) { return { error: "Invalid bracket template" }; } + // Parse per-event region config for NCAA-style brackets + let regionOverride: BracketRegion[] | undefined; + if (template.regions && template.regions.length > 0) { + const regions: BracketRegion[] = []; + for (let r = 0; r < template.regions.length; r++) { + const name = (formData.get(`regionName${r}`) as string | null)?.trim() + || `Region ${r + 1}`; + const seedsRaw = (formData.get(`regionPlayInSeeds${r}`) as string | null) || ""; + const playInSeeds = seedsRaw + .split(",") + .map((s) => parseInt(s.trim(), 10)) + .filter((n) => !isNaN(n) && n >= 1 && n <= 16); + const playInSet = new Set(playInSeeds); + regions.push({ + name, + directSeeds: ALL_16_SEEDS.filter((s) => !playInSet.has(s)), + playIns: playInSeeds.map((s) => ({ seedSlot: s, teams: 2 as const })), + }); + } + // Validate total = template.totalTeams + const total = regions.reduce( + (sum, r) => sum + r.directSeeds.length + r.playIns.length * 2, + 0 + ); + if (total !== template.totalTeams) { + return { + error: `Region config accounts for ${total} team slots but template requires ${template.totalTeams}`, + }; + } + regionOverride = regions; + } + const participantIds: string[] = []; for (let i = 0; i < template.totalTeams; i++) { @@ -108,7 +140,7 @@ export async function action({ request, params }: Route.ActionArgs) { } try { - await generateBracketFromTemplate(params.eventId, templateId, participantIds); + await generateBracketFromTemplate(params.eventId, templateId, participantIds, regionOverride); // PHASE 5.3: Mark participants NOT in the bracket as eliminated const event = await getScoringEventById(params.eventId); @@ -134,10 +166,11 @@ export async function action({ request, params }: Route.ActionArgs) { console.log(`[BracketGeneration] Marked ${allParticipants.length - participantIds.length} participants as eliminated`); } - // Update the event to store the template ID and scoring start round + // Update the event to store the template ID, scoring start round, and region config await updateScoringEvent(params.eventId, { bracketTemplateId: templateId, scoringStartsAtRound: template.scoringStartsAtRound, + bracketRegionConfig: regionOverride, }); return { success: "Bracket generated successfully" }; diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx index 52472fb..59b61a0 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx @@ -22,7 +22,7 @@ import { SelectValue, } from "~/components/ui/select"; import { ArrowLeft, Plus, Trophy, X, Check, ChevronDown, ChevronRight, Calendar, TrendingUp, Trash2 } from "lucide-react"; -import { getAllBracketTemplates, getBracketTemplate } from "~/lib/bracket-templates"; +import { getAllBracketTemplates, getBracketTemplate, buildNCAA68SlotMap, ALL_16_SEEDS, type BracketRegion } from "~/lib/bracket-templates"; import { Table, TableBody, @@ -67,6 +67,84 @@ export default function EventBracket({ // Determine if this is a group-stage event const hasGroupStage = template?.groupStage != null; + + // Determine if this template uses named regions (e.g. NCAA 68) + const hasRegions = (template?.regions?.length ?? 0) > 0; + + // Per-region configuration state: name + which seed slots are play-ins + // Initialized from: stored event config → template defaults → empty + const buildDefaultRegionConfig = (tmpl: typeof template) => { + const source = + (event.bracketRegionConfig as unknown as BracketRegion[] | null) ?? tmpl?.regions ?? []; + return source.map((r) => ({ + name: r.name, + playInSeeds: r.playIns.map((pi) => pi.seedSlot), + })); + }; + const [regionConfig, setRegionConfig] = useState(() => buildDefaultRegionConfig(template)); + + // Derive effective BracketRegion[] from regionConfig state + const effectiveRegions: BracketRegion[] = useMemo(() => { + return regionConfig.map((cfg) => { + const playInSet = new Set(cfg.playInSeeds); + return { + name: cfg.name, + directSeeds: ALL_16_SEEDS.filter((s) => !playInSet.has(s)), + playIns: cfg.playInSeeds.map((s) => ({ seedSlot: s, teams: 2 as const })), + }; + }); + }, [regionConfig]); + + const regionSlotMap = useMemo( + () => (effectiveRegions.length > 0 ? buildNCAA68SlotMap(effectiveRegions) : null), + [effectiveRegions] + ); + + // Region config handlers + const updateRegionName = (regionIdx: number, name: string) => { + setRegionConfig((prev) => + prev.map((r, i) => (i === regionIdx ? { ...r, name } : r)) + ); + }; + const addPlayIn = (regionIdx: number) => { + setRegionConfig((prev) => + prev.map((r, i) => { + if (i !== regionIdx || r.playInSeeds.length >= 2) return r; + // Default to seed 16 if no play-ins yet, otherwise seed 11 + const defaultSeed = r.playInSeeds.length === 0 ? 16 : 11; + const seed = r.playInSeeds.includes(defaultSeed) + ? ([11, 16].find((s) => !r.playInSeeds.includes(s)) ?? 11) + : defaultSeed; + return { ...r, playInSeeds: [...r.playInSeeds, seed] }; + }) + ); + // Reset participant selections since slot indices change + setSelectedParticipants({}); + }; + const removePlayIn = (regionIdx: number, piIdx: number) => { + setRegionConfig((prev) => + prev.map((r, i) => + i === regionIdx + ? { ...r, playInSeeds: r.playInSeeds.filter((_, j) => j !== piIdx) } + : r + ) + ); + setSelectedParticipants({}); + }; + const updatePlayInSeed = (regionIdx: number, piIdx: number, seed: number) => { + setRegionConfig((prev) => + prev.map((r, i) => + i === regionIdx + ? { + ...r, + playInSeeds: r.playInSeeds.map((s, j) => (j === piIdx ? seed : s)), + } + : r + ) + ); + setSelectedParticipants({}); + }; + const isGroupStageEvent = tournamentGroups.length > 0; // Determine the phase for group-stage events @@ -133,10 +211,12 @@ export default function EventBracket({ } }, [actionData?.success]); - // Reset selected participants when template changes + // Reset selected participants and region config when template changes const handleTemplateChange = (newTemplateId: string) => { setSelectedTemplate(newTemplateId); setSelectedParticipants({}); + const newTemplate = templates.find((t) => t.id === newTemplateId); + setRegionConfig(buildDefaultRegionConfig(newTemplate)); }; // Update selected participant for a seed @@ -372,8 +452,172 @@ export default function EventBracket({ )} - {/* Non-group-stage template: show seeded list */} - {!hasGroupStage && ( + {/* Non-group-stage template: region-grouped or flat seeded list */} + {!hasGroupStage && hasRegions && regionSlotMap && ( +
+ + {/* ── Region configuration ─────────────────────────────── */} +
+
+

Configure Regions

+

+ Set each region's name and which seed slots are play-in games. +

+
+ {regionConfig.map((region, r) => ( +
+ updateRegionName(r, e.target.value)} + placeholder={`Region ${r + 1}`} + /> + {region.playInSeeds.map((seed, pi) => ( +
+ play-in seed: + + +
+ ))} + {region.playInSeeds.length < 2 && ( + + )} +
+ ))} + {/* Hidden fields so server receives the region config */} + {regionConfig.map((region, r) => ( + + + + + ))} +
+ + {/* ── Seed entry per region ─────────────────────────────── */} +

+ Select participants by region. Use search to find teams quickly. +

+
+ {effectiveRegions.map((region, r) => { + const directOffset = regionSlotMap.directOffsets[r]; + return ( +
+

{region.name || `Region ${r + 1}`}

+ {region.directSeeds.map((seedNum, pos) => { + const globalIdx = directOffset + pos; + const opponentSeed = 17 - seedNum; + const opponentIsPlayIn = region.playIns.some((pi) => pi.seedSlot === opponentSeed); + return ( +
+ +
+ + handleParticipantChange(globalIdx, value)} + placeholder={`Seed ${seedNum}…`} + /> +
+ {opponentIsPlayIn && ( + ← faces play-in + )} +
+ ); + })} + {region.playIns.length > 0 && ( +

+ {region.playIns.map((pi) => `Seed ${pi.seedSlot}`).join(" & ")} via play-in ↓ +

+ )} +
+ ); + })} +
+ + {/* ── Play-in team entry ─────────────────────────────────── */} + {regionSlotMap.playInOffsets.length > 0 && ( +
+

Play-In Teams

+

+ Enter both teams for each play-in game. The winner advances to the main bracket. +

+ {regionSlotMap.playInOffsets.map((pi, ffIdx) => { + const regionName = effectiveRegions[pi.regionIndex]?.name || `Region ${pi.regionIndex + 1}`; + const idx1 = pi.startIndex; + const idx2 = pi.startIndex + 1; + return ( +
+

+ Play-In {ffIdx + 1} — {regionName} {pi.seedSlot}-seed +

+ {[idx1, idx2].map((globalIdx, slot) => ( +
+ +
+ + handleParticipantChange(globalIdx, value)} + placeholder={`${regionName} ${pi.seedSlot}-seed play-in team ${slot + 1}…`} + /> +
+
+ ))} +
+ ); + })} +
+ )} +
+ )} + + {/* Non-group-stage, non-region template: flat seeded list */} + {!hasGroupStage && !hasRegions && (

diff --git a/database/schema.ts b/database/schema.ts index 856b4a4..53d0d80 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -1,4 +1,4 @@ -import { integer, pgTable, varchar, uuid, timestamp, pgEnum, boolean, text, date, decimal, uniqueIndex } from "drizzle-orm/pg-core"; +import { integer, pgTable, varchar, uuid, timestamp, pgEnum, boolean, text, date, decimal, uniqueIndex, jsonb } from "drizzle-orm/pg-core"; import { relations } from "drizzle-orm"; // Users table - synced from Clerk @@ -351,6 +351,8 @@ export const scoringEvents = pgTable("scoring_events", { // Template system (Phase 2.6) bracketTemplateId: varchar("bracket_template_id", { length: 50 }), // "ncaa_68", "nfl_14", "simple_16", etc. scoringStartsAtRound: varchar("scoring_starts_at_round", { length: 50 }), // "Elite Eight", "Quarterfinals", etc. + // Per-event region config for NCAA-style brackets (overrides template defaults) + bracketRegionConfig: jsonb("bracket_region_config"), // BracketRegion[] | null isComplete: boolean("is_complete").notNull().default(false), completedAt: timestamp("completed_at"), createdAt: timestamp("created_at").defaultNow().notNull(), diff --git a/drizzle/0043_demonic_vanisher.sql b/drizzle/0043_demonic_vanisher.sql new file mode 100644 index 0000000..343808c --- /dev/null +++ b/drizzle/0043_demonic_vanisher.sql @@ -0,0 +1 @@ +ALTER TABLE "scoring_events" ADD COLUMN "bracket_region_config" jsonb; \ No newline at end of file diff --git a/drizzle/meta/0043_snapshot.json b/drizzle/meta/0043_snapshot.json new file mode 100644 index 0000000..e200e6a --- /dev/null +++ b/drizzle/meta/0043_snapshot.json @@ -0,0 +1,3534 @@ +{ + "id": "16019fc9-f516-4d33-86ba-c2971ca492c0", + "prevId": "e43952eb-be51-480f-90ef-3bceb407d6d9", + "version": "7", + "dialect": "postgresql", + "tables": { + "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.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "commissioners_league_id_leagues_id_fk": { + "name": "commissioners_league_id_leagues_id_fk", + "tableFrom": "commissioners", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_picks": { + "name": "draft_picks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pick_number": { + "name": "pick_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pick_in_round": { + "name": "pick_in_round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "picked_by_user_id": { + "name": "picked_by_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "picked_by_type": { + "name": "picked_by_type", + "type": "picked_by_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "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_participants_id_fk": { + "name": "draft_picks_participant_id_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_queue": { + "name": "draft_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_position": { + "name": "queue_position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_queue_season_id_seasons_id_fk": { + "name": "draft_queue_season_id_seasons_id_fk", + "tableFrom": "draft_queue", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_team_id_teams_id_fk": { + "name": "draft_queue_team_id_teams_id_fk", + "tableFrom": "draft_queue", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_participant_id_participants_id_fk": { + "name": "draft_queue_participant_id_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_slots": { + "name": "draft_slots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "draft_order": { + "name": "draft_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_slots_season_id_seasons_id_fk": { + "name": "draft_slots_season_id_seasons_id_fk", + "tableFrom": "draft_slots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_slots_team_id_teams_id_fk": { + "name": "draft_slots_team_id_teams_id_fk", + "tableFrom": "draft_slots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_timers": { + "name": "draft_timers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "time_remaining": { + "name": "time_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_timers_season_id_seasons_id_fk": { + "name": "draft_timers_season_id_seasons_id_fk", + "tableFrom": "draft_timers", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_timers_team_id_teams_id_fk": { + "name": "draft_timers_team_id_teams_id_fk", + "tableFrom": "draft_timers", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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 + }, + "participant_id": { + "name": "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": {}, + "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_participant_id_participants_id_fk": { + "name": "event_results_participant_id_participants_id_fk", + "tableFrom": "event_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leagues": { + "name": "leagues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "current_season_id": { + "name": "current_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_public_draft_board": { + "name": "is_public_draft_board", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_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_participants_id_fk": { + "name": "participant_ev_snapshots_participant_id_participants_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "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_expected_values": { + "name": "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 + }, + "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": {}, + "foreignKeys": { + "participant_expected_values_participant_id_participants_id_fk": { + "name": "participant_expected_values_participant_id_participants_id_fk", + "tableFrom": "participant_expected_values", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_expected_values_sports_season_id_sports_seasons_id_fk": { + "name": "participant_expected_values_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_expected_values", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_qualifying_totals": { + "name": "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": { + "participant_qualifying_totals_participant_id_participants_id_fk": { + "name": "participant_qualifying_totals_participant_id_participants_id_fk", + "tableFrom": "participant_qualifying_totals", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk": { + "name": "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_qualifying_totals", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_results": { + "name": "participant_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "final_position": { + "name": "final_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "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": { + "participant_results_participant_id_participants_id_fk": { + "name": "participant_results_participant_id_participants_id_fk", + "tableFrom": "participant_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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_participants_id_fk": { + "name": "participant_season_results_participant_id_participants_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "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.participants": { + "name": "participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "short_name": { + "name": "short_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "expected_value": { + "name": "expected_value", + "type": "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": {}, + "foreignKeys": { + "participants_sports_season_id_sports_seasons_id_fk": { + "name": "participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participants", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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_participants_id_fk": { + "name": "playoff_match_games_winner_id_participants_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "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_participants_id_fk": { + "name": "playoff_match_odds_participant_id_participants_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "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_participants_id_fk": { + "name": "playoff_matches_participant1_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_participant2_id_participants_id_fk": { + "name": "playoff_matches_participant2_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_winner_id_participants_id_fk": { + "name": "playoff_matches_winner_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_loser_id_participants_id_fk": { + "name": "playoff_matches_loser_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "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.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 + }, + "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" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_sports": { + "name": "season_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_sports_season_id_seasons_id_fk": { + "name": "season_sports_season_id_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_template_sports": { + "name": "season_template_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_template_sports_template_id_season_templates_id_fk": { + "name": "season_template_sports_template_id_season_templates_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_template_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_template_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_templates": { + "name": "season_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.seasons": { + "name": "seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pre_draft'" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "draft_rounds": { + "name": "draft_rounds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20 + }, + "flex_spots": { + "name": "flex_spots", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "draft_date_time": { + "name": "draft_date_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_initial_time": { + "name": "draft_initial_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 120 + }, + "draft_increment_time": { + "name": "draft_increment_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 30 + }, + "current_pick_number": { + "name": "current_pick_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "draft_started_at": { + "name": "draft_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "invite_code": { + "name": "invite_code", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "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.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 + }, + "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'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sports_seasons_sport_id_sports_id_fk": { + "name": "sports_seasons_sport_id_sports_id_fk", + "tableFrom": "sports_seasons", + "tableTo": "sports", + "columnsFrom": [ + "sport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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_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": {}, + "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_participants_id_fk": { + "name": "tournament_group_members_participant_id_participants_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "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.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.autodraft_mode": { + "name": "autodraft_mode", + "schema": "public", + "values": [ + "next_pick", + "while_on" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "playoff_game", + "major_tournament", + "final_standings", + "schedule_event" + ] + }, + "public.picked_by_type": { + "name": "picked_by_type", + "schema": "public", + "values": [ + "owner", + "commissioner", + "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" + ] + }, + "public.sport_type": { + "name": "sport_type", + "schema": "public", + "values": [ + "team", + "individual" + ] + }, + "public.sports_season_status": { + "name": "sports_season_status", + "schema": "public", + "values": [ + "upcoming", + "active", + "completed" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 39d844f..c138f3a 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -302,6 +302,13 @@ "when": 1773600000000, "tag": "0042_migrate_event_date_to_starts_at", "breakpoints": true + }, + { + "idx": 43, + "version": "7", + "when": 1773634394633, + "tag": "0043_demonic_vanisher", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6434155..7da1964 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,7 +76,7 @@ "dotenv-cli": "^8.0.0", "esbuild": "^0.25.11", "jsdom": "^27.0.1", - "shadcn": "^4.0.5", + "shadcn": "^4.0.8", "tailwindcss": "^4.1.4", "tsx": "^4.20.6", "tw-animate-css": "^1.4.0", @@ -107,35 +107,6 @@ "node": ">=6.0.0" } }, - "node_modules/@antfu/ni": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/@antfu/ni/-/ni-25.0.0.tgz", - "integrity": "sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansis": "^4.0.0", - "fzf": "^0.5.2", - "package-manager-detector": "^1.3.0", - "tinyexec": "^1.0.1" - }, - "bin": { - "na": "bin/na.mjs", - "nci": "bin/nci.mjs", - "ni": "bin/ni.mjs", - "nlx": "bin/nlx.mjs", - "nr": "bin/nr.mjs", - "nun": "bin/nun.mjs", - "nup": "bin/nup.mjs" - } - }, - "node_modules/@antfu/ni/node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", - "dev": true, - "license": "MIT" - }, "node_modules/@asamuzakjp/css-color": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz", @@ -6641,16 +6612,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", - "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - } - }, "node_modules/arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", @@ -9918,13 +9879,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fzf": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", - "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -12490,13 +12444,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "node_modules/package-manager-detector": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.5.0.tgz", - "integrity": "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==", - "dev": true, - "license": "MIT" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13728,13 +13675,12 @@ "license": "ISC" }, "node_modules/shadcn": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/shadcn/-/shadcn-4.0.5.tgz", - "integrity": "sha512-z0SOHEU1+ADam1UJHrgxJhUsOb0/jBoYc+u9mhWs071KrnORq48X7uCwG3mD2ysQEBtOfeK/MxMGsmzL5Jt+Jg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/shadcn/-/shadcn-4.0.8.tgz", + "integrity": "sha512-DVAyeo95TQ/OvaHugLm5V0Dqz3al+dnoP3mZdWWxKJ33IYG1jN5B3sGZyNaYsfzm7JsWokfksSzDl83LnmMing==", "dev": true, "license": "MIT", "dependencies": { - "@antfu/ni": "^25.0.0", "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", diff --git a/package.json b/package.json index e0b0fb1..c9c19e6 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "dotenv-cli": "^8.0.0", "esbuild": "^0.25.11", "jsdom": "^27.0.1", - "shadcn": "^4.0.5", + "shadcn": "^4.0.8", "tailwindcss": "^4.1.4", "tsx": "^4.20.6", "tw-animate-css": "^1.4.0",