Create tournaments rounds seems to work edition

This commit is contained in:
Kalle (Sendou) 2021-12-22 19:54:13 +02:00
parent 88dc19f450
commit d0b9708510
10 changed files with 75 additions and 29 deletions

View File

@ -24,16 +24,22 @@ export const stages = [
"New Albacore Hotel",
"Ancho-V Games",
"Skipper Pavilion",
];
] as const;
export const modesShort: Mode[] = ["TW", "SZ", "TC", "RM", "CB"];
export const modesShort: readonly Mode[] = [
"TW",
"SZ",
"TC",
"RM",
"CB",
] as const;
export const modesShortToLong: Record<Mode, string> = {
TW: "Turf War",
SZ: "Splat Zones",
TC: "Tower Control",
RM: "Rainmaker",
CB: "Clam Blitz",
};
} as const;
export const navItems = [
{
@ -52,7 +58,7 @@ export const navItems = [
title: "misc",
items: ["badges", "links"],
},
];
] as const;
export const DISCORD_URL = "https://discord.gg/sendou";

View File

@ -1,6 +1,7 @@
import type { BracketType, Stage, TeamOrder } from ".prisma/client";
import { v4 as uuidv4 } from "uuid";
import invariant from "tiny-invariant";
import { v4 as uuidv4 } from "uuid";
import { z } from "zod";
import { TOURNAMENT_TEAM_ROSTER_MIN_SIZE } from "../../constants";
import { FindTournamentByNameForUrlI } from "../../services/tournament";
import { Bracket, eliminationBracket, Match } from "./algorithms";
@ -183,6 +184,12 @@ export function countParticipants(teams: FindTournamentByNameForUrlI["teams"]) {
}, 0);
}
export type MapListIds = z.infer<typeof MapListIdsSchema>;
export const MapListIdsSchema = z.object({
winners: z.array(z.array(z.object({ id: z.number() }))),
losers: z.array(z.array(z.object({ id: z.number() }))),
});
export interface TournamentRoundForDB {
id: string;
position: number;
@ -205,7 +212,7 @@ export function tournamentRoundsForDB({
bracketType,
participantsSeeded,
}: {
mapList: EliminationBracket<Stage[][]>;
mapList: MapListIds;
bracketType: BracketType;
participantsSeeded: { id: string }[];
}): TournamentRoundForDB[] {
@ -221,7 +228,6 @@ export function tournamentRoundsForDB({
const isWinners = sideI === 0;
for (const [roundI, round] of side.entries()) {
const position = isWinners ? roundI + 1 : -(roundI + 1);
const stagesRaw = mapList[isWinners ? "winners" : "losers"][roundI];
invariant(stagesRaw, "stagesRaw is undefined");
const stages = stagesRaw.map((stage, i) => ({

View File

@ -72,6 +72,7 @@ export function CatchBoundary() {
);
}
// TODO.... maybe we can render the same exact thing with added dialog you can close?
export function ErrorBoundary({ error }: { error: Error }) {
console.error(error);
return (

View File

@ -2,11 +2,14 @@ import type { Mode, Stage } from ".prisma/client";
import classNames from "classnames";
import {
ActionFunction,
Form,
json,
LinksFunction,
LoaderFunction,
redirect,
useLoaderData,
useMatches,
} from "remix";
import { Form, json, redirect, useLoaderData } from "remix";
import invariant from "tiny-invariant";
import { Alert } from "~/components/Alert";
import { Button } from "~/components/Button";
@ -14,8 +17,9 @@ import { Catcher } from "~/components/Catcher";
import { modesShort, modesShortToLong } from "~/constants";
import { eliminationBracket } from "~/core/tournament/algorithms";
import {
EliminationBracket,
EliminationBracketSide,
MapListIds,
MapListIdsSchema,
participantCountToRoundsInfo,
} from "~/core/tournament/bracket";
import { useTournamentRounds } from "~/hooks/useTournamentRounds";
@ -44,8 +48,7 @@ export const action: ActionFunction = async ({ request, params, context }) => {
const bracketId = formData.get("bracket-id");
invariant(typeof mapListString === "string", "Type of map list not string");
invariant(typeof bracketId === "string", "Type of bracket id not string");
// TODO: could use Zod here
const mapList = JSON.parse(mapListString) as EliminationBracket<Stage[][]>;
const mapList = MapListIdsSchema.parse(JSON.parse(mapListString));
const organizationNameForUrl = params.organization;
const tournamentNameForUrl = params.tournament;
@ -111,15 +114,17 @@ export default function StartBracketTab() {
// TODO: dropdown to select this
const bracketId = brackets[0].id;
const mapListForInput: MapListIds = {
losers: bracket.winners.map((round) => round.mapList),
winners: bracket.winners.map((round) => round.mapList),
};
return (
<Form method="post" className="width-100">
<input
type="hidden"
name="map-list"
value={JSON.stringify({
losers: bracket.winners.map((round) => round.mapList),
winners: bracket.winners.map((round) => round.mapList),
})}
value={JSON.stringify(mapListForInput)}
/>
<input type="hidden" name="bracket-id" value={bracketId} />
<div className="tournament__start__container">

View File

@ -1,12 +1,9 @@
import type { Prisma, Stage } from ".prisma/client";
import type { Prisma } from ".prisma/client";
import {
TOURNAMENT_CHECK_IN_CLOSING_MINUTES_FROM_START,
TOURNAMENT_TEAM_ROSTER_MAX_SIZE,
} from "~/constants";
import {
EliminationBracket,
tournamentRoundsForDB,
} from "~/core/tournament/bracket";
import { MapListIds, tournamentRoundsForDB } from "~/core/tournament/bracket";
import { isTournamentAdmin } from "~/core/tournament/permissions";
import { sortTeamsBySeed } from "~/core/tournament/utils";
import * as Tournament from "~/models/Tournament";
@ -134,7 +131,7 @@ export async function createTournamentRounds({
}: {
organizationNameForUrl: string;
tournamentNameForUrl: string;
mapList: EliminationBracket<Stage[][]>;
mapList: MapListIds;
userId: string;
bracketId: string;
}) {
@ -156,9 +153,9 @@ export async function createTournamentRounds({
throw new Response("Invalid bracket id provided", { status: 400 });
}
const participantsSeeded = tournament.teams.sort(
sortTeamsBySeed(tournament.seeds)
);
const participantsSeeded = tournament.teams
.filter((team) => team.checkedInTime)
.sort(sortTeamsBySeed(tournament.seeds));
const rounds = tournamentRoundsForDB({
mapList,
@ -176,13 +173,17 @@ export async function createTournamentRounds({
}),
db.tournamentRoundStage.createMany({
data: rounds.flatMap((round) => {
return round.stages.map((stage) => ({ ...stage, roundId: round.id }));
return round.stages.map(({ position, stageId }) => ({
position,
stageId,
roundId: round.id,
}));
}),
}),
db.tournamentMatch.createMany({
data: rounds.flatMap((round) => {
return round.matches.map((match) => ({
...match,
id: match.id,
roundId: round.id,
}));
}),

View File

@ -5,7 +5,7 @@ import * as React from "react";
export const useUser = () => {
const [root] = useMatches();
return root.data.user as LoggedInUser;
return root.data?.user as LoggedInUser;
};
export const useBaseURL = () => {

View File

@ -53,6 +53,10 @@ export async function formDataFromRequest<T extends string>({
return result as Record<T, string>;
}
/** @link https://stackoverflow.com/a/69413184 */
// @ts-expect-error
export const assertType = <A, B extends A>() => {};
export type LoggedInUser = {
id: string;
discordId: string;

8
app/validators/mode.ts Normal file
View File

@ -0,0 +1,8 @@
import { Mode } from ".prisma/client";
import { z } from "zod";
import { assertType, Unpacked } from "~/utils";
type MapList = z.infer<typeof ModeSchema>;
assertType<Unpacked<MapList>, Mode>();
export const ModeSchema = z.enum(["TW", "SZ", "TC", "RM", "CB"]);

16
package-lock.json generated
View File

@ -28,7 +28,8 @@
"react-dom": "^17.0.2",
"remix": "^1.1.1",
"tiny-invariant": "^1.2.0",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"zod": "^3.11.6"
},
"devDependencies": {
"@remix-run/dev": "^1.1.1",
@ -9106,6 +9107,14 @@
"node": ">=6"
}
},
"node_modules/zod": {
"version": "3.11.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.11.6.tgz",
"integrity": "sha512-daZ80A81I3/9lIydI44motWe6n59kRBfNzTuS2bfzVh1nAXi667TOTWWtatxyG+fwgNUiagSj/CWZwRRbevJIg==",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zwitch": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz",
@ -15782,6 +15791,11 @@
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
},
"zod": {
"version": "3.11.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.11.6.tgz",
"integrity": "sha512-daZ80A81I3/9lIydI44motWe6n59kRBfNzTuS2bfzVh1nAXi667TOTWWtatxyG+fwgNUiagSj/CWZwRRbevJIg=="
},
"zwitch": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.2.tgz",

View File

@ -44,7 +44,8 @@
"react-dom": "^17.0.2",
"remix": "^1.1.1",
"tiny-invariant": "^1.2.0",
"uuid": "^8.3.2"
"uuid": "^8.3.2",
"zod": "^3.11.6"
},
"devDependencies": {
"@remix-run/dev": "^1.1.1",