From cfedcf3c72c96e81dcea2bd477da98a860b78a8f Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Mon, 17 Jan 2022 09:15:38 +0200 Subject: [PATCH] Match modal using match number with round name --- .../tournament/EliminationBracketMatch.tsx | 2 +- app/core/tournament/bracket.ts | 35 ++++++++++++++ .../queries/findMatchModalInfoByNumber.ts | 46 +++++++++++++++++++ .../bracket.$bid/match.$mid.tsx | 37 --------------- .../bracket.$bid/match.$num.tsx | 31 +++++++++++++ 5 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 app/db/tournament/queries/findMatchModalInfoByNumber.ts delete mode 100644 app/routes/to/$organization.$tournament/bracket.$bid/match.$mid.tsx create mode 100644 app/routes/to/$organization.$tournament/bracket.$bid/match.$num.tsx diff --git a/app/components/tournament/EliminationBracketMatch.tsx b/app/components/tournament/EliminationBracketMatch.tsx index bf8e04e52..971d8aa57 100644 --- a/app/components/tournament/EliminationBracketMatch.tsx +++ b/app/components/tournament/EliminationBracketMatch.tsx @@ -35,7 +35,7 @@ export function EliminationBracketMatch({ return ( {children} diff --git a/app/core/tournament/bracket.ts b/app/core/tournament/bracket.ts index aec5e5b27..fe0b7670a 100644 --- a/app/core/tournament/bracket.ts +++ b/app/core/tournament/bracket.ts @@ -120,6 +120,41 @@ export function getRoundNames(bracket: Bracket): EliminationBracket { }; } +/** Returns round name (e.g. "Winners' Round 1") from positions + * + * @param position Position of the match to get the name for + * @param allPositions All positions of matches in the bracket. Positive integers are winners and negative losers. + * + */ +export function getRoundNameByPositions( + position: number, + allPositions: number[] +): string { + const winnersRoundCount = allPositions.reduce( + (sum, pos) => (pos > 0 ? 1 : 0) + sum, + 0 + ); + const losersRoundCount = allPositions.reduce( + (sum, pos) => (pos < 0 ? 1 : 0) + sum, + 0 + ); + + const allRoundNames = { + winners: winnersRoundNames(winnersRoundCount, losersRoundCount === 0), + losers: losersRoundNames(losersRoundCount), + }; + + const result = (() => { + const index = Math.abs(position) - 1; + if (position > 0) return allRoundNames.winners[index]; + + return allRoundNames.losers[index]; + })(); + invariant(result, "No round name found"); + + return result; +} + export function countRounds(bracket: Bracket): EliminationBracket { const isDE = bracket.losers.length > 0; let winners = 1 + Number(isDE); diff --git a/app/db/tournament/queries/findMatchModalInfoByNumber.ts b/app/db/tournament/queries/findMatchModalInfoByNumber.ts new file mode 100644 index 000000000..a6bbc698a --- /dev/null +++ b/app/db/tournament/queries/findMatchModalInfoByNumber.ts @@ -0,0 +1,46 @@ +import { Prisma } from "@prisma/client"; +import { getRoundNameByPositions } from "~/core/tournament/bracket"; +import { db } from "~/utils/db.server"; + +export type FindMatchModalInfoByNumber = Prisma.PromiseReturnType< + typeof findMatchModalInfoByNumber +>; + +export async function findMatchModalInfoByNumber({ + bracketId, + matchNumber, +}: { + bracketId: string; + matchNumber: number; +}) { + const tournamentRounds = await db.tournamentRound.findMany({ + where: { bracketId }, + include: { + matches: { + include: { results: true, participants: { include: { team: true } } }, + }, + stages: true, + }, + }); + + const tournamentRound = tournamentRounds.find((round) => + round.matches.find((match) => match.position === matchNumber) + ); + const match = tournamentRound?.matches.find( + (match) => match.position === matchNumber + ); + + if (!tournamentRound || !match) return; + + const teamsOrdered = match.participants.sort((a, b) => + b.order.localeCompare(a.order) + ); + + return { + title: `${teamsOrdered[0].team.name} vs. ${teamsOrdered[1].team.name}`, + roundName: getRoundNameByPositions( + tournamentRound.position, + tournamentRounds.map((round) => round.position) + ), + }; +} diff --git a/app/routes/to/$organization.$tournament/bracket.$bid/match.$mid.tsx b/app/routes/to/$organization.$tournament/bracket.$bid/match.$mid.tsx deleted file mode 100644 index 9cb108548..000000000 --- a/app/routes/to/$organization.$tournament/bracket.$bid/match.$mid.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { json, LoaderFunction, useLoaderData, useLocation } from "remix"; -import invariant from "tiny-invariant"; -import Modal from "~/components/Modal"; -import { matchById } from "~/services/bracket"; -import type { MatchByIdI } from "~/services/bracket"; -import { Serialized } from "~/utils"; - -const typedJson = (args: MatchByIdI) => json(args); - -export const loader: LoaderFunction = async ({ params }) => { - invariant(typeof params.mid === "string", "Expected params.mid to be string"); - - const match = await matchById(params.mid); - return typedJson(match); -}; - -export default function MatchModal() { - const data = useLoaderData>(); - const location = useLocation(); - - const teamVersusTitle = (): string => { - const teamsOrdered = data.participants.sort((a, b) => - b.order.localeCompare(a.order) - ); - - return `${teamsOrdered[0].team.name} vs. ${teamsOrdered[1].team.name}`; - }; - - return ( - - asdasd - - ); -} diff --git a/app/routes/to/$organization.$tournament/bracket.$bid/match.$num.tsx b/app/routes/to/$organization.$tournament/bracket.$bid/match.$num.tsx new file mode 100644 index 000000000..f00aa4d33 --- /dev/null +++ b/app/routes/to/$organization.$tournament/bracket.$bid/match.$num.tsx @@ -0,0 +1,31 @@ +import { json, LoaderFunction, useLoaderData, useLocation } from "remix"; +import { z } from "zod"; +import Modal from "~/components/Modal"; +import { + FindMatchModalInfoByNumber, + findMatchModalInfoByNumber, +} from "~/db/tournament/queries/findMatchModalInfoByNumber"; + +const typedJson = (args: NonNullable) => json(args); + +export const loader: LoaderFunction = async ({ params }) => { + const { bid: bracketId, num: matchNumber } = z + .object({ bid: z.string(), num: z.preprocess(Number, z.number()) }) + .parse(params); + + const match = await findMatchModalInfoByNumber({ bracketId, matchNumber }); + if (!match) throw new Response("No match found", { status: 400 }); + + return typedJson(match); +}; + +export default function MatchModal() { + const data = useLoaderData>(); + const location = useLocation(); + + return ( + +

{data.roundName}

+
+ ); +}