mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-29 06:43:17 -05:00
Match modal using match number with round name
This commit is contained in:
@@ -35,7 +35,7 @@ export function EliminationBracketMatch({
|
||||
return (
|
||||
<Link
|
||||
className="tournament-bracket__match__link"
|
||||
to={`match/${match.id}`}
|
||||
to={`match/${match.number}`}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
|
||||
@@ -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);
|
||||
|
||||
46
app/db/tournament/queries/findMatchModalInfoByNumber.ts
Normal file
46
app/db/tournament/queries/findMatchModalInfoByNumber.ts
Normal 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)
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user