diff --git a/app/features/tournament-bracket/core/tests/mocks-li.ts b/app/features/tournament-bracket/core/tests/mocks-li.ts index f30984f93..6d06c76bb 100644 --- a/app/features/tournament-bracket/core/tests/mocks-li.ts +++ b/app/features/tournament-bracket/core/tests/mocks-li.ts @@ -6834,6 +6834,7 @@ export const LOW_INK_DECEMBER_2024 = (): TournamentData => ({ id: 815, eventId: 2614, parentTournamentId: null, + parentTournamentName: null, tier: null, tentativeTier: null, discordUrl: "https://discord.gg/F7RaNUR", diff --git a/app/features/tournament-bracket/core/tests/mocks-sos.ts b/app/features/tournament-bracket/core/tests/mocks-sos.ts index 978af01bd..c3ba573b9 100644 --- a/app/features/tournament-bracket/core/tests/mocks-sos.ts +++ b/app/features/tournament-bracket/core/tests/mocks-sos.ts @@ -1934,6 +1934,7 @@ export const SWIM_OR_SINK_167 = ( ctx: { id: 672, parentTournamentId: null, + parentTournamentName: null, tier: null, tentativeTier: null, eventId: 2425, diff --git a/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts b/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts index 928485c0c..d849e7444 100644 --- a/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts +++ b/app/features/tournament-bracket/core/tests/mocks-zones-weekly.ts @@ -323,6 +323,7 @@ export const ZONES_WEEKLY_38 = (): TournamentData => ({ mapPickingStyle: "TO", hasRules: true, parentTournamentId: null, + parentTournamentName: null, name: "Zones Weekly 38", startTime: 1734685200, isFinalized: 0, diff --git a/app/features/tournament-bracket/core/tests/mocks.ts b/app/features/tournament-bracket/core/tests/mocks.ts index 22eaa6cb7..4708ce4a8 100644 --- a/app/features/tournament-bracket/core/tests/mocks.ts +++ b/app/features/tournament-bracket/core/tests/mocks.ts @@ -1399,6 +1399,7 @@ export const PADDLING_POOL_257 = () => tier: null, tentativeTier: null, parentTournamentId: null, + parentTournamentName: null, tags: null, eventId: 1352, bracketProgressionOverrides: [], @@ -8068,6 +8069,7 @@ export const PADDLING_POOL_255 = () => tier: null, tentativeTier: null, parentTournamentId: null, + parentTournamentName: null, tags: null, eventId: 1286, bracketProgressionOverrides: [], @@ -15074,6 +15076,7 @@ export const IN_THE_ZONE_32 = ({ ctx: { id: 11, parentTournamentId: null, + parentTournamentName: null, organization: null, tier: null, tentativeTier: null, diff --git a/app/features/tournament-bracket/core/tests/test-utils.ts b/app/features/tournament-bracket/core/tests/test-utils.ts index 54316c09d..e39bc6d27 100644 --- a/app/features/tournament-bracket/core/tests/test-utils.ts +++ b/app/features/tournament-bracket/core/tests/test-utils.ts @@ -67,6 +67,7 @@ export const testTournament = ({ tier: null, tentativeTier: null, parentTournamentId: null, + parentTournamentName: null, hasRules: false, logoUrl: "/test.avif", discordUrl: null, diff --git a/app/features/tournament/TournamentRepository.server.ts b/app/features/tournament/TournamentRepository.server.ts index 32be3cee6..117470bcd 100644 --- a/app/features/tournament/TournamentRepository.server.ts +++ b/app/features/tournament/TournamentRepository.server.ts @@ -58,6 +58,15 @@ export async function findById(id: number) { "Tournament.mapPickingStyle", sql`"Tournament"."rules" is not null`.as("hasRules"), "Tournament.parentTournamentId", + eb + .selectFrom("CalendarEvent as ParentCalendarEvent") + .select("ParentCalendarEvent.name") + .whereRef( + "ParentCalendarEvent.tournamentId", + "=", + "Tournament.parentTournamentId", + ) + .as("parentTournamentName"), "Tournament.tier", "CalendarEvent.name", "CalendarEventDate.startTime", diff --git a/app/features/tournament/components/TournamentHeader.tsx b/app/features/tournament/components/TournamentHeader.tsx index 266ce637c..d9d5ba288 100644 --- a/app/features/tournament/components/TournamentHeader.tsx +++ b/app/features/tournament/components/TournamentHeader.tsx @@ -16,14 +16,11 @@ import { tournamentPage, userPage, } from "~/utils/urls"; -import { splitTournamentName } from "../tournament-utils"; +import { tournamentNameParts } from "../tournament-utils"; import styles from "./TournamentHeader.module.css"; export function TournamentHeader({ tournament }: { tournament: Tournament }) { - const { name, subtext } = splitTournamentName( - tournament.ctx.name, - tournament.ctx.organization?.series ?? [], - ); + const { name, subtext } = tournamentNameParts(tournament); const startTimes = R.uniqueBy( [ diff --git a/app/features/tournament/components/TournamentNav.tsx b/app/features/tournament/components/TournamentNav.tsx index 70f260a33..8989f5455 100644 --- a/app/features/tournament/components/TournamentNav.tsx +++ b/app/features/tournament/components/TournamentNav.tsx @@ -26,7 +26,7 @@ import { tournamentInfoPage, tournamentRulesPage, } from "~/utils/urls"; -import { splitTournamentName } from "../tournament-utils"; +import { tournamentNameParts } from "../tournament-utils"; import styles from "./TournamentNav.module.css"; type NavItemKey = @@ -77,10 +77,7 @@ export function TournamentNav({ const overflowItems = navItems.slice(visibleCount); - const { name, subtext } = splitTournamentName( - tournament.ctx.name, - tournament.ctx.organization?.series ?? [], - ); + const { name, subtext } = tournamentNameParts(tournament); const homeHref = tournament.isLeagueDivision ? tournamentInfoPage(tournament.ctx.parentTournamentId!) diff --git a/app/features/tournament/tournament-utils.test.ts b/app/features/tournament/tournament-utils.test.ts index a201a89e6..7309a3672 100644 --- a/app/features/tournament/tournament-utils.test.ts +++ b/app/features/tournament/tournament-utils.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import type { CastedMatchesInfo } from "~/db/tables"; import * as Seasons from "../mmr/core/Seasons"; import type { ParsedBracket } from "../tournament-bracket/core/Progression"; +import { testTournament } from "../tournament-bracket/core/tests/test-utils"; import { bracketProgressionLabel, compareTeamsForOrdering, @@ -11,6 +12,7 @@ import { splitTournamentName, type TeamForOrdering, tournamentInWeaponReportingWindow, + tournamentNameParts, updatedCastedMatchesInfo, } from "./tournament-utils"; @@ -756,6 +758,44 @@ describe("splitTournamentName", () => { }); }); +describe("tournamentNameParts", () => { + it("uses the parent tournament name and division subtext for a league division", () => { + const tournament = testTournament({ + ctx: { + name: "LUTI: Season 17 - Division 1", + parentTournamentId: 1, + parentTournamentName: "LUTI: Season 17", + }, + }); + + expect(tournamentNameParts(tournament)).toEqual({ + name: "LUTI: Season 17", + subtext: "Division 1", + }); + }); + + it("falls back to the organization series when not a league division", () => { + const tournament = testTournament({ + ctx: { + name: "In The Zone 54", + organization: { + id: 1, + name: "Sendou's Tournaments", + slug: "sendou", + logoUrl: null, + members: [], + series: [{ name: "In The Zone" }], + }, + }, + }); + + expect(tournamentNameParts(tournament)).toEqual({ + name: "In The Zone", + subtext: "54", + }); + }); +}); + describe("bracketProgressionLabel", () => { it("returns the short code for a single stage", () => { expect(bracketProgressionLabel([{ type: "single_elimination" }])).toBe( diff --git a/app/features/tournament/tournament-utils.ts b/app/features/tournament/tournament-utils.ts index 24af8bac0..210b3b50e 100644 --- a/app/features/tournament/tournament-utils.ts +++ b/app/features/tournament/tournament-utils.ts @@ -464,6 +464,31 @@ export function splitTournamentName( return { name: matchingSeries.name, subtext }; } +/** + * Resolves the display name and subtext for a tournament's identity. + * + * For a league division the parent tournament name is used as the base name and + * the division name (e.g. `"Division 1"`) becomes the subtext. For all other + * tournaments the split is based on the organization's tournament series. + * + * @see {@link splitTournamentName} + */ +export function tournamentNameParts(tournament: TournamentClass): { + name: string; + subtext?: string; +} { + if (tournament.isLeagueDivision && tournament.ctx.parentTournamentName) { + return splitTournamentName(tournament.ctx.name, [ + { name: tournament.ctx.parentTournamentName }, + ]); + } + + return splitTournamentName( + tournament.ctx.name, + tournament.ctx.organization?.series ?? [], + ); +} + const STAGE_TYPE_TO_SHORT_CODE: Record< Tables["TournamentStage"]["type"], string