diff --git a/app/lib/audit-log-display.ts b/app/lib/audit-log-display.ts new file mode 100644 index 0000000..5728dbf --- /dev/null +++ b/app/lib/audit-log-display.ts @@ -0,0 +1,106 @@ +import type { AuditLogEntry } from "~/models/audit-log"; + +export const AUDIT_ACTION_LABELS: Record = { + league_settings_changed: "League Settings Changed", + draft_settings_changed: "Draft Settings Changed", + scoring_rules_changed: "Scoring Rules Changed", + draft_order_set: "Draft Order Set", + draft_order_randomized: "Draft Order Randomized", + draft_started: "Draft Started", + draft_paused: "Draft Paused", + draft_resumed: "Draft Resumed", + draft_reset: "Draft Reset", + draft_rollback: "Draft Rolled Back", + force_autopick: "Forced Auto-Pick", + force_manual_pick: "Forced Manual Pick", + draft_pick_changed: "Pick Replaced", + time_bank_edited: "Time Bank Edited", +}; + +function formatSeconds(seconds: number): string { + const m = Math.floor(Math.abs(seconds) / 60); + const s = Math.abs(seconds) % 60; + const sign = seconds < 0 ? "-" : "+"; + if (m === 0) return `${sign}${s}s`; + return `${sign}${m}m ${s}s`; +} + +function formatTimeRemaining(seconds: number): string { + const m = Math.floor(seconds / 60); + const s = seconds % 60; + return `${m}:${String(s).padStart(2, "0")}`; +} + +/** + * Returns a concise human-readable summary of an audit log entry. + * Shared between the league home page summary widget and the full audit log page. + */ +export function formatAuditDetail(entry: AuditLogEntry): string { + const d = (entry.details ?? {}) as Record; + + switch (entry.action) { + case "draft_rollback": + return `Rolled back to pick #${d.rolledBackToPickNumber}`; + + case "time_bank_edited": { + const adj = Number(d.adjustment ?? 0); + const remaining = Number(d.newTimeRemaining ?? 0); + return `${formatSeconds(adj)} for ${d.teamName ?? "team"} (now ${formatTimeRemaining(remaining)})`; + } + + case "draft_pick_changed": + return `Pick #${d.pickNumber}: ${d.oldParticipantName} → ${d.newParticipantName}`; + + case "force_autopick": + return `Auto-picked ${d.participantName} for ${d.teamName} at pick #${d.pickNumber}`; + + case "force_manual_pick": + return `Picked ${d.participantName} for ${d.teamName} at pick #${d.pickNumber}`; + + case "draft_order_set": + return "Draft order manually set"; + + case "draft_order_randomized": + return "Draft order randomized"; + + case "draft_reset": { + const prev = d.previousPickNumber; + return prev !== null && prev !== undefined + ? `Draft reset (was at pick #${prev})` + : "Draft reset"; + } + + case "league_settings_changed": { + const fields = (d.changedFields as string[] | undefined) ?? []; + return fields.length > 0 + ? `Changed: ${fields.join(", ")}` + : "League settings updated"; + } + + case "draft_settings_changed": { + const fields = (d.changedFields as string[] | undefined) ?? []; + return fields.length > 0 + ? `Changed: ${fields.join(", ")}` + : "Draft settings updated"; + } + + case "scoring_rules_changed": + return "Scoring rules updated"; + + case "draft_started": + return "Draft started"; + + case "draft_paused": + return d.pickNumber !== null && d.pickNumber !== undefined + ? `Draft paused at pick #${d.pickNumber}` + : "Draft paused"; + + case "draft_resumed": + return d.pickNumber !== null && d.pickNumber !== undefined + ? `Draft resumed at pick #${d.pickNumber}` + : "Draft resumed"; + + default: + return (entry.action as string).replace(/_/g, " "); + } +} diff --git a/app/models/__tests__/audit-log.test.ts b/app/models/__tests__/audit-log.test.ts new file mode 100644 index 0000000..51b8056 --- /dev/null +++ b/app/models/__tests__/audit-log.test.ts @@ -0,0 +1,287 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("~/models/user", () => ({ + findUserByClerkId: vi.fn(), + getUserDisplayName: vi.fn(), +})); + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { + createAuditLogEntry, + getAuditLogForSeason, + logCommissionerAction, +} from "../audit-log"; +import { findUserByClerkId, getUserDisplayName } from "~/models/user"; +import { database } from "~/database/context"; + +const SEASON_ID = "season-1"; +const LEAGUE_ID = "league-1"; +const ACTOR_CLERK_ID = "user_clerk_1"; + +const SAMPLE_ENTRY = { + id: "entry-1", + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + actorDisplayName: "Alice", + action: "draft_rollback" as const, + affectedTeamIds: ["team-1"], + details: { rolledBackToPickNumber: 5, previousPickNumber: 10 }, + createdAt: new Date("2025-01-01T12:00:00Z"), +}; + +/** + * Builds a mock db for insert operations. + */ +function makeInsertDb(returnValue: object) { + return { + insert: vi.fn().mockReturnValue({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([returnValue]), + }), + }), + }; +} + +/** + * Builds a mock db for getAuditLogForSeason which fires two select queries in + * parallel: one for the data rows and one for the count. We use two + * mockResolvedValueOnce calls so the first promise.all resolves the data and + * the second resolves the count. + * + * Both calls share the same chain: select().from().where() — after that they + * diverge (data adds .orderBy().limit().offset(), count stops). We build a + * single chainable mock where the terminal call is a jest.fn that returns + * different values on each invocation. + */ +function makeSelectDb( + dataRows: object[], + countRow: { count: number } +): object { + // Count chain: select().from().where() → resolves to [countRow] + const countChain = { + from: vi.fn().mockReturnThis(), + where: vi.fn().mockResolvedValue([countRow]), + }; + + // Data chain: select().from().where().orderBy().limit().offset() → resolves to dataRows + const dataChain = { + from: vi.fn().mockReturnThis(), + where: vi.fn().mockReturnThis(), + orderBy: vi.fn().mockReturnThis(), + limit: vi.fn().mockReturnThis(), + offset: vi.fn().mockResolvedValue(dataRows), + }; + + let selectCallCount = 0; + return { + select: vi.fn().mockImplementation(() => { + // First call is the data query; second call is the count query + selectCallCount++; + return selectCallCount === 1 ? dataChain : countChain; + }), + }; +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +// ─── createAuditLogEntry ────────────────────────────────────────────────────── + +describe("createAuditLogEntry", () => { + it("inserts the entry and returns the persisted record", async () => { + vi.mocked(database).mockReturnValue(makeInsertDb(SAMPLE_ENTRY) as never); + + const result = await createAuditLogEntry({ + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + actorDisplayName: "Alice", + action: "draft_rollback", + affectedTeamIds: ["team-1"], + details: { rolledBackToPickNumber: 5, previousPickNumber: 10 }, + }); + + expect(result).toEqual(SAMPLE_ENTRY); + }); +}); + +// ─── getAuditLogForSeason ───────────────────────────────────────────────────── + +describe("getAuditLogForSeason", () => { + it("returns entries and computes hasMore=false when on the last page", async () => { + vi.mocked(database).mockReturnValue( + makeSelectDb([SAMPLE_ENTRY], { count: 1 }) as never + ); + + const result = await getAuditLogForSeason(SEASON_ID, { + limit: 50, + offset: 0, + }); + + expect(result.entries).toHaveLength(1); + expect(result.total).toBe(1); + expect(result.hasMore).toBe(false); + }); + + it("returns hasMore=true when more entries remain", async () => { + // 51 total, fetching first 50 starting at offset 0 → 50 returned → hasMore + const manyEntries = Array.from({ length: 50 }, (_, i) => ({ + ...SAMPLE_ENTRY, + id: `entry-${i}`, + })); + + vi.mocked(database).mockReturnValue( + makeSelectDb(manyEntries, { count: 51 }) as never + ); + + const result = await getAuditLogForSeason(SEASON_ID, { + limit: 50, + offset: 0, + }); + + expect(result.total).toBe(51); + expect(result.hasMore).toBe(true); + }); + + it("returns hasMore=false on the final page", async () => { + // 51 total, on page 2 (offset=50), 1 entry returned + vi.mocked(database).mockReturnValue( + makeSelectDb([SAMPLE_ENTRY], { count: 51 }) as never + ); + + const result = await getAuditLogForSeason(SEASON_ID, { + limit: 50, + offset: 50, + }); + + expect(result.hasMore).toBe(false); + }); + + it("uses default limit of 50 and offset of 0 when options are omitted", async () => { + const db = makeSelectDb([SAMPLE_ENTRY], { count: 1 }); + vi.mocked(database).mockReturnValue(db as never); + + await getAuditLogForSeason(SEASON_ID); + + // Verify the limit and offset were applied via the data chain + const dataChain = (db as { select: ReturnType }).select.mock.results[0].value; + expect(dataChain.limit).toHaveBeenCalledWith(50); + expect(dataChain.offset).toHaveBeenCalledWith(0); + }); + + it("returns an empty page when no entries exist", async () => { + vi.mocked(database).mockReturnValue( + makeSelectDb([], { count: 0 }) as never + ); + + const result = await getAuditLogForSeason(SEASON_ID); + + expect(result.entries).toHaveLength(0); + expect(result.total).toBe(0); + expect(result.hasMore).toBe(false); + }); +}); + +// ─── logCommissionerAction ──────────────────────────────────────────────────── + +describe("logCommissionerAction", () => { + it("resolves the actor display name from the users table", async () => { + const mockUser = { username: "alice", displayName: "Alice Smith" }; + vi.mocked(findUserByClerkId).mockResolvedValue(mockUser as never); + vi.mocked(getUserDisplayName).mockReturnValue("alice"); + + const insertDb = makeInsertDb(SAMPLE_ENTRY); + vi.mocked(database).mockReturnValue(insertDb as never); + + await logCommissionerAction({ + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + action: "draft_rollback", + }); + + const insertValues = (insertDb.insert as ReturnType).mock.results[0].value.values.mock.calls[0][0]; + expect(insertValues.actorDisplayName).toBe("alice"); + }); + + it("falls back to actorClerkId if the user is not found", async () => { + vi.mocked(findUserByClerkId).mockResolvedValue(undefined); + vi.mocked(getUserDisplayName).mockReturnValue(null); + + const insertDb = makeInsertDb(SAMPLE_ENTRY); + vi.mocked(database).mockReturnValue(insertDb as never); + + await logCommissionerAction({ + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + action: "draft_rollback", + }); + + const insertValues = (insertDb.insert as ReturnType).mock.results[0].value.values.mock.calls[0][0]; + expect(insertValues.actorDisplayName).toBe(ACTOR_CLERK_ID); + }); + + it("defaults affectedTeamIds to [] when not provided", async () => { + vi.mocked(findUserByClerkId).mockResolvedValue({ username: "alice", displayName: "Alice" } as never); + vi.mocked(getUserDisplayName).mockReturnValue("alice"); + + const insertDb = makeInsertDb(SAMPLE_ENTRY); + vi.mocked(database).mockReturnValue(insertDb as never); + + await logCommissionerAction({ + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + action: "draft_started", + }); + + const insertValues = (insertDb.insert as ReturnType).mock.results[0].value.values.mock.calls[0][0]; + expect(insertValues.affectedTeamIds).toEqual([]); + }); + + it("defaults details to {} when not provided", async () => { + vi.mocked(findUserByClerkId).mockResolvedValue({ username: "alice", displayName: "Alice" } as never); + vi.mocked(getUserDisplayName).mockReturnValue("alice"); + + const insertDb = makeInsertDb(SAMPLE_ENTRY); + vi.mocked(database).mockReturnValue(insertDb as never); + + await logCommissionerAction({ + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + action: "draft_started", + }); + + const insertValues = (insertDb.insert as ReturnType).mock.results[0].value.values.mock.calls[0][0]; + expect(insertValues.details).toEqual({}); + }); + + it("passes through provided affectedTeamIds and details", async () => { + vi.mocked(findUserByClerkId).mockResolvedValue({ username: "alice", displayName: "Alice" } as never); + vi.mocked(getUserDisplayName).mockReturnValue("alice"); + + const insertDb = makeInsertDb(SAMPLE_ENTRY); + vi.mocked(database).mockReturnValue(insertDb as never); + + const details = { pickNumber: 7, teamName: "Team Bravo", participantName: "Max V" }; + await logCommissionerAction({ + seasonId: SEASON_ID, + leagueId: LEAGUE_ID, + actorClerkId: ACTOR_CLERK_ID, + action: "force_manual_pick", + affectedTeamIds: ["team-7"], + details, + }); + + const insertValues = (insertDb.insert as ReturnType).mock.results[0].value.values.mock.calls[0][0]; + expect(insertValues.affectedTeamIds).toEqual(["team-7"]); + expect(insertValues.details).toEqual(details); + }); +}); diff --git a/app/models/audit-log.ts b/app/models/audit-log.ts new file mode 100644 index 0000000..cbfbd95 --- /dev/null +++ b/app/models/audit-log.ts @@ -0,0 +1,87 @@ +import { eq, desc, and, inArray, sql } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import { findUserByClerkId, getUserDisplayName } from "~/models/user"; + +export type AuditLogEntry = typeof schema.commissionerAuditLog.$inferSelect; +export type NewAuditLogEntry = typeof schema.commissionerAuditLog.$inferInsert; +export type AuditAction = typeof schema.auditActionEnum.enumValues[number]; + +export interface AuditLogPage { + entries: AuditLogEntry[]; + total: number; + hasMore: boolean; +} + +export async function createAuditLogEntry( + data: NewAuditLogEntry +): Promise { + const db = database(); + const [entry] = await db + .insert(schema.commissionerAuditLog) + .values(data) + .returning(); + return entry; +} + +export async function getAuditLogForSeason( + seasonId: string, + options?: { limit?: number; offset?: number; actions?: AuditAction[] } +): Promise { + const db = database(); + const limit = options?.limit ?? 50; + const offset = options?.offset ?? 0; + + const whereClause = + options?.actions && options.actions.length > 0 + ? and( + eq(schema.commissionerAuditLog.seasonId, seasonId), + inArray(schema.commissionerAuditLog.action, options.actions) + ) + : eq(schema.commissionerAuditLog.seasonId, seasonId); + + const [entries, countRows] = await Promise.all([ + db + .select() + .from(schema.commissionerAuditLog) + .where(whereClause) + .orderBy(desc(schema.commissionerAuditLog.createdAt)) + .limit(limit) + .offset(offset), + db + .select({ count: sql`count(*)::int` }) + .from(schema.commissionerAuditLog) + .where(whereClause), + ]); + + const total = countRows[0]?.count ?? 0; + return { entries, total, hasMore: offset + entries.length < total }; +} + +/** + * Convenience wrapper that resolves the actor display name from the users table + * and writes a single audit log record. Call this after the main action succeeds. + */ +export async function logCommissionerAction(params: { + seasonId: string; + leagueId: string; + actorClerkId: string; + action: AuditAction; + affectedTeamIds?: string[]; + details?: Record; +}): Promise { + const user = await findUserByClerkId(params.actorClerkId); + const actorDisplayName = user + ? (getUserDisplayName(user) ?? params.actorClerkId) + : params.actorClerkId; + + await createAuditLogEntry({ + seasonId: params.seasonId, + leagueId: params.leagueId, + actorClerkId: params.actorClerkId, + actorDisplayName, + action: params.action, + affectedTeamIds: params.affectedTeamIds ?? [], + details: params.details ?? {}, + }); +} diff --git a/app/models/index.ts b/app/models/index.ts index 2c499dd..de85200 100644 --- a/app/models/index.ts +++ b/app/models/index.ts @@ -16,3 +16,4 @@ export * from "./draft-pick"; export * from "./draft-queue"; export * from "./draft-timer"; export * from "./draft-utils"; +export * from "./audit-log"; diff --git a/app/routes.ts b/app/routes.ts index 723812a..2a7f71a 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -6,6 +6,7 @@ export default [ route("leagues/new", "routes/leagues/new.tsx"), route("leagues/:leagueId", "routes/leagues/$leagueId.tsx"), route("leagues/:leagueId/settings", "routes/leagues/$leagueId.settings.tsx"), + route("leagues/:leagueId/audit-log", "routes/leagues/$leagueId.audit-log.tsx"), route( "leagues/:leagueId/upcoming-events", "routes/leagues/$leagueId.upcoming-events.tsx" diff --git a/app/routes/api/__tests__/draft.force-manual-pick.test.ts b/app/routes/api/__tests__/draft.force-manual-pick.test.ts index 502e876..c313861 100644 --- a/app/routes/api/__tests__/draft.force-manual-pick.test.ts +++ b/app/routes/api/__tests__/draft.force-manual-pick.test.ts @@ -31,6 +31,9 @@ vi.mock("~/models/draft-utils", () => ({ vi.mock("~/models/user", () => ({ isUserAdminByClerkId: vi.fn(), })); +vi.mock("~/models/audit-log", () => ({ + logCommissionerAction: vi.fn().mockResolvedValue(undefined), +})); // ── Fixtures ───────────────────────────────────────────────────────────────── diff --git a/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts b/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts index ba3aabd..b763bfc 100644 --- a/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts +++ b/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts @@ -37,6 +37,9 @@ vi.mock("~/models/draft-utils", () => ({ vi.mock("~/models/user", () => ({ isUserAdminByClerkId: vi.fn(), })); +vi.mock("~/models/audit-log", () => ({ + logCommissionerAction: vi.fn().mockResolvedValue(undefined), +})); // ── Fixtures ────────────────────────────────────────────────────────────────── diff --git a/app/routes/api/__tests__/draft.start.test.ts b/app/routes/api/__tests__/draft.start.test.ts index da9a17b..1bd8dc7 100644 --- a/app/routes/api/__tests__/draft.start.test.ts +++ b/app/routes/api/__tests__/draft.start.test.ts @@ -18,6 +18,9 @@ vi.mock("~/models/draft-timer", () => ({ deleteSeasonTimers: vi.fn(), initializeDraftTimers: vi.fn(), })); +vi.mock("~/models/audit-log", () => ({ + logCommissionerAction: vi.fn().mockResolvedValue(undefined), +})); // ── Fixtures ───────────────────────────────────────────────────────────────── diff --git a/app/routes/api/draft.adjust-time-bank.ts b/app/routes/api/draft.adjust-time-bank.ts index e634b7a..015c0f9 100644 --- a/app/routes/api/draft.adjust-time-bank.ts +++ b/app/routes/api/draft.adjust-time-bank.ts @@ -3,6 +3,7 @@ import { eq, and } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { isCommissioner } from "~/models/commissioner"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; @@ -76,6 +77,24 @@ export async function action(args: ActionFunctionArgs) { .where(eq(schema.draftTimers.id, currentTimer.id)); } + const team = await db.query.teams.findFirst({ + where: eq(schema.teams.id, teamId), + }); + + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "time_bank_edited", + affectedTeamIds: [teamId], + details: { + teamId, + teamName: team?.name ?? teamId, + adjustment, + newTimeRemaining: newTime, + }, + }); + try { getSocketIO() .to(`draft-${seasonId}`) diff --git a/app/routes/api/draft.force-autopick.ts b/app/routes/api/draft.force-autopick.ts index b26e356..fc6eef8 100644 --- a/app/routes/api/draft.force-autopick.ts +++ b/app/routes/api/draft.force-autopick.ts @@ -4,6 +4,7 @@ import * as schema from "~/database/schema"; import { eq } from "drizzle-orm"; import { executeAutoPick } from "~/models/draft-utils"; import { isCommissioner } from "~/models/commissioner"; +import { logCommissionerAction } from "~/models/audit-log"; import type { ActionFunctionArgs } from "react-router"; export async function action(args: ActionFunctionArgs) { @@ -53,6 +54,25 @@ export async function action(args: ActionFunctionArgs) { return Response.json({ error: result.error }, { status: 400 }); } + const team = await db.query.teams.findFirst({ + where: eq(schema.teams.id, teamId), + }); + + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "force_autopick", + affectedTeamIds: [teamId], + details: { + pickNumber, + teamId, + teamName: team?.name ?? teamId, + participantId: result.participant?.id, + participantName: result.participant?.name, + }, + }); + return Response.json({ success: true, pick: result.pick, diff --git a/app/routes/api/draft.force-manual-pick.ts b/app/routes/api/draft.force-manual-pick.ts index 174bd62..b46caf0 100644 --- a/app/routes/api/draft.force-manual-pick.ts +++ b/app/routes/api/draft.force-manual-pick.ts @@ -8,6 +8,7 @@ import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/d import { getParticipantsForSeasonWithSports } from "~/models/participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; import { calculatePickInfo, checkAndTriggerNextAutodraft } from "~/models/draft-utils"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; import type { ActionFunctionArgs } from "react-router"; @@ -257,6 +258,23 @@ export async function action(args: ActionFunctionArgs) { logger.error("Socket.IO error:", error); } + const pickedTeam = draftSlots.find((slot) => slot.team.id === teamId)?.team; + + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "force_manual_pick", + affectedTeamIds: [teamId], + details: { + pickNumber, + teamId, + teamName: pickedTeam?.name ?? teamId, + participantId, + participantName: participant.name, + }, + }); + // Check if next team has autodraft enabled and trigger immediately if (!isDraftComplete) { const freshSeason = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId) }); diff --git a/app/routes/api/draft.pause.ts b/app/routes/api/draft.pause.ts index e523550..548e8f0 100644 --- a/app/routes/api/draft.pause.ts +++ b/app/routes/api/draft.pause.ts @@ -3,6 +3,7 @@ import { eq } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { isCommissioner } from "~/models/commissioner"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; @@ -55,6 +56,14 @@ export async function action(args: ActionFunctionArgs) { .set({ draftPaused: true }) .where(eq(schema.seasons.id, seasonId)); + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "draft_paused", + details: { pickNumber: season.currentPickNumber }, + }); + // Emit socket event try { getSocketIO().to(`draft-${seasonId}`).emit("draft-paused", { diff --git a/app/routes/api/draft.replace-pick.ts b/app/routes/api/draft.replace-pick.ts index 86bdf0c..b1cbb0d 100644 --- a/app/routes/api/draft.replace-pick.ts +++ b/app/routes/api/draft.replace-pick.ts @@ -7,6 +7,7 @@ import { calculateDraftEligibility } from "~/lib/draft-eligibility"; import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/draft-pick"; import { getParticipantsForSeasonWithSports } from "~/models/participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; @@ -125,6 +126,11 @@ export async function action(args: ActionFunctionArgs) { return Response.json({ error: reason }, { status: 400 }); } + // Fetch old participant name for the audit log (before overwriting the pick) + const oldParticipant = await db.query.participants.findFirst({ + where: eq(schema.participants.id, oldParticipantId), + }); + // Update the pick in-place const [updatedPick] = await db .update(schema.draftPicks) @@ -141,6 +147,25 @@ export async function action(args: ActionFunctionArgs) { ) .returning(); + const replacedTeam = draftSlots.find((slot) => slot.team.id === teamId)?.team; + + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "draft_pick_changed", + affectedTeamIds: [teamId], + details: { + pickNumber, + teamId, + teamName: replacedTeam?.name ?? teamId, + oldParticipantId, + oldParticipantName: oldParticipant?.name ?? oldParticipantId, + newParticipantId: participantId, + newParticipantName: participant.name, + }, + }); + // Remove new participant from all team queues await db .delete(schema.draftQueue) diff --git a/app/routes/api/draft.resume.ts b/app/routes/api/draft.resume.ts index 18a68bd..33e55ff 100644 --- a/app/routes/api/draft.resume.ts +++ b/app/routes/api/draft.resume.ts @@ -3,6 +3,7 @@ import { eq } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { isCommissioner } from "~/models/commissioner"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; @@ -55,6 +56,14 @@ export async function action(args: ActionFunctionArgs) { .set({ draftPaused: false }) .where(eq(schema.seasons.id, seasonId)); + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "draft_resumed", + details: { pickNumber: season.currentPickNumber }, + }); + // Emit socket event try { getSocketIO().to(`draft-${seasonId}`).emit("draft-resumed", { diff --git a/app/routes/api/draft.rollback.ts b/app/routes/api/draft.rollback.ts index c7367bc..e67825b 100644 --- a/app/routes/api/draft.rollback.ts +++ b/app/routes/api/draft.rollback.ts @@ -3,6 +3,7 @@ import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq, and, gte } from "drizzle-orm"; import { isCommissioner } from "~/models/commissioner"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; @@ -83,6 +84,18 @@ export async function action(args: ActionFunctionArgs) { }) .where(eq(schema.seasons.id, seasonId)); + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "draft_rollback", + affectedTeamIds: rollbackSlot?.teamId ? [rollbackSlot.teamId] : [], + details: { + rolledBackToPickNumber: pickNumber, + previousPickNumber: season.currentPickNumber, + }, + }); + // Emit socket events try { const io = getSocketIO(); diff --git a/app/routes/api/draft.start.ts b/app/routes/api/draft.start.ts index d095447..bafb659 100644 --- a/app/routes/api/draft.start.ts +++ b/app/routes/api/draft.start.ts @@ -4,6 +4,7 @@ import * as schema from "~/database/schema"; import { eq } from "drizzle-orm"; import { deleteSeasonTimers, initializeDraftTimers } from "~/models/draft-timer"; import { isCommissioner } from "~/models/commissioner"; +import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; import { logger } from "~/lib/logger"; @@ -77,6 +78,14 @@ export async function action(args: ActionFunctionArgs) { initialTime ); + await logCommissionerAction({ + seasonId, + leagueId: season.leagueId, + actorClerkId: userId, + action: "draft_started", + details: { pickNumber: 1 }, + }); + // Emit socket event try { getSocketIO().to(`draft-${seasonId}`).emit("draft-started", { diff --git a/app/routes/leagues/$leagueId.audit-log.tsx b/app/routes/leagues/$leagueId.audit-log.tsx new file mode 100644 index 0000000..f5f3daf --- /dev/null +++ b/app/routes/leagues/$leagueId.audit-log.tsx @@ -0,0 +1,308 @@ +import { getAuth } from "@clerk/react-router/server"; +import { format } from "date-fns"; +import { Link, Form, useSearchParams } from "react-router"; +import type { Route } from "./+types/$leagueId.audit-log"; +import { Button } from "~/components/ui/button"; +import { Badge } from "~/components/ui/badge"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "~/components/ui/table"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "~/components/ui/select"; +import { findLeagueById, isCommissioner, isUserLeagueMember } from "~/models"; +import { findCurrentSeasonWithSports } from "~/models/season"; +import { findTeamsBySeasonId } from "~/models/team"; +import { getAuditLogForSeason, type AuditAction, type AuditLogEntry } from "~/models/audit-log"; +import { AUDIT_ACTION_LABELS, formatAuditDetail } from "~/lib/audit-log-display"; +import * as schema from "~/database/schema"; + +const VALID_AUDIT_ACTIONS = new Set(schema.auditActionEnum.enumValues); + +export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { + return [{ title: `Audit Log — ${data?.league?.name ?? "League"} - Brackt` }]; +} + +const PAGE_SIZE = 50; + +export async function loader(args: Route.LoaderArgs) { + const { userId } = await getAuth(args); + const { params, request } = args; + const { leagueId } = params; + + if (!userId) { + throw new Response("You must be logged in to view this page", { + status: 401, + }); + } + + const league = await findLeagueById(leagueId); + if (!league) { + throw new Response("League not found", { status: 404 }); + } + + const [userIsCommissioner, userIsMember] = await Promise.all([ + isCommissioner(leagueId, userId), + isUserLeagueMember(leagueId, userId), + ]); + + if (!userIsCommissioner && !userIsMember) { + throw new Response("You do not have access to this league", { + status: 403, + }); + } + + const season = await findCurrentSeasonWithSports(leagueId); + + if (!season) { + return { + league, + season: null, + auditLog: { entries: [] as AuditLogEntry[], total: 0, hasMore: false }, + teams: [] as Array<{ id: string; name: string }>, + page: 0, + isUserCommissioner: userIsCommissioner, + }; + } + + const searchParams = new URL(request.url).searchParams; + const page = Math.max(0, parseInt(searchParams.get("page") ?? "0", 10) || 0); + const rawAction = searchParams.get("action"); + const actionFilter: AuditAction | null = + rawAction && VALID_AUDIT_ACTIONS.has(rawAction) ? (rawAction as AuditAction) : null; + + const [auditLog, teams] = await Promise.all([ + getAuditLogForSeason(season.id, { + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + actions: actionFilter ? [actionFilter] : undefined, + }), + findTeamsBySeasonId(season.id), + ]); + + return { + league, + season, + auditLog, + teams: teams.map((t) => ({ id: t.id, name: t.name })), + page, + isUserCommissioner: userIsCommissioner, + }; +} + +function getActionBadgeVariant( + action: string +): "default" | "secondary" | "destructive" | "outline" { + if (action === "draft_reset" || action === "draft_rollback") { + return "destructive"; + } + if ( + action === "draft_started" || + action === "draft_paused" || + action === "draft_resumed" + ) { + return "secondary"; + } + if ( + action === "league_settings_changed" || + action === "draft_settings_changed" || + action === "scoring_rules_changed" + ) { + return "outline"; + } + return "default"; +} + +interface AuditLogRowProps { + entry: AuditLogEntry; + teamMap: Map; +} + +function AuditLogRow({ entry, teamMap }: AuditLogRowProps) { + const affectedTeamNames = + entry.affectedTeamIds && entry.affectedTeamIds.length > 0 + ? entry.affectedTeamIds + .map((id) => teamMap.get(id) ?? id) + .join(", ") + : "—"; + + return ( + + + {format(new Date(entry.createdAt), "MMM d, yyyy HH:mm")} + + + {entry.actorDisplayName ?? entry.actorClerkId} + + + + {AUDIT_ACTION_LABELS[entry.action] ?? entry.action} + + + + {affectedTeamNames} + + + {formatAuditDetail(entry)} + + + ); +} + +export default function AuditLogPage({ loaderData }: Route.ComponentProps) { + const { league, season, auditLog, teams, page, isUserCommissioner } = + loaderData; + const [searchParams] = useSearchParams(); + const actionFilter = searchParams.get("action") ?? ""; + + const teamMap = new Map(teams.map((t) => [t.id, t.name])); + const totalPages = Math.ceil(auditLog.total / PAGE_SIZE); + + function buildPageUrl(newPage: number) { + const p = new URLSearchParams(searchParams); + p.set("page", String(newPage)); + return `?${p.toString()}`; + } + + return ( +
+ {/* Header */} +
+
+

Audit Log

+

{league.name}

+
+
+ {isUserCommissioner && ( + + )} + +
+
+ + {!season ? ( + + + No season found for this league. + + + ) : ( + <> + {/* Filter bar */} + + +
+ + + {actionFilter && ( + + )} +
+
+
+ + {/* Results table */} + + + Commissioner Actions + + {auditLog.total === 0 + ? "No entries found" + : `${auditLog.total} ${auditLog.total === 1 ? "entry" : "entries"}`} + {totalPages > 1 && + ` — page ${page + 1} of ${totalPages}`} + + + + {auditLog.entries.length === 0 ? ( +

+ No activity recorded yet. +

+ ) : ( + + + + Time + Commissioner + Action + Teams Affected + Details + + + + {auditLog.entries.map((entry) => ( + + ))} + +
+ )} + + {/* Pagination */} + {totalPages > 1 && ( +
+ {page > 0 && ( + + )} + + Page {page + 1} of {totalPages} + + {auditLog.hasMore && ( + + )} +
+ )} +
+
+ + )} +
+ ); +} diff --git a/app/routes/leagues/$leagueId.server.ts b/app/routes/leagues/$leagueId.server.ts index dfe33a0..338fabf 100644 --- a/app/routes/leagues/$leagueId.server.ts +++ b/app/routes/leagues/$leagueId.server.ts @@ -16,6 +16,7 @@ import { getSeasonStandings } from "~/models/standings"; import { getUpcomingEventsForDraftedParticipants } from "~/models/scoring-event"; import { getUpcomingGroupStageMatchesForParticipants } from "~/models/group-stage-match"; import { getDraftedParticipantsBySportsSeason } from "~/models/draft-pick"; +import { getAuditLogForSeason } from "~/models/audit-log"; import type { Route } from "./+types/$leagueId"; export async function loader(args: Route.LoaderArgs) { @@ -188,6 +189,11 @@ export async function loader(args: Route.LoaderArgs) { // Extract origin for client use (avoids SSR/client mismatch on invite URLs) const origin = new URL(args.request.url).origin; + // Fetch recent audit log entries for the "Recent Activity" summary widget + const recentActivity = season + ? await getAuditLogForSeason(season.id, { limit: 5 }) + : { entries: [], total: 0, hasMore: false }; + return { league, season, @@ -206,5 +212,6 @@ export async function loader(args: Route.LoaderArgs) { standings, origin, upcomingCalendarEvents, + recentActivity, }; } diff --git a/app/routes/leagues/$leagueId.settings.tsx b/app/routes/leagues/$leagueId.settings.tsx index 25850ba..685416c 100644 --- a/app/routes/leagues/$leagueId.settings.tsx +++ b/app/routes/leagues/$leagueId.settings.tsx @@ -25,6 +25,7 @@ import * as schema from "~/database/schema"; import { deleteAllDraftPicks } from "~/models/draft-pick"; import { clearAllQueuesForSeason } from "~/models/draft-queue"; import { deleteSeasonTimers } from "~/models/draft-timer"; +import { logCommissionerAction } from "~/models/audit-log"; import { parseDraftSpeed } from "~/lib/draft-timer"; import type { Route } from "./+types/$leagueId.settings"; import { Button } from "~/components/ui/button"; @@ -218,11 +219,41 @@ export async function action(args: Route.ActionArgs) { } try { + const changedFields: string[] = []; + if (name.trim() !== league.name) changedFields.push("name"); + if (isPublicDraftBoard !== league.isPublicDraftBoard) changedFields.push("isPublicDraftBoard"); + if ((webhookUrl || null) !== league.discordWebhookUrl) changedFields.push("discordWebhookUrl"); + await updateLeague(leagueId, { name: name.trim(), isPublicDraftBoard, discordWebhookUrl: webhookUrl || null, }); + + if (changedFields.length > 0) { + const currentSeason = await findCurrentSeasonWithSports(leagueId); + if (currentSeason) { + await logCommissionerAction({ + seasonId: currentSeason.id, + leagueId, + actorClerkId: userId, + action: "league_settings_changed", + details: { + changedFields, + previousValues: { + name: league.name, + isPublicDraftBoard: league.isPublicDraftBoard, + discordWebhookUrl: league.discordWebhookUrl, + }, + newValues: { + name: name.trim(), + isPublicDraftBoard, + discordWebhookUrl: webhookUrl || null, + }, + }, + }); + } + } } catch (error) { logger.error("Error updating league:", error); return { error: "Failed to update league. Please try again." }; @@ -334,6 +365,22 @@ export async function action(args: Route.ActionArgs) { } await setDraftOrder(season.id, teamIds); + + await logCommissionerAction({ + seasonId: season.id, + leagueId, + actorClerkId: userId, + action: "draft_order_set", + affectedTeamIds: teamIds, + details: { + order: teamIds.map((id, i) => ({ + teamId: id, + teamName: teams.find((t) => t.id === id)?.name ?? id, + position: i + 1, + })), + }, + }); + return { success: true, message: "Draft order updated successfully" }; } @@ -346,6 +393,26 @@ export async function action(args: Route.ActionArgs) { const teamIds = teams.map(t => t.id); await randomizeDraftOrder(season.id, teamIds); + + // Fetch the new order that was just written so we can log it accurately + const newSlots = await findDraftSlotsBySeasonId(season.id); + const sortedSlots = newSlots.toSorted((a, b) => a.draftOrder - b.draftOrder); + + await logCommissionerAction({ + seasonId: season.id, + leagueId, + actorClerkId: userId, + action: "draft_order_randomized", + affectedTeamIds: sortedSlots.map((s) => s.teamId), + details: { + order: sortedSlots.map((s) => ({ + teamId: s.teamId, + teamName: teams.find((t) => t.id === s.teamId)?.name ?? s.teamId, + position: s.draftOrder, + })), + }, + }); + return { success: true, message: "Draft order randomized successfully" }; } @@ -427,6 +494,8 @@ export async function action(args: Route.ActionArgs) { // Delete all draft timers await deleteSeasonTimers(season.id); + const previousPickNumber = season.currentPickNumber; + // Set season status back to pre_draft and reset draft state (keeps draft order intact) await updateSeason(season.id, { status: "pre_draft", @@ -435,6 +504,14 @@ export async function action(args: Route.ActionArgs) { draftPaused: false, }); + await logCommissionerAction({ + seasonId: season.id, + leagueId, + actorClerkId: userId, + action: "draft_reset", + details: { previousPickNumber }, + }); + return { success: true, message: "Draft has been reset successfully. Draft order preserved." }; } catch (error) { logger.error("Error resetting draft:", error); @@ -525,6 +602,37 @@ export async function action(args: Route.ActionArgs) { // Update season if there are changes if (Object.keys(seasonUpdates).length > 0) { await updateSeason(season.id, seasonUpdates); + + const scoringFields = ["pointsFor1st", "pointsFor2nd", "pointsFor3rd", "pointsFor4th", + "pointsFor5th", "pointsFor6th", "pointsFor7th", "pointsFor8th"]; + const draftFields = Object.keys(seasonUpdates).filter((k) => !scoringFields.includes(k)); + const scoringChangedFields = Object.keys(seasonUpdates).filter((k) => scoringFields.includes(k)); + + if (draftFields.length > 0) { + await logCommissionerAction({ + seasonId: season.id, + leagueId, + actorClerkId: userId, + action: "draft_settings_changed", + details: { + changedFields: draftFields, + newValues: Object.fromEntries(draftFields.map((k) => [k, (seasonUpdates as Record)[k]])), + }, + }); + } + + if (scoringChangedFields.length > 0) { + await logCommissionerAction({ + seasonId: season.id, + leagueId, + actorClerkId: userId, + action: "scoring_rules_changed", + details: { + changedFields: scoringChangedFields, + newValues: Object.fromEntries(scoringChangedFields.map((k) => [k, (seasonUpdates as Record)[k]])), + }, + }); + } } // Handle team count changes if provided @@ -1443,6 +1551,23 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone + {/* Audit Log */} + + + Audit Log + + View the full history of commissioner actions for this season. All league members can see this log. + + + + + + + {/* Danger Zone */} diff --git a/app/routes/leagues/$leagueId.tsx b/app/routes/leagues/$leagueId.tsx index 17f3e13..da96fd8 100644 --- a/app/routes/leagues/$leagueId.tsx +++ b/app/routes/leagues/$leagueId.tsx @@ -16,6 +16,7 @@ import { SportSeasonCard } from "~/components/sports/SportSeasonCard"; import { UpcomingCalendarPanel } from "~/components/sport-season/UpcomingCalendarPanel"; import { TeamNameDisplay } from "~/components/ui/team-name-display"; import { buildTiedRankChecker, getDisplayRank } from "~/lib/standings-display"; +import { formatAuditDetail } from "~/lib/audit-log-display"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `${data?.league?.name ?? "League"} - Brackt` }]; @@ -42,6 +43,7 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) { standings, origin, upcomingCalendarEvents, + recentActivity, } = loaderData; const myTeam = teams.find((t) => t.ownerId === currentUserId); @@ -533,6 +535,36 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) { + + {recentActivity.entries.length > 0 && ( + + +
+ Recent Activity + Recent commissioner actions +
+ +
+ +
    + {recentActivity.entries.map((entry) => ( +
  • + + {format(new Date(entry.createdAt), "MMM d, HH:mm")} + + + {entry.actorDisplayName ?? entry.actorClerkId} + {" — "} + {formatAuditDetail(entry)} + +
  • + ))} +
+
+
+ )} diff --git a/database/schema.ts b/database/schema.ts index 2220dbe..b88e261 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, jsonb } from "drizzle-orm/pg-core"; +import { integer, pgTable, varchar, uuid, timestamp, pgEnum, boolean, text, date, decimal, uniqueIndex, index, jsonb } from "drizzle-orm/pg-core"; import { relations } from "drizzle-orm"; // Users table - synced from Clerk @@ -1243,3 +1243,54 @@ export const cs2MajorStageResultsRelations = relations(cs2MajorStageResults, ({ references: [participants.id], }), })); + +// ─── Commissioner Audit Log ──────────────────────────────────────────────────── +// Immutable log of significant commissioner/admin actions per season. +// Readable by all league members for transparency; writable only by the +// server-side route actions that perform the underlying operations. + +export const auditActionEnum = pgEnum("audit_action", [ + "league_settings_changed", + "draft_settings_changed", + "scoring_rules_changed", + "draft_order_set", + "draft_order_randomized", + "draft_started", + "draft_paused", + "draft_resumed", + "draft_reset", + "draft_rollback", + "force_autopick", + "force_manual_pick", + "draft_pick_changed", + "time_bank_edited", +]); + +export const commissionerAuditLog = pgTable("commissioner_audit_log", { + id: uuid("id").primaryKey().defaultRandom(), + seasonId: uuid("season_id") + .notNull() + .references(() => seasons.id, { onDelete: "cascade" }), + leagueId: uuid("league_id") + .notNull() + .references(() => leagues.id, { onDelete: "cascade" }), + actorClerkId: varchar("actor_clerk_id", { length: 255 }).notNull(), + actorDisplayName: varchar("actor_display_name", { length: 255 }), + action: auditActionEnum("action").notNull(), + affectedTeamIds: text("affected_team_ids").array(), + details: jsonb("details"), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (t) => ({ + auditLogSeasonIdx: index("audit_log_season_id_idx").on(t.seasonId, t.createdAt), +})); + +export const commissionerAuditLogRelations = relations(commissionerAuditLog, ({ one }) => ({ + season: one(seasons, { + fields: [commissionerAuditLog.seasonId], + references: [seasons.id], + }), + league: one(leagues, { + fields: [commissionerAuditLog.leagueId], + references: [leagues.id], + }), +})); diff --git a/drizzle/0075_chubby_stephen_strange.sql b/drizzle/0075_chubby_stephen_strange.sql new file mode 100644 index 0000000..27922c0 --- /dev/null +++ b/drizzle/0075_chubby_stephen_strange.sql @@ -0,0 +1,26 @@ +CREATE TYPE "public"."audit_action" AS ENUM('league_settings_changed', 'draft_settings_changed', 'scoring_rules_changed', 'draft_order_set', 'draft_order_randomized', 'draft_started', 'draft_paused', 'draft_resumed', 'draft_reset', 'draft_rollback', 'force_autopick', 'force_manual_pick', 'draft_pick_changed', 'time_bank_edited');--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "commissioner_audit_log" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "season_id" uuid NOT NULL, + "league_id" uuid NOT NULL, + "actor_clerk_id" varchar(255) NOT NULL, + "actor_display_name" varchar(255), + "action" "audit_action" NOT NULL, + "affected_team_ids" text[], + "details" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "commissioner_audit_log" ADD CONSTRAINT "commissioner_audit_log_season_id_seasons_id_fk" FOREIGN KEY ("season_id") REFERENCES "public"."seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "commissioner_audit_log" ADD CONSTRAINT "commissioner_audit_log_league_id_leagues_id_fk" FOREIGN KEY ("league_id") REFERENCES "public"."leagues"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "audit_log_season_id_idx" ON "commissioner_audit_log" USING btree ("season_id","created_at"); \ No newline at end of file diff --git a/drizzle/meta/0075_snapshot.json b/drizzle/meta/0075_snapshot.json new file mode 100644 index 0000000..85f4843 --- /dev/null +++ b/drizzle/meta/0075_snapshot.json @@ -0,0 +1,4619 @@ +{ + "id": "3e3b0229-b1c1-459c-8eba-a6cf2a649b3b", + "prevId": "8b61b5f1-0f6e-40a9-bd4c-bafcb59c6bc3", + "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.commissioner_audit_log": { + "name": "commissioner_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_clerk_id": { + "name": "actor_clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "actor_display_name": { + "name": "actor_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "audit_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "affected_team_ids": { + "name": "affected_team_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_log_season_id_idx": { + "name": "audit_log_season_id_idx", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commissioner_audit_log_season_id_seasons_id_fk": { + "name": "commissioner_audit_log_season_id_seasons_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commissioner_audit_log_league_id_leagues_id_fk": { + "name": "commissioner_audit_log_league_id_leagues_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "commissioners_league_id_leagues_id_fk": { + "name": "commissioners_league_id_leagues_id_fk", + "tableFrom": "commissioners", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cs2_major_stage_results": { + "name": "cs2_major_stage_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "stage_entry": { + "name": "stage_entry", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stage_eliminated": { + "name": "stage_eliminated", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "stage_eliminated_wins": { + "name": "stage_eliminated_wins", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "final_placement": { + "name": "final_placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cs2_major_stage_results_unique": { + "name": "cs2_major_stage_results_unique", + "columns": [ + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk": { + "name": "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cs2_major_stage_results_participant_id_participants_id_fk": { + "name": "cs2_major_stage_results_participant_id_participants_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_picks": { + "name": "draft_picks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pick_number": { + "name": "pick_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pick_in_round": { + "name": "pick_in_round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "picked_by_user_id": { + "name": "picked_by_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "picked_by_type": { + "name": "picked_by_type", + "type": "picked_by_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "draft_picks_season_pick_unique": { + "name": "draft_picks_season_pick_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pick_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "draft_picks_season_id_seasons_id_fk": { + "name": "draft_picks_season_id_seasons_id_fk", + "tableFrom": "draft_picks", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_team_id_teams_id_fk": { + "name": "draft_picks_team_id_teams_id_fk", + "tableFrom": "draft_picks", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_participant_id_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.group_stage_matches": { + "name": "group_stage_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_score": { + "name": "participant1_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "matchday": { + "name": "matchday", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "group_stage_matches_group_pair_unique": { + "name": "group_stage_matches_group_pair_unique", + "columns": [ + { + "expression": "tournament_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant1_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant2_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "group_stage_matches_tournament_group_id_tournament_groups_id_fk": { + "name": "group_stage_matches_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "group_stage_matches_participant1_id_participants_id_fk": { + "name": "group_stage_matches_participant1_id_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "group_stage_matches_participant2_id_participants_id_fk": { + "name": "group_stage_matches_participant2_id_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leagues": { + "name": "leagues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "current_season_id": { + "name": "current_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_public_draft_board": { + "name": "is_public_draft_board", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "discord_webhook_url": { + "name": "discord_webhook_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_ev_snapshots": { + "name": "participant_ev_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "calculated_ev": { + "name": "calculated_ev", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_snapshots_unique": { + "name": "participant_ev_snapshots_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_ev_snapshots_participant_id_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 + }, + "source_elo": { + "name": "source_elo", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "world_ranking": { + "name": "world_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_participant_season_unique": { + "name": "participant_ev_participant_season_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "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_golf_skills": { + "name": "participant_golf_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sg_total": { + "name": "sg_total", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "datagolf_rank": { + "name": "datagolf_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "masters_odds": { + "name": "masters_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "us_open_odds": { + "name": "us_open_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "open_championship_odds": { + "name": "open_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pga_championship_odds": { + "name": "pga_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_golf_skills_unique": { + "name": "participant_golf_skills_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_golf_skills_participant_id_participants_id_fk": { + "name": "participant_golf_skills_participant_id_participants_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_golf_skills_sports_season_id_sports_seasons_id_fk": { + "name": "participant_golf_skills_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_golf_skills", + "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.participant_surface_elos": { + "name": "participant_surface_elos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "world_ranking": { + "name": "world_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_hard": { + "name": "elo_hard", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_clay": { + "name": "elo_clay", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_grass": { + "name": "elo_grass", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_surface_elos_unique": { + "name": "participant_surface_elos_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_surface_elos_participant_id_participants_id_fk": { + "name": "participant_surface_elos_participant_id_participants_id_fk", + "tableFrom": "participant_surface_elos", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_surface_elos_sports_season_id_sports_seasons_id_fk": { + "name": "participant_surface_elos_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_surface_elos", + "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'" + }, + "vorp_value": { + "name": "vorp_value", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participants_sports_season_name_unique": { + "name": "participants_sports_season_name_unique", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "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.pending_standings_mappings": { + "name": "pending_standings_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "standing_data": { + "name": "standing_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "psm_season_external_id_idx": { + "name": "psm_season_external_id_idx", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_standings_mappings_sports_season_id_sports_seasons_id_fk": { + "name": "pending_standings_mappings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "pending_standings_mappings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_games": { + "name": "playoff_match_games", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_number": { + "name": "game_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "playoff_match_game_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_match_games_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_games_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_games_winner_id_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.regular_season_standings": { + "name": "regular_season_standings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "wins": { + "name": "wins", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "losses": { + "name": "losses", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ot_losses": { + "name": "ot_losses", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ties": { + "name": "ties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "win_pct": { + "name": "win_pct", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false + }, + "games_played": { + "name": "games_played", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "games_back": { + "name": "games_back", + "type": "numeric(5, 1)", + "primaryKey": false, + "notNull": false + }, + "conference": { + "name": "conference", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "division": { + "name": "division", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "conference_rank": { + "name": "conference_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "division_rank": { + "name": "division_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "league_rank": { + "name": "league_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "streak": { + "name": "streak", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_ten": { + "name": "last_ten", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "home_record": { + "name": "home_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "away_record": { + "name": "away_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "srs": { + "name": "srs", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rss_participant_season_idx": { + "name": "rss_participant_season_idx", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "regular_season_standings_participant_id_participants_id_fk": { + "name": "regular_season_standings_participant_id_participants_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "regular_season_standings_sports_season_id_sports_seasons_id_fk": { + "name": "regular_season_standings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scoring_events": { + "name": "scoring_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "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 + }, + "draft_timer_mode": { + "name": "draft_timer_mode", + "type": "draft_timer_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'chess_clock'" + }, + "current_pick_number": { + "name": "current_pick_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "draft_started_at": { + "name": "draft_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_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'" + }, + "draft_on": { + "name": "draft_on", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "draft_off": { + "name": "draft_off", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sports_seasons_sport_id_sports_id_fk": { + "name": "sports_seasons_sport_id_sports_id_fk", + "tableFrom": "sports_seasons", + "tableTo": "sports", + "columnsFrom": [ + "sport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "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": { + "team_standings_snapshots_unique": { + "name": "team_standings_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_standings_snapshots_team_id_teams_id_fk": { + "name": "team_standings_snapshots_team_id_teams_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_snapshots_season_id_seasons_id_fk": { + "name": "team_standings_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "logo_url": { + "name": "logo_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_season_id_seasons_id_fk": { + "name": "teams_season_id_seasons_id_fk", + "tableFrom": "teams", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_group_members": { + "name": "tournament_group_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "eliminated": { + "name": "eliminated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "tournament_group_members_tournament_group_id_tournament_groups_id_fk": { + "name": "tournament_group_members_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tournament_group_members_participant_id_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.audit_action": { + "name": "audit_action", + "schema": "public", + "values": [ + "league_settings_changed", + "draft_settings_changed", + "scoring_rules_changed", + "draft_order_set", + "draft_order_randomized", + "draft_started", + "draft_paused", + "draft_resumed", + "draft_reset", + "draft_rollback", + "force_autopick", + "force_manual_pick", + "draft_pick_changed", + "time_bank_edited" + ] + }, + "public.autodraft_mode": { + "name": "autodraft_mode", + "schema": "public", + "values": [ + "next_pick", + "while_on" + ] + }, + "public.draft_timer_mode": { + "name": "draft_timer_mode", + "schema": "public", + "values": [ + "chess_clock", + "standard" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "playoff_game", + "major_tournament", + "final_standings", + "schedule_event" + ] + }, + "public.picked_by_type": { + "name": "picked_by_type", + "schema": "public", + "values": [ + "owner", + "commissioner", + "admin", + "auto" + ] + }, + "public.playoff_match_game_status": { + "name": "playoff_match_game_status", + "schema": "public", + "values": [ + "scheduled", + "complete", + "postponed" + ] + }, + "public.probability_source": { + "name": "probability_source", + "schema": "public", + "values": [ + "manual", + "futures_odds", + "elo_simulation", + "performance_model" + ] + }, + "public.scoring_pattern": { + "name": "scoring_pattern", + "schema": "public", + "values": [ + "playoff_bracket", + "season_standings", + "qualifying_points" + ] + }, + "public.scoring_type": { + "name": "scoring_type", + "schema": "public", + "values": [ + "playoffs", + "regular_season", + "majors" + ] + }, + "public.season_status": { + "name": "season_status", + "schema": "public", + "values": [ + "pre_draft", + "draft", + "active", + "completed" + ] + }, + "public.simulation_status": { + "name": "simulation_status", + "schema": "public", + "values": [ + "idle", + "running", + "failed" + ] + }, + "public.simulator_type": { + "name": "simulator_type", + "schema": "public", + "values": [ + "f1_standings", + "indycar_standings", + "golf_qualifying_points", + "playoff_bracket", + "ucl_bracket", + "ncaam_bracket", + "ncaaw_bracket", + "nba_bracket", + "nhl_bracket", + "nfl_bracket", + "afl_bracket", + "snooker_bracket", + "tennis_qualifying_points", + "mlb_bracket", + "wnba_bracket", + "world_cup", + "darts_bracket", + "cs2_major_qualifying_points", + "ncaa_football_bracket", + "llws_bracket" + ] + }, + "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 6cbfd74..4e430a1 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -526,6 +526,13 @@ "when": 1775699707816, "tag": "0074_clammy_tiger_shark", "breakpoints": true + }, + { + "idx": 75, + "version": "7", + "when": 1776101579459, + "tag": "0075_chubby_stephen_strange", + "breakpoints": true } ] } \ No newline at end of file