From 7914377309fe2401a1a5cf2f7d3dab1c3932ac63 Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Thu, 23 Dec 2021 13:58:18 +0200 Subject: [PATCH] Tournament bracket working with data --- .../tournament/EliminationBracket.tsx | 172 ++++++++---------- app/core/tournament/bracket.ts | 37 ++-- app/models/TournamentBracket.ts | 2 + app/routes/to/$organization.$tournament.tsx | 1 + .../$organization.$tournament/bracket.$id.tsx | 9 +- app/services/tournament.ts | 45 +++-- app/styles/tournament-bracket.css | 7 +- 7 files changed, 145 insertions(+), 128 deletions(-) diff --git a/app/components/tournament/EliminationBracket.tsx b/app/components/tournament/EliminationBracket.tsx index ee76a18c4..84187a0d1 100644 --- a/app/components/tournament/EliminationBracket.tsx +++ b/app/components/tournament/EliminationBracket.tsx @@ -1,126 +1,96 @@ import classNames from "classnames"; +import type { BracketModified } from "~/services/tournament"; +import { Unpacked } from "~/utils"; -const rounds = [ - { - title: "Round 1", - bestOf: 3, - status: "DONE", - }, - { - title: "Round 2", - bestOf: 5, - status: "INPROGRESS", - }, - { - title: "Semifinals", - bestOf: 5, - status: "UPCOMING", - }, - { - title: "Finals", - bestOf: 7, - status: "UPCOMING", - }, -] as const; - -export function EliminationBracket() { +export function EliminationBracket({ + bracketSide, +}: { + bracketSide: BracketModified["winners"]; +}) { return (
- {rounds.map((round, i) => ( + {bracketSide.map((round, i) => ( ))} -
-
- - - - -
-
-
-
-
-
-
-
- - -
-
-
-
-
-
-
- -
-
-
-
-
-
- -
+ {bracketSide.map((round, i) => { + return ( +
+
+ {round.matches.map((match) => { + return ; + })} +
+
+ {i !== bracketSide.length - 1 && + new Array(Math.ceil(round.matches.length / 2)) + .fill(null) + .map((_, i) =>
)} +
+
+ ); + })}
); } -function RoundInfo(props: { - round: { - title: string; - bestOf: number; - status: "DONE" | "INPROGRESS" | "UPCOMING"; - }; +function RoundInfo({ + title, + bestOf, + status, + isLast, +}: { + title: string; + bestOf: number; + status: "DONE" | "INPROGRESS" | "UPCOMING"; isLast?: boolean; }) { return (
-
- {props.round.title} -
- {props.round.status !== "DONE" && ( -
- Bo{props.round.bestOf} -
+
{title}
+ {status !== "DONE" && ( +
Bo{bestOf}
)}
); } -export function Match() { +export function Match({ + match, +}: { + match: Unpacked["matches"]>; +}) { return (
1
@@ -130,7 +100,14 @@ export function Match() { "tournament-bracket__elim__teamOne" )} > - Team Olive 1 + {match.participants?.[0]}{" "} + + {match.score?.[0] ?? 0} +
- Chimera 1 + {match.participants?.[1]}{" "} + + {match.score?.[1] ?? 0} +
); diff --git a/app/core/tournament/bracket.ts b/app/core/tournament/bracket.ts index 24807842f..bc3e226a9 100644 --- a/app/core/tournament/bracket.ts +++ b/app/core/tournament/bracket.ts @@ -89,26 +89,33 @@ export function getRoundsDefaultBestOf( }; } +export function winnersRoundNames(count: number, isSE: boolean) { + return new Array(count).fill(null).map((_, i) => { + if (i === count - 4 + Number(isSE)) { + return "Winners' Semifinals"; + } + if (i === count - 3 + Number(isSE)) return "Winners' Finals"; + if (i === count - 2 + Number(isSE)) return "Grand Finals"; + if (!isSE && i === count - 1) return "Bracket Reset"; + return `Winners' Round ${i + 1}`; + }); +} + +export function losersRoundNames(count: number) { + return new Array(count) + .fill(null) + .map((_, i) => + i === count - 1 ? "Losers' Finals" : `Losers' Round ${i + 1}` + ); +} + export function getRoundNames(bracket: Bracket): EliminationBracket { const { winners: winnersRoundCount, losers: losersRoundCount } = countRounds(bracket); return { - winners: new Array(winnersRoundCount).fill(null).map((_, i) => { - const isSE = bracket.losers.length === 0; - if (i === winnersRoundCount - 4 + Number(isSE)) { - return "Winners' Semifinals"; - } - if (i === winnersRoundCount - 3 + Number(isSE)) return "Winners' Finals"; - if (i === winnersRoundCount - 2 + Number(isSE)) return "Grand Finals"; - if (!isSE && i === winnersRoundCount - 1) return "Bracket Reset"; - return `Winners' Round ${i + 1}`; - }), - losers: new Array(losersRoundCount) - .fill(null) - .map((_, i) => - i === losersRoundCount - 1 ? "Losers' Finals" : `Losers' Round ${i + 1}` - ), + winners: winnersRoundNames(winnersRoundCount, losersRoundCount === 0), + losers: losersRoundNames(losersRoundCount), }; } diff --git a/app/models/TournamentBracket.ts b/app/models/TournamentBracket.ts index 2362130bc..bf3da9d05 100644 --- a/app/models/TournamentBracket.ts +++ b/app/models/TournamentBracket.ts @@ -8,6 +8,7 @@ export function findById(bracketId: string) { select: { rounds: { select: { + id: true, position: true, _count: { select: { @@ -16,6 +17,7 @@ export function findById(bracketId: string) { }, matches: { select: { + id: true, participants: { select: { team: { diff --git a/app/routes/to/$organization.$tournament.tsx b/app/routes/to/$organization.$tournament.tsx index 70ed5b1fc..f8696ff1e 100644 --- a/app/routes/to/$organization.$tournament.tsx +++ b/app/routes/to/$organization.$tournament.tsx @@ -64,6 +64,7 @@ export default function TournamentPage() { const tournamentIsOver = false; if (tournamentHasStarted(data.brackets)) { + // TODO: link broken result.push({ code: "bracket", text: "Bracket" }); if (!tournamentIsOver) { result.push({ code: "streams", text: "Streams (4)" }); diff --git a/app/routes/to/$organization.$tournament/bracket.$id.tsx b/app/routes/to/$organization.$tournament/bracket.$id.tsx index c7c5fca7f..df42d4040 100644 --- a/app/routes/to/$organization.$tournament/bracket.$id.tsx +++ b/app/routes/to/$organization.$tournament/bracket.$id.tsx @@ -19,14 +19,17 @@ export const loader: LoaderFunction = async ({ params }) => { return typedJson(bracket); }; +// TODO: top seed is at the bottom of the tournament +// TODO: bottom bracket lines are wrong +// TODO: match numbers are off +// TODO: make bracket a bit smaller export default function BracketTabWrapper() { const data = useLoaderData(); - console.log("data", data); - return (
- + +
); } diff --git a/app/services/tournament.ts b/app/services/tournament.ts index f1bbeaeb4..f6e3fd52c 100644 --- a/app/services/tournament.ts +++ b/app/services/tournament.ts @@ -5,16 +5,19 @@ import { } from "~/constants"; import { EliminationBracket, + EliminationBracketSide, + losersRoundNames, MapListIds, tournamentRoundsForDB, + winnersRoundNames, } from "~/core/tournament/bracket"; import { isTournamentAdmin } from "~/core/tournament/permissions"; import { sortTeamsBySeed } from "~/core/tournament/utils"; import * as Tournament from "~/models/Tournament"; +import * as TournamentBracket from "~/models/TournamentBracket"; import * as TournamentTeam from "~/models/TournamentTeam"; import * as TournamentTeamMember from "~/models/TournamentTeamMember"; import * as TrustRelationship from "~/models/TrustRelationship"; -import * as TournamentBracket from "~/models/TournamentBracket"; import { Serialized, Unpacked } from "~/utils"; import { db } from "~/utils/db.server"; @@ -127,8 +130,11 @@ export async function findTournamentWithInviteCodes({ export type BracketModified = EliminationBracket; type BracketModifiedSide = { + id: string; + name: string; bestOf: number; matches: { + id: string; score?: [upperTeamScore: number, lowerTeamScore: number]; participants?: [upperTeamName: string | null, lowerTeamName: string | null]; }[]; @@ -139,29 +145,39 @@ export async function bracketById(bracketId: string): Promise { if (!bracket) throw new Response("No bracket found", { status: 404 }); + const winnersRounds = bracket.rounds + .filter((round) => round.position > 0) + .sort((a, b) => a.position - b.position); + + const losersRounds = bracket.rounds + .filter((round) => round.position < 0) + .sort((a, b) => b.position - a.position); + + console.log("losersRounds.length", losersRounds.length); + return { - winners: modifyRounds( - bracket.rounds - .filter((round) => round.position > 0) - .sort((a, b) => a.position - b.position) - ), - losers: modifyRounds( - bracket.rounds - .filter((round) => round.position < 0) - .sort((a, b) => b.position - a.position) - ), + winners: modifyRounds(winnersRounds, "winners", losersRounds.length === 0), + losers: modifyRounds(losersRounds, "losers", false), }; } function modifyRounds( - rounds: NonNullable["rounds"] + rounds: NonNullable["rounds"], + side: EliminationBracketSide, + isSE: boolean ): BracketModifiedSide { - return rounds.map((round) => { + const roundNames = + side === "winners" + ? winnersRoundNames(rounds.length, isSE) + : losersRoundNames(rounds.length); + return rounds.map((round, i) => { return { + id: round.id, + name: roundNames[i], bestOf: round._count.stages, matches: round.matches.map((match) => { const score = - match.results.length > 0 + match.participants.length === 2 ? match.results.reduce( (scores: [number, number], result) => { if (result.winner === "UPPER") scores[0]++; @@ -182,6 +198,7 @@ function modifyRounds( ] : undefined; return { + id: match.id, score, participants, }; diff --git a/app/styles/tournament-bracket.css b/app/styles/tournament-bracket.css index f775a06c2..3585734ba 100644 --- a/app/styles/tournament-bracket.css +++ b/app/styles/tournament-bracket.css @@ -1,6 +1,9 @@ /* TODO: should be able to go wider than container https://stackoverflow.com/questions/5581034/is-there-are-way-to-make-a-child-divs-width-wider-than-the-parent-div-using-css */ .tournament-bracket__container { + display: flex; width: 100%; + flex-direction: column; + gap: var(--s-6); } .tournament-bracket__elim__container { @@ -95,7 +98,7 @@ .tournament-bracket__elim__matches { display: flex; - height: calc(var(--brackets-match-height) * var(--brackets-matches)); + height: calc(var(--brackets-match-height) * var(--brackets-max-matches)); flex-direction: column; justify-content: space-around; } @@ -111,7 +114,7 @@ width: calc(var(--brackets-match-height) / 4); height: calc( 2px + var(--brackets-match-height) * - (var(--brackets-matches) / var(--brackets-column-matches)) + (var(--brackets-max-matches) / var(--brackets-column-matches)) ); border-top: 2px solid var(--theme-transparent); border-bottom: var(--brackets-bottom-border-length, 2px) solid