mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 23:27:55 -05:00
Loser team source info shown
This commit is contained in:
@@ -3,6 +3,7 @@ import invariant from "tiny-invariant";
|
||||
import { matchIsOver } from "~/core/tournament/utils";
|
||||
import type { BracketModified } from "~/services/tournament";
|
||||
import { MyCSSProperties, Unpacked } from "~/utils";
|
||||
import { EliminationBracketMatch } from "./EliminationBracketMatch";
|
||||
|
||||
export function EliminationBracket({
|
||||
bracketSide,
|
||||
@@ -84,7 +85,7 @@ export function EliminationBracket({
|
||||
{round.matches.map((match) => {
|
||||
// TODO: handle losers
|
||||
return (
|
||||
<Match
|
||||
<EliminationBracketMatch
|
||||
hidden={
|
||||
round.name.includes("Winner") &&
|
||||
roundI === 0 &&
|
||||
@@ -137,65 +138,3 @@ function RoundInfo({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Match({
|
||||
match,
|
||||
hidden,
|
||||
ownTeamName,
|
||||
isOver,
|
||||
}: {
|
||||
match: Unpacked<Unpacked<BracketModified["winners"]>["matches"]>;
|
||||
hidden?: boolean;
|
||||
ownTeamName?: string;
|
||||
isOver: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className={clsx("tournament-bracket__elim__match", { hidden })}>
|
||||
<div className="tournament-bracket__elim__roundNumber">
|
||||
{match.number}
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
"tournament-bracket__elim__team",
|
||||
"tournament-bracket__elim__teamOne",
|
||||
{
|
||||
own:
|
||||
!isOver && ownTeamName && ownTeamName === match.participants?.[0],
|
||||
defeated:
|
||||
isOver && (match.score?.[0] ?? 0) < (match.score?.[1] ?? 0),
|
||||
}
|
||||
)}
|
||||
>
|
||||
{match.participants?.[0]}{" "}
|
||||
<span
|
||||
className={clsx("tournament-bracket__elim__score", {
|
||||
invisible: typeof match.score?.[0] !== "number",
|
||||
})}
|
||||
>
|
||||
{match.score?.[0] ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
"tournament-bracket__elim__team",
|
||||
"tournament-bracket__elim__teamTwo",
|
||||
{
|
||||
own:
|
||||
!isOver && ownTeamName && ownTeamName === match.participants?.[1],
|
||||
defeated:
|
||||
isOver && (match.score?.[1] ?? 0) < (match.score?.[0] ?? 0),
|
||||
}
|
||||
)}
|
||||
>
|
||||
{match.participants?.[1]}{" "}
|
||||
<span
|
||||
className={clsx("tournament-bracket__elim__score", {
|
||||
invisible: typeof match.score?.[0] !== "number",
|
||||
})}
|
||||
>
|
||||
{match.score?.[1] ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
76
app/components/tournament/EliminationBracketMatch.tsx
Normal file
76
app/components/tournament/EliminationBracketMatch.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import clsx from "clsx";
|
||||
import type { BracketModified } from "~/services/tournament";
|
||||
import { Unpacked } from "~/utils";
|
||||
|
||||
export function EliminationBracketMatch({
|
||||
match,
|
||||
hidden,
|
||||
ownTeamName,
|
||||
isOver,
|
||||
}: {
|
||||
match: Unpacked<Unpacked<BracketModified["winners"]>["matches"]>;
|
||||
hidden?: boolean;
|
||||
ownTeamName?: string;
|
||||
isOver: boolean;
|
||||
}) {
|
||||
const cellText = (index: number) => {
|
||||
if (match.participants?.[index]) return match.participants?.[index];
|
||||
if (match.participantSourceMatches?.[index]) {
|
||||
return (
|
||||
<i className="tournament-bracket__elim__loser-info">{`Loser of match ${match.participantSourceMatches?.[index]}`}</i>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx("tournament-bracket__elim__match", { hidden })}>
|
||||
<div className="tournament-bracket__elim__roundNumber">
|
||||
{match.number}
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
"tournament-bracket__elim__team",
|
||||
"tournament-bracket__elim__teamOne",
|
||||
{
|
||||
own:
|
||||
!isOver && ownTeamName && ownTeamName === match.participants?.[0],
|
||||
defeated:
|
||||
isOver && (match.score?.[0] ?? 0) < (match.score?.[1] ?? 0),
|
||||
}
|
||||
)}
|
||||
>
|
||||
{cellText(0)}
|
||||
<span
|
||||
className={clsx("tournament-bracket__elim__score", {
|
||||
invisible: typeof match.score?.[0] !== "number",
|
||||
})}
|
||||
>
|
||||
{match.score?.[0] ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
"tournament-bracket__elim__team",
|
||||
"tournament-bracket__elim__teamTwo",
|
||||
{
|
||||
own:
|
||||
!isOver && ownTeamName && ownTeamName === match.participants?.[1],
|
||||
defeated:
|
||||
isOver && (match.score?.[1] ?? 0) < (match.score?.[0] ?? 0),
|
||||
}
|
||||
)}
|
||||
>
|
||||
{cellText(1)}{" "}
|
||||
<span
|
||||
className={clsx("tournament-bracket__elim__score", {
|
||||
invisible: typeof match.score?.[0] !== "number",
|
||||
})}
|
||||
>
|
||||
{match.score?.[1] ?? 0}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -147,6 +147,10 @@ type BracketModifiedSide = {
|
||||
number: number;
|
||||
score?: [upperTeamScore: number, lowerTeamScore: number];
|
||||
participants?: [upperTeamName: string | null, lowerTeamName: string | null];
|
||||
participantSourceMatches?: [
|
||||
upperTeamSourceMatchNumber: number | null,
|
||||
lowerTeamSourceMatchNumber: number | null
|
||||
];
|
||||
winnerDestinationMatchId: string | null;
|
||||
loserDestinationMatchId: string | null;
|
||||
}[];
|
||||
@@ -165,12 +169,20 @@ export async function bracketById(bracketId: string): Promise<BracketModified> {
|
||||
.filter((round) => round.position < 0)
|
||||
.sort((a, b) => b.position - a.position);
|
||||
|
||||
const winners = modifyRounds(
|
||||
winnersRounds,
|
||||
"winners",
|
||||
losersRounds.length === 0
|
||||
);
|
||||
const losers = modifyRounds(losersRounds, "losers", false);
|
||||
|
||||
return {
|
||||
winners: modifyRounds(winnersRounds, "winners", losersRounds.length === 0),
|
||||
losers: modifyRounds(losersRounds, "losers", false),
|
||||
winners,
|
||||
losers: addLoserTeamSourceInfo({ winners, losers }),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: rename
|
||||
function modifyRounds(
|
||||
rounds: NonNullable<TournamentBracket.FindById>["rounds"],
|
||||
side: EliminationBracketSide,
|
||||
@@ -220,6 +232,40 @@ function modifyRounds(
|
||||
});
|
||||
}
|
||||
|
||||
function addLoserTeamSourceInfo({
|
||||
winners,
|
||||
losers,
|
||||
}: {
|
||||
winners: BracketModifiedSide;
|
||||
losers: BracketModifiedSide;
|
||||
}): BracketModifiedSide {
|
||||
const matchIdToSourceMatchNumber = winners
|
||||
.flatMap((round) => round.matches)
|
||||
.reduce((acc: Map<string, number[]>, match) => {
|
||||
if (!match.loserDestinationMatchId) return acc;
|
||||
|
||||
return acc.set(
|
||||
match.loserDestinationMatchId,
|
||||
(acc.get(match.loserDestinationMatchId) ?? [])
|
||||
.concat(match.number)
|
||||
.sort((a, b) => a - b)
|
||||
);
|
||||
}, new Map());
|
||||
|
||||
return losers.map((round) => ({
|
||||
...round,
|
||||
matches: round.matches.map((match) => {
|
||||
const sourceMatches = matchIdToSourceMatchNumber.get(match.id);
|
||||
return {
|
||||
...match,
|
||||
participantSourceMatches: sourceMatches
|
||||
? [sourceMatches[0] ?? null, sourceMatches[1] ?? null]
|
||||
: undefined,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
export const createTournamentTeam = TournamentTeam.create;
|
||||
|
||||
export async function createTournamentRounds({
|
||||
|
||||
@@ -230,6 +230,13 @@
|
||||
grid-area: team-two;
|
||||
}
|
||||
|
||||
.tournament-bracket__elim__loser-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xs);
|
||||
}
|
||||
|
||||
.tournament-bracket__elim__score {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user