Prompt to select team count when preparing SE/DE maps
Some checks failed
Tests and checks on push / run-checks-and-tests (push) Has been cancelled
Updates translation progress / update-translation-progress-issue (push) Has been cancelled

This commit is contained in:
Kalle 2025-02-20 22:28:17 +02:00
parent 2b38c0ab5c
commit 0c470a99cb
3 changed files with 158 additions and 111 deletions

View File

@ -66,22 +66,24 @@ export function BracketMapListDialog({
: untrimmedPreparedMaps;
const [szFirst, setSzFirst] = React.useState(false);
const [eliminationTeamCount, setEliminationTeamCount] = React.useState(() => {
const [eliminationTeamCount, setEliminationTeamCount] = React.useState<
number | null
>(() => {
if (preparedMaps?.eliminationTeamCount) {
return preparedMaps.eliminationTeamCount;
}
// at least 8 for somewhat reasonable default
return Math.max(
PreparedMaps.eliminationTeamCountOptions(bracketTeamsCount)[0].max,
PreparedMaps.eliminationTeamCountOptions(0)[2].max,
);
if (isPreparing) {
return null;
}
return PreparedMaps.eliminationTeamCountOptions(bracketTeamsCount)[0].max;
});
const bracketData = isPreparing
? teamCountAdjustedBracketData({
bracket,
teamCount: eliminationTeamCount,
teamCount: eliminationTeamCount ?? 2,
})
: bracket.data;
const rounds = bracketData.round;
@ -206,8 +208,13 @@ export function BracketMapListDialog({
const globalSelections =
bracket.type === "round_robin" || bracket.type === "swiss";
const needsToPickEliminationTeamCount =
(bracket.type === "single_elimination" ||
bracket.type === "double_elimination") &&
!eliminationTeamCount;
return (
<Dialog isOpen={isOpen} close={close} className="w-full">
<Dialog isOpen={isOpen} close={close} className="map-list-dialog__dialog">
<fetcher.Form method="post" className="map-list-dialog__container">
<input type="hidden" name="bracketIdx" value={bracketIdx} />
<input
@ -228,7 +235,7 @@ export function BracketMapListDialog({
<input
type="hidden"
name="eliminationTeamCount"
value={eliminationTeamCount}
value={eliminationTeamCount ?? -1}
/>
) : null}
<div>
@ -286,6 +293,11 @@ export function BracketMapListDialog({
count={eliminationTeamCount}
realCount={bracketTeamsCount}
setCount={(newCount) => {
if (!newCount) {
setEliminationTeamCount(null);
return;
}
const newBracketData = teamCountAdjustedBracketData({
bracket,
teamCount: newCount,
@ -378,106 +390,130 @@ export function BracketMapListDialog({
</Button>
) : null}
</div>
<div className="stack horizontal md flex-wrap justify-center">
{roundsWithNames.map((round) => {
const roundMaps = maps.get(round.id);
invariant(roundMaps, "Expected maps to be defined");
return (
<RoundMapList
key={round.id}
name={round.name}
maps={roundMaps}
onHoverMap={setHoveredMap}
hoveredMap={hoveredMap}
includeRoundSpecificSelections={
bracket.type !== "round_robin"
}
onCountChange={(newCount) => {
const newMapCounts = new Map(mapCounts);
const bracketRound = rounds.find(
(r) => r.id === round.id,
);
invariant(bracketRound, "Expected round to be defined");
const groupInfo = newMapCounts.get(bracketRound.group_id);
invariant(groupInfo, "Expected group info to be defined");
const oldMapInfo = newMapCounts
.get(bracketRound.group_id)
?.get(bracketRound.number);
invariant(oldMapInfo, "Expected map info to be defined");
groupInfo.set(bracketRound.number, {
...oldMapInfo,
count: newCount,
});
const newMaps = generateTournamentRoundMaplist({
mapCounts: newMapCounts,
pool: tournament.ctx.toSetMapPool,
rounds,
type: bracket.type,
roundsWithPickBan,
pickBanStyle,
flavor,
});
setMaps(newMaps);
}}
onPickBanChange={
pickBanStyle
? (hasPickBan) => {
const newRoundsWithPickBan = new Set(
roundsWithPickBan,
);
if (hasPickBan) {
newRoundsWithPickBan.add(round.id);
} else {
newRoundsWithPickBan.delete(round.id);
}
setMaps(
generateTournamentRoundMaplist({
mapCounts,
pool: tournament.ctx.toSetMapPool,
rounds,
type: bracket.type,
roundsWithPickBan: newRoundsWithPickBan,
pickBanStyle,
flavor,
}),
);
}
: undefined
}
onRoundMapListChange={(newRoundMaps) => {
const newMaps = new Map(maps);
newMaps.set(round.id, newRoundMaps);
setMaps(newMaps);
}}
/>
);
})}
</div>
{!validateNoDecreasingCount() ? (
<div className="text-warning text-center">
Invalid selection: tournament progression decreases in map count
</div>
) : pickBanStyle && roundsWithPickBan.size === 0 ? (
<div className="text-warning text-center">
Invalid selection: pick/ban style selected but no rounds have it
enabled
{needsToPickEliminationTeamCount ? (
<div className="text-center text-lg font-bold my-24">
Pick the expected teams count above to prepare maps
<div className="text-lighter text-sm">
For SE/DE formats team count affects the amount of rounds
played
</div>
</div>
) : (
<SubmitButton
variant="outlined"
size="tiny"
testId="confirm-finalize-bracket-button"
_action={isPreparing ? "PREPARE_MAPS" : "START_BRACKET"}
className="mx-auto"
>
{isPreparing ? "Save the maps" : "Start the bracket"}
</SubmitButton>
<>
<div className="stack horizontal md flex-wrap justify-center">
{roundsWithNames.map((round) => {
const roundMaps = maps.get(round.id);
invariant(roundMaps, "Expected maps to be defined");
return (
<RoundMapList
key={round.id}
name={round.name}
maps={roundMaps}
onHoverMap={setHoveredMap}
hoveredMap={hoveredMap}
includeRoundSpecificSelections={
bracket.type !== "round_robin"
}
onCountChange={(newCount) => {
const newMapCounts = new Map(mapCounts);
const bracketRound = rounds.find(
(r) => r.id === round.id,
);
invariant(
bracketRound,
"Expected round to be defined",
);
const groupInfo = newMapCounts.get(
bracketRound.group_id,
);
invariant(
groupInfo,
"Expected group info to be defined",
);
const oldMapInfo = newMapCounts
.get(bracketRound.group_id)
?.get(bracketRound.number);
invariant(
oldMapInfo,
"Expected map info to be defined",
);
groupInfo.set(bracketRound.number, {
...oldMapInfo,
count: newCount,
});
const newMaps = generateTournamentRoundMaplist({
mapCounts: newMapCounts,
pool: tournament.ctx.toSetMapPool,
rounds,
type: bracket.type,
roundsWithPickBan,
pickBanStyle,
flavor,
});
setMaps(newMaps);
}}
onPickBanChange={
pickBanStyle
? (hasPickBan) => {
const newRoundsWithPickBan = new Set(
roundsWithPickBan,
);
if (hasPickBan) {
newRoundsWithPickBan.add(round.id);
} else {
newRoundsWithPickBan.delete(round.id);
}
setMaps(
generateTournamentRoundMaplist({
mapCounts,
pool: tournament.ctx.toSetMapPool,
rounds,
type: bracket.type,
roundsWithPickBan: newRoundsWithPickBan,
pickBanStyle,
flavor,
}),
);
}
: undefined
}
onRoundMapListChange={(newRoundMaps) => {
const newMaps = new Map(maps);
newMaps.set(round.id, newRoundMaps);
setMaps(newMaps);
}}
/>
);
})}
</div>
{!validateNoDecreasingCount() ? (
<div className="text-warning text-center">
Invalid selection: tournament progression decreases in map
count
</div>
) : pickBanStyle && roundsWithPickBan.size === 0 ? (
<div className="text-warning text-center">
Invalid selection: pick/ban style selected but no rounds
have it enabled
</div>
) : (
<SubmitButton
variant="outlined"
size="tiny"
testId="confirm-finalize-bracket-button"
_action={isPreparing ? "PREPARE_MAPS" : "START_BRACKET"}
className="mx-auto"
>
{isPreparing ? "Save the maps" : "Start the bracket"}
</SubmitButton>
)}
</>
)}
</>
)}
@ -593,18 +629,21 @@ function EliminationTeamCountSelect({
realCount,
setCount,
}: {
count: number;
count: number | null;
realCount: number;
setCount: (count: number) => void;
setCount: (count: number | null) => void;
}) {
return (
<div>
<Label htmlFor="elimination-team-count">Expected teams</Label>
<select
id="elimination-team-count"
onChange={(e) => setCount(Number(e.target.value))}
defaultValue={count}
onChange={(e) =>
setCount(e.target.value === "" ? null : Number(e.target.value))
}
defaultValue={count ?? ""}
>
<option value="">Select count</option>
{PreparedMaps.eliminationTeamCountOptions(realCount).map(
(teamCountRange) => {
const label =

View File

@ -534,6 +534,10 @@
width: 280px;
}
.map-list-dialog__dialog {
width: 64rem;
}
.map-list-dialog__container {
display: flex;
flex-direction: column;

View File

@ -326,6 +326,10 @@
margin-block: var(--s-4);
}
.my-24 {
margin-block: var(--s-24);
}
.m-0-auto {
margin: 0 auto;
}