mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-12 06:06:28 -05:00
User configurable tournament max member count (#2565)
This commit is contained in:
@@ -456,6 +456,8 @@ export interface TournamentSettings {
|
||||
roundCount: number;
|
||||
};
|
||||
minMembersPerTeam?: number;
|
||||
/** Maximum number of team members that can be registered (only applies to 4v4 tournaments) */
|
||||
maxMembersPerTeam?: number;
|
||||
isTest?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -477,6 +477,7 @@ type CreateArgs = Pick<
|
||||
mapPickingStyle: Tables["Tournament"]["mapPickingStyle"];
|
||||
bracketProgression: TournamentSettings["bracketProgression"] | null;
|
||||
minMembersPerTeam?: number;
|
||||
maxMembersPerTeam?: number;
|
||||
teamsPerGroup?: number;
|
||||
thirdPlaceMatch?: boolean;
|
||||
requireInGameNames?: boolean;
|
||||
@@ -525,6 +526,7 @@ export async function create(args: CreateArgs) {
|
||||
regClosesAt: args.regClosesAt,
|
||||
requireInGameNames: args.requireInGameNames,
|
||||
minMembersPerTeam: args.minMembersPerTeam,
|
||||
maxMembersPerTeam: args.maxMembersPerTeam,
|
||||
swiss:
|
||||
args.swissGroupCount && args.swissRoundCount
|
||||
? {
|
||||
@@ -725,6 +727,7 @@ async function updateTournamentTables(
|
||||
regClosesAt: args.regClosesAt,
|
||||
requireInGameNames: args.requireInGameNames,
|
||||
minMembersPerTeam: args.minMembersPerTeam,
|
||||
maxMembersPerTeam: args.maxMembersPerTeam,
|
||||
swiss:
|
||||
args.swissGroupCount && args.swissRoundCount
|
||||
? {
|
||||
|
||||
@@ -79,6 +79,7 @@ export const action: ActionFunction = async ({ request }) => {
|
||||
rankedModesShort.find((mode) => mode === data.toToolsMode) ?? null,
|
||||
bracketProgression: data.bracketProgression ?? null,
|
||||
minMembersPerTeam: data.minMembersPerTeam ?? undefined,
|
||||
maxMembersPerTeam: data.maxMembersPerTeam ?? undefined,
|
||||
isRanked: data.isRanked ?? undefined,
|
||||
isTest: data.isTest ?? undefined,
|
||||
isInvitational: data.isInvitational ?? false,
|
||||
|
||||
@@ -86,7 +86,14 @@ export const newCalendarEventActionSchema = z
|
||||
checkboxValueToBoolean,
|
||||
z.boolean().nullish(),
|
||||
),
|
||||
minMembersPerTeam: z.coerce.number().int().min(1).max(4).nullish(),
|
||||
minMembersPerTeam: z.preprocess(
|
||||
actualNumber,
|
||||
z.number().int().min(1).max(4).nullish(),
|
||||
),
|
||||
maxMembersPerTeam: z.preprocess(
|
||||
actualNumber,
|
||||
z.number().int().min(4).max(10).nullish(),
|
||||
),
|
||||
bracketProgression: bracketProgressionSchema.nullish(),
|
||||
})
|
||||
.refine(
|
||||
|
||||
@@ -1257,25 +1257,59 @@ function MapPoolValidationStatusMessage({
|
||||
function MemberCountSelect() {
|
||||
const baseEvent = useBaseEvent();
|
||||
const id = React.useId();
|
||||
const [memberCount, setMemberCount] = React.useState(
|
||||
baseEvent?.tournament?.ctx.settings.minMembersPerTeam ?? 4,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<label htmlFor={id} className="w-max">
|
||||
Players count
|
||||
</label>
|
||||
<select
|
||||
id={id}
|
||||
name="minMembersPerTeam"
|
||||
value={memberCount}
|
||||
onChange={(e) => setMemberCount(Number(e.target.value))}
|
||||
className="input__extra-small"
|
||||
>
|
||||
{[4, 3, 2, 1].map((count) => (
|
||||
<option key={count} value={count}>
|
||||
{`${count}v${count}`}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
{memberCount === 4 ? <MaxTeamMemberCountInput /> : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function MaxTeamMemberCountInput() {
|
||||
const baseEvent = useBaseEvent();
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label htmlFor={id} className="w-max">
|
||||
Players count
|
||||
Max team size
|
||||
</label>
|
||||
<select
|
||||
name="minMembersPerTeam"
|
||||
<input
|
||||
type="number"
|
||||
id={id}
|
||||
name="maxMembersPerTeam"
|
||||
min={4}
|
||||
max={10}
|
||||
defaultValue={
|
||||
baseEvent?.tournament?.ctx.settings.minMembersPerTeam ?? 4
|
||||
baseEvent?.tournament?.ctx.settings.maxMembersPerTeam ?? undefined
|
||||
}
|
||||
className="w-max"
|
||||
>
|
||||
{[4, 3, 2, 1].map((count) => (
|
||||
<option key={count} value={count}>
|
||||
{`${count}v${count}`}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
className="input__extra-small"
|
||||
/>
|
||||
<FormMessage type="info">
|
||||
Maximum number of players that can be registered per team. Doesn't apply
|
||||
to tournament organizers. Default is 6.
|
||||
</FormMessage>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -863,24 +863,15 @@ export class Tournament {
|
||||
}
|
||||
|
||||
/** what is the max amount of members teams can add in total? This limit doesn't apply to the organizer adding members to a team. */
|
||||
get maxTeamMemberCount() {
|
||||
get maxMembersPerTeam() {
|
||||
// special format
|
||||
if (this.minMembersPerTeam !== 4) return this.minMembersPerTeam;
|
||||
|
||||
if (this.isLeagueSignup || this.isLeagueDivision) return 8;
|
||||
|
||||
// TODO: retire this hack by making it user configurable
|
||||
if (this.ctx.organization?.id === 19 && this.ctx.name.includes("FLUTI")) {
|
||||
return 8;
|
||||
if (this.ctx.settings.maxMembersPerTeam) {
|
||||
return this.ctx.settings.maxMembersPerTeam;
|
||||
}
|
||||
|
||||
const maxMembersBeforeStart = 6;
|
||||
|
||||
if (this.hasStarted) {
|
||||
return maxMembersBeforeStart + 1;
|
||||
}
|
||||
|
||||
return maxMembersBeforeStart;
|
||||
return 6;
|
||||
}
|
||||
|
||||
/** Is the regular check-in (check-in for the whole tournament) open at this time? */
|
||||
|
||||
@@ -386,7 +386,7 @@ function AddSubsPopOver() {
|
||||
}
|
||||
|
||||
const subsAvailableToAdd =
|
||||
tournament.maxTeamMemberCount - ownedTeam.members.length;
|
||||
tournament.maxMembersPerTeam - ownedTeam.members.length;
|
||||
|
||||
const inviteLink = `${SENDOU_INK_BASE_URL}${tournamentJoinPage({
|
||||
tournamentId: tournament.ctx.id,
|
||||
|
||||
@@ -68,7 +68,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
inviteCode,
|
||||
teamToJoin,
|
||||
userId: user.id,
|
||||
maxTeamSize: tournament.maxTeamMemberCount,
|
||||
maxTeamSize: tournament.maxMembersPerTeam,
|
||||
}) === "VALID",
|
||||
"Cannot join this team or invite code is invalid",
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function JoinTeamPage() {
|
||||
inviteCode: data.inviteCode,
|
||||
teamToJoin,
|
||||
userId: user?.id,
|
||||
maxTeamSize: tournament.maxTeamMemberCount,
|
||||
maxTeamSize: tournament.maxMembersPerTeam,
|
||||
});
|
||||
|
||||
const textPrompt = () => {
|
||||
|
||||
@@ -971,7 +971,7 @@ function FillRoster({
|
||||
);
|
||||
|
||||
const optionalMembers = Math.max(
|
||||
tournament.maxTeamMemberCount - ownTeamMembers.length - missingMembers,
|
||||
tournament.maxMembersPerTeam - ownTeamMembers.length - missingMembers,
|
||||
0,
|
||||
);
|
||||
|
||||
@@ -992,7 +992,7 @@ function FillRoster({
|
||||
});
|
||||
})();
|
||||
|
||||
const teamIsFull = ownTeamMembers.length >= tournament.maxTeamMemberCount;
|
||||
const teamIsFull = ownTeamMembers.length >= tournament.maxMembersPerTeam;
|
||||
const canAddMembers = !teamIsFull && tournament.registrationOpen;
|
||||
|
||||
return (
|
||||
@@ -1085,7 +1085,7 @@ function FillRoster({
|
||||
<div className="tournament__section__warning">
|
||||
{t("tournament:pre.roster.footer", {
|
||||
atLeastCount: tournament.minMembersPerTeam,
|
||||
maxCount: tournament.maxTeamMemberCount,
|
||||
maxCount: tournament.maxMembersPerTeam,
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -81,14 +81,17 @@ input:user-invalid {
|
||||
|
||||
.input__extra-small {
|
||||
min-width: var(--input-width-extra-small);
|
||||
max-width: var(--input-width-extra-small);
|
||||
}
|
||||
|
||||
.input__small {
|
||||
min-width: var(--input-width-small);
|
||||
max-width: var(--input-width-small);
|
||||
}
|
||||
|
||||
.input__medium {
|
||||
min-width: var(--input-width-medium);
|
||||
max-width: var(--input-width-medium);
|
||||
}
|
||||
|
||||
input:not(.plain, .combobox-input):focus-within {
|
||||
|
||||
@@ -93,6 +93,7 @@ async function main() {
|
||||
autonomousSubs: false,
|
||||
isRanked: tournament.ctx.settings.isRanked,
|
||||
minMembersPerTeam: tournament.ctx.settings.minMembersPerTeam,
|
||||
maxMembersPerTeam: tournament.ctx.settings.maxMembersPerTeam,
|
||||
regClosesAt: tournament.ctx.settings.regClosesAt,
|
||||
requireInGameNames: tournament.ctx.settings.requireInGameNames,
|
||||
bracketUrl: "https://sendou.ink",
|
||||
|
||||
Reference in New Issue
Block a user