brackt/app/models/sports-season-tournament.ts
Chris Parsons 0de8cc0b95 Add sports season ↔ tournament linking with admin UI
Adds a sports_season_tournaments junction table and bidirectional
link/unlink UI on both the sports season and tournament admin pages.

- New junction table with unique index on (sports_season_id, tournament_id)
  and a separate index on tournament_id for reverse lookups
- New model with link/unlink/query functions and cross-sport validation
- Migration includes backfill from existing scoring_events tournament links
- Admin sports season page: Tournaments card with add/remove UI
- Admin tournament page: Linked Sports Seasons card with add/remove UI
- Inline success/error feedback, empty states, aria-labels on remove buttons
- cloneSportsSeason now copies tournament links
- Fixes darts simulator test timeout (15s for 128-player pre-bracket sim)
2026-05-02 12:41:22 -07:00

74 lines
2.2 KiB
TypeScript

import { eq, and } from "drizzle-orm";
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { getTournamentById } from "./tournament";
import { findSportsSeasonById } from "./sports-season";
export type SportsSeasonTournament = typeof schema.sportsSeasonTournaments.$inferSelect;
export type NewSportsSeasonTournament = typeof schema.sportsSeasonTournaments.$inferInsert;
export async function linkTournamentToSportsSeason(
sportsSeasonId: string,
tournamentId: string
): Promise<SportsSeasonTournament> {
const [sportsSeason, tournament] = await Promise.all([
findSportsSeasonById(sportsSeasonId),
getTournamentById(tournamentId),
]);
if (!sportsSeason) {
throw new Error(`Sports season ${sportsSeasonId} not found`);
}
if (!tournament) {
throw new Error(`Tournament ${tournamentId} not found`);
}
if (sportsSeason.sportId !== tournament.sportId) {
throw new Error("Tournament and sports season must belong to the same sport");
}
const db = database();
const [link] = await db
.insert(schema.sportsSeasonTournaments)
.values({ sportsSeasonId, tournamentId })
.returning();
return link;
}
export async function unlinkTournamentFromSportsSeason(
sportsSeasonId: string,
tournamentId: string
): Promise<void> {
const db = database();
await db
.delete(schema.sportsSeasonTournaments)
.where(
and(
eq(schema.sportsSeasonTournaments.sportsSeasonId, sportsSeasonId),
eq(schema.sportsSeasonTournaments.tournamentId, tournamentId)
)
);
}
export async function findTournamentsBySportsSeason(sportsSeasonId: string) {
const db = database();
return await db.query.sportsSeasonTournaments.findMany({
where: eq(schema.sportsSeasonTournaments.sportsSeasonId, sportsSeasonId),
with: {
tournament: true,
},
});
}
export async function findSportsSeasonsByTournament(tournamentId: string) {
const db = database();
return await db.query.sportsSeasonTournaments.findMany({
where: eq(schema.sportsSeasonTournaments.tournamentId, tournamentId),
with: {
sportsSeason: {
with: {
sport: true,
},
},
},
});
}