Match modal using match number with round name

This commit is contained in:
Kalle (Sendou)
2022-01-17 09:15:38 +02:00
parent 93c4ed9edd
commit cfedcf3c72
5 changed files with 113 additions and 38 deletions

View File

@@ -35,7 +35,7 @@ export function EliminationBracketMatch({
return (
<Link
className="tournament-bracket__match__link"
to={`match/${match.id}`}
to={`match/${match.number}`}
>
{children}
</Link>

View File

@@ -120,6 +120,41 @@ export function getRoundNames(bracket: Bracket): EliminationBracket<string[]> {
};
}
/** 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<number> {
const isDE = bracket.losers.length > 0;
let winners = 1 + Number(isDE);

View File

@@ -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)
),
};
}

View File

@@ -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<Serialized<MatchByIdI>>();
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 (
<Modal
title={teamVersusTitle()}
closeUrl={location.pathname.split("/match")[0]}
>
asdasd
</Modal>
);
}

View File

@@ -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<FindMatchModalInfoByNumber>) => 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<NonNullable<FindMatchModalInfoByNumber>>();
const location = useLocation();
return (
<Modal title={data.title} closeUrl={location.pathname.split("/match")[0]}>
<h4>{data.roundName}</h4>
</Modal>
);
}