From 97b97dc8159fac33462ea13d77517f43370faa61 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Sun, 10 Mar 2024 14:42:27 +0200
Subject: [PATCH] Auto check in all feature Closes #1687
---
app/db/tables.ts | 1 +
.../calendar/CalendarRepository.server.ts | 3 +
.../calendar/calendar-schemas.server.ts | 1 +
app/features/calendar/routes/calendar.new.tsx | 143 ++++++++++--------
.../routes/to.$id.brackets.tsx | 22 ++-
.../tournament/TournamentRepository.server.ts | 16 +-
6 files changed, 117 insertions(+), 69 deletions(-)
diff --git a/app/db/tables.ts b/app/db/tables.ts
index 1c7b8bbb4..8750731ea 100644
--- a/app/db/tables.ts
+++ b/app/db/tables.ts
@@ -387,6 +387,7 @@ export interface TournamentSettings {
teamsPerGroup?: number;
thirdPlaceMatch?: boolean;
isRanked?: boolean;
+ autoCheckInAll?: boolean;
}
export interface CastedMatchesInfo {
diff --git a/app/features/calendar/CalendarRepository.server.ts b/app/features/calendar/CalendarRepository.server.ts
index 2ca885019..87296fe05 100644
--- a/app/features/calendar/CalendarRepository.server.ts
+++ b/app/features/calendar/CalendarRepository.server.ts
@@ -386,6 +386,7 @@ type CreateArgs = Pick<
bracketProgression: TournamentSettings["bracketProgression"] | null;
teamsPerGroup?: number;
thirdPlaceMatch?: boolean;
+ autoCheckInAll?: boolean;
isRanked?: boolean;
};
export async function create(args: CreateArgs) {
@@ -398,6 +399,7 @@ export async function create(args: CreateArgs) {
teamsPerGroup: args.teamsPerGroup,
thirdPlaceMatch: args.thirdPlaceMatch,
isRanked: args.isRanked,
+ autoCheckInAll: args.autoCheckInAll,
};
tournamentId = (
@@ -468,6 +470,7 @@ export async function update(args: UpdateArgs) {
teamsPerGroup: args.teamsPerGroup,
thirdPlaceMatch: args.thirdPlaceMatch,
isRanked: args.isRanked,
+ autoCheckInAll: args.autoCheckInAll,
};
await trx
diff --git a/app/features/calendar/calendar-schemas.server.ts b/app/features/calendar/calendar-schemas.server.ts
index e8c7a233c..e8dcbeb58 100644
--- a/app/features/calendar/calendar-schemas.server.ts
+++ b/app/features/calendar/calendar-schemas.server.ts
@@ -80,6 +80,7 @@ export const newCalendarEventActionSchema = z
checkboxValueToBoolean,
z.boolean().nullish(),
),
+ autoCheckInAll: z.preprocess(checkboxValueToBoolean, z.boolean().nullish()),
teamsPerGroup: z.coerce
.number()
.min(TOURNAMENT.MIN_GROUP_SIZE)
diff --git a/app/features/calendar/routes/calendar.new.tsx b/app/features/calendar/routes/calendar.new.tsx
index 5b74e60ab..7243839da 100644
--- a/app/features/calendar/routes/calendar.new.tsx
+++ b/app/features/calendar/routes/calendar.new.tsx
@@ -118,6 +118,7 @@ export const action: ActionFunction = async ({ request }) => {
teamsPerGroup: data.teamsPerGroup ?? undefined,
thirdPlaceMatch: data.thirdPlaceMatch ?? undefined,
isRanked: data.isRanked ?? undefined,
+ autoCheckInAll: data.autoCheckInAll ?? undefined,
};
validate(
!commonArgs.toToolsEnabled || commonArgs.bracketProgression,
@@ -892,19 +893,6 @@ function TournamentFormatSelector() {
) : null}
- {format === "RR_TO_SE" ? (
-
-
-
-
- ) : null}
-
{format === "RR_TO_SE" ? (
@@ -922,6 +910,20 @@ function TournamentFormatSelector() {
) : null}
+
+ {format === "RR_TO_SE" ? (
+
+
+
+
+ ) : null}
+
{format === "RR_TO_SE" ? (
) : null}
@@ -931,6 +933,9 @@ function TournamentFormatSelector() {
function FollowUpBrackets({ teamsPerGroup }: { teamsPerGroup: number }) {
const data = useLoaderData();
+ const [autoCheckInAll, setAutoCheckInAll] = React.useState(
+ data.tournamentCtx?.settings.autoCheckInAll ?? false,
+ );
const [_brackets, setBrackets] = React.useState>(
() => {
if (
@@ -958,56 +963,76 @@ function FollowUpBrackets({ teamsPerGroup }: { teamsPerGroup: number }) {
const validationErrorMsg = validateFollowUpBrackets(brackets, teamsPerGroup);
return (
-
-
-
-
- {brackets.map((b, i) => (
-
{
- setBrackets(
- brackets.map((oldBracket, j) =>
- j === i ? newBracket : oldBracket,
- ),
- );
- }}
- bracket={b}
- nth={i + 1}
+ <>
+ {brackets.length > 1 ? (
+
+
+
- ))}
-
-
-
+
+ If disabled, the only follow-up bracket with automatic check-in is
+ the top cut
+
+ ) : null}
+
+
+
+
+ {brackets.map((b, i) => (
+
{
+ setBrackets(
+ brackets.map((oldBracket, j) =>
+ j === i ? newBracket : oldBracket,
+ ),
+ );
+ }}
+ bracket={b}
+ nth={i + 1}
+ />
+ ))}
+
+
+
+
- {validationErrorMsg ? (
- {validationErrorMsg}
- ) : null}
+ {validationErrorMsg ? (
+ {validationErrorMsg}
+ ) : null}
+
-
+ >
);
}
diff --git a/app/features/tournament-bracket/routes/to.$id.brackets.tsx b/app/features/tournament-bracket/routes/to.$id.brackets.tsx
index 6977eb5ed..c071a4738 100644
--- a/app/features/tournament-bracket/routes/to.$id.brackets.tsx
+++ b/app/features/tournament-bracket/routes/to.$id.brackets.tsx
@@ -31,7 +31,7 @@ import * as TournamentRepository from "~/features/tournament/TournamentRepositor
import { HACKY_isInviteOnlyEvent } from "~/features/tournament/tournament-utils";
import { useSearchParamState } from "~/hooks/useSearchParamState";
import { useVisibilityChange } from "~/hooks/useVisibilityChange";
-import { removeDuplicates } from "~/utils/arrays";
+import { nullFilledArray, removeDuplicates } from "~/utils/arrays";
import { parseRequestFormData, validate } from "~/utils/remix";
import { assertUnreachable } from "~/utils/types";
import {
@@ -113,8 +113,17 @@ export const action: ActionFunction = async ({ params, request }) => {
const finalStageIdx = tournament.brackets.findIndex((b) => b.isFinals);
if (finalStageIdx !== -1) {
+ const allFollowUpBracketIdxs = nullFilledArray(
+ tournament.brackets.length,
+ )
+ .map((_, i) => i)
+ // filter out groups stage
+ .filter((i) => i !== 0);
+
await TournamentRepository.checkInMany({
- bracketIdx: finalStageIdx,
+ bracketIdxs: tournament.ctx.settings.autoCheckInAll
+ ? allFollowUpBracketIdxs
+ : [finalStageIdx],
tournamentTeamIds: tournament.ctx.teams
.filter((t) => t.checkIns.length > 0)
.map((t) => t.id),
@@ -259,7 +268,7 @@ export default function TournamentBracketsPage() {
) {
return `Teams that get eliminated in the first ${Math.abs(
Math.min(...(bracket.sources ?? []).flatMap((s) => s.placements)),
- )} rounds of the losers bracket can play in this bracket (optional)`;
+ )} rounds of the losers bracket can play in this bracket`;
}
return null;
@@ -358,6 +367,13 @@ export default function TournamentBracketsPage() {
{teamsSourceText()}
) : null}
+ {bracket.sources &&
+ bracket.sources.every((s) => !s.placements.includes(1)) &&
+ !tournament.ctx.settings.autoCheckInAll ? (
+
+ Bracket requires check-in
+
+ ) : null}
) : null}
diff --git a/app/features/tournament/TournamentRepository.server.ts b/app/features/tournament/TournamentRepository.server.ts
index a1818713b..9d0ceb529 100644
--- a/app/features/tournament/TournamentRepository.server.ts
+++ b/app/features/tournament/TournamentRepository.server.ts
@@ -333,19 +333,21 @@ export function checkOut({
export function checkInMany({
tournamentTeamIds,
- bracketIdx,
+ bracketIdxs,
}: {
tournamentTeamIds: number[];
- bracketIdx: number;
+ bracketIdxs: number[];
}) {
return db
.insertInto("TournamentTeamCheckIn")
.values(
- tournamentTeamIds.map((tournamentTeamId) => ({
- checkedInAt: dateToDatabaseTimestamp(new Date()),
- tournamentTeamId,
- bracketIdx,
- })),
+ tournamentTeamIds.flatMap((tournamentTeamId) =>
+ bracketIdxs.map((bracketIdx) => ({
+ checkedInAt: dateToDatabaseTimestamp(new Date()),
+ tournamentTeamId,
+ bracketIdx,
+ })),
+ ),
)
.execute();
}