diff --git a/app/hooks/useTournamentRounds/index.ts b/app/hooks/useTournamentRounds/index.ts index 8ffe59840..15bfe46b8 100644 --- a/app/hooks/useTournamentRounds/index.ts +++ b/app/hooks/useTournamentRounds/index.ts @@ -1,26 +1,7 @@ -import { eliminationBracket } from "~/core/tournament/algorithms"; -import { participantCountToRoundsInfo } from "~/core/tournament/bracket"; -import { - UseTournamentRoundsAction, - UseTournamentRoundsState, - UseTournamentsArgs, -} from "./types"; import * as React from "react"; +import { UseTournamentRoundsAction, UseTournamentRoundsState } from "./types"; -const getInitialState = ({ - teams, - type, - mapPool, -}: UseTournamentsArgs): UseTournamentRoundsState => { - const teamCount = teams.reduce( - (acc, cur) => acc + (cur.checkedInTime ? 1 : 0), - 0 - ); - const bracket = eliminationBracket(teamCount, type); - return participantCountToRoundsInfo({ bracket, mapPool }); -}; - -export function useTournamentRounds(args: UseTournamentsArgs) { +export function useTournamentRounds(initialState: UseTournamentRoundsState) { return React.useReducer( (oldState: UseTournamentRoundsState, action: UseTournamentRoundsAction) => { switch (action.type) { @@ -30,6 +11,6 @@ export function useTournamentRounds(args: UseTournamentsArgs) { return oldState; } }, - getInitialState(args) + initialState ); } diff --git a/app/hooks/useTournamentRounds/types.ts b/app/hooks/useTournamentRounds/types.ts index 65ee7c1e1..e749398e3 100644 --- a/app/hooks/useTournamentRounds/types.ts +++ b/app/hooks/useTournamentRounds/types.ts @@ -1,12 +1,6 @@ -import type { BracketType, Stage } from ".prisma/client"; +import type { Stage } from ".prisma/client"; import type { EliminationBracket } from "~/core/tournament/bracket"; -export interface UseTournamentsArgs { - teams: { checkedInTime: string | null }[]; - type: BracketType; - mapPool: Stage[]; -} - export type UseTournamentRoundsState = EliminationBracket< { bestOf: number; diff --git a/app/routes/to/$organization.$tournament/start.tsx b/app/routes/to/$organization.$tournament/start.tsx index cb24ff179..db88c12a1 100644 --- a/app/routes/to/$organization.$tournament/start.tsx +++ b/app/routes/to/$organization.$tournament/start.tsx @@ -1,8 +1,11 @@ -import { useMatches } from "remix"; -import type { LinksFunction } from "remix"; +import type { LinksFunction, LoaderFunction } from "remix"; +import { json, useLoaderData } from "remix"; +import invariant from "tiny-invariant"; +import { eliminationBracket } from "~/core/tournament/algorithms"; +import { participantCountToRoundsInfo } from "~/core/tournament/bracket"; import { useTournamentRounds } from "~/hooks/useTournamentRounds"; import type { UseTournamentRoundsState } from "~/hooks/useTournamentRounds/types"; -import type { FindTournamentByNameForUrlI } from "~/services/tournament"; +import { findTournamentByNameForUrl } from "~/services/tournament"; import startBracketTabStylesUrl from "~/styles/tournament-start.css"; // 2) can change best of -> regens maps @@ -14,19 +17,45 @@ export const links: LinksFunction = () => { return [{ rel: "stylesheet", href: startBracketTabStylesUrl }]; }; -// TODO: handle warning if check-in has not concluded -export default function StartBracketTab() { - const [, parentRoute] = useMatches(); - const { teams, mapPool } = parentRoute.data as FindTournamentByNameForUrlI; - const [rounds, dispatch] = useTournamentRounds({ - mapPool, - teams, - // TODO: and also below don't show losers if SE - type: "DE", +const typedJson = (args: UseTournamentRoundsState) => json(args); + +export const loader: LoaderFunction = async ({ params }) => { + invariant( + typeof params.organization === "string", + "Expected params.organization to be string" + ); + invariant( + typeof params.tournament === "string", + "Expected params.tournament to be string" + ); + + const tournament = await findTournamentByNameForUrl({ + organizationNameForUrl: params.organization, + tournamentNameForUrl: params.tournament, }); + const teamCount = tournament.teams.reduce( + (acc, cur) => acc + (cur.checkedInTime ? 1 : 0), + 0 + ); + + // TODO: and also below don't show losers if SE + const bracket = eliminationBracket(teamCount, "DE"); + const result = participantCountToRoundsInfo({ + bracket, + mapPool: tournament.mapPool, + }); + + return typedJson(result); +}; + +// TODO: handle warning if check-in has not concluded +export default function StartBracketTab() { + const initialState = useLoaderData(); + const [rounds, dispatch] = useTournamentRounds(initialState); + return ( -
+
diff --git a/app/styles/tournament-start.css b/app/styles/tournament-start.css new file mode 100644 index 000000000..53d316a1c --- /dev/null +++ b/app/styles/tournament-start.css @@ -0,0 +1,38 @@ +.tournament__start__container { + width: 100%; +} + +.tournament__start__rounds-container { + display: grid; + gap: var(--s-4); + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); +} + +.tournament__start__round { + padding: var(--s-4); + background-color: var(--bg-lighter); + border-radius: var(--rounded); +} + +.tournament__start__best-of { + color: var(--theme-secondary); + font-size: var(--fonts-xs); + font-weight: var(--semi-bold); +} + +.tournament__start__rounds-list { + padding-top: var(--s-2); + padding-left: 0; + font-size: var(--fonts-sm); +} + +.tournament__start__map-row { + display: flex; + align-items: center; + padding-bottom: var(--s-1); + gap: var(--s-2); +} + +.tournament__start__mode-image { + width: 1.75rem; +}