From 7b196384cc25d09091d00c67d6e4df8bbb9150d6 Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Fri, 17 Dec 2021 22:39:00 +0200 Subject: [PATCH] Edit map list stages functionality --- app/constants.ts | 11 +- app/hooks/useTournamentRounds/index.ts | 64 +++++++++- app/hooks/useTournamentRounds/types.ts | 23 ++++ .../to/$organization.$tournament/start.tsx | 117 +++++++++++++++++- app/styles/tournament-start.css | 7 ++ 5 files changed, 211 insertions(+), 11 deletions(-) diff --git a/app/constants.ts b/app/constants.ts index b5bb3c965..f72803209 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -1,3 +1,5 @@ +import { Mode } from ".prisma/client"; + export const stages = [ "The Reef", "Musselforge Fitness", @@ -24,7 +26,14 @@ export const stages = [ "Skipper Pavilion", ]; -export const modesShort = ["TW", "SZ", "TC", "RM", "CB"]; +export const modesShort: Mode[] = ["TW", "SZ", "TC", "RM", "CB"]; +export const modesShortToLong: Record = { + TW: "Turf War", + SZ: "Splat Zones", + TC: "Tower Control", + RM: "Rainmaker", + CB: "Clam Blitz", +}; export const navItems = [ { diff --git a/app/hooks/useTournamentRounds/index.ts b/app/hooks/useTournamentRounds/index.ts index c617aec54..df02d432f 100644 --- a/app/hooks/useTournamentRounds/index.ts +++ b/app/hooks/useTournamentRounds/index.ts @@ -1,4 +1,6 @@ +import clone from "just-clone"; import * as React from "react"; +import invariant from "tiny-invariant"; import { generateMapListForRounds } from "~/core/tournament/mapList"; import type { UseTournamentRoundsAction, @@ -10,16 +12,70 @@ export function useTournamentRounds(args: UseTournamentRoundsArgs) { return React.useReducer( (oldState: UseTournamentRoundsState, action: UseTournamentRoundsAction) => { switch (action.type) { - case "REGENERATE_MAP_LIST": + case "START_EDITING_ROUND": { + const newState = clone(oldState); + newState[action.data.side] = newState[action.data.side].map( + (round, i) => + i === action.data.index + ? { ...round, editing: true, newMapList: [...round.mapList] } + : round + ); + + return newState; + } + case "CANCEL_EDITING_ROUND": { + const newState = clone(oldState); + newState[action.data.side] = newState[action.data.side].map( + (round, i) => + i === action.data.index ? { ...round, editing: false } : round + ); + + return newState; + } + case "SAVE_ROUND": { + const newState = clone(oldState); + newState[action.data.side] = newState[action.data.side].map( + (round, i) => { + if (i !== action.data.index) return round; + invariant(round.newMapList, "round.newMapList is undefined"); + return { ...round, editing: false, mapList: round.newMapList }; + } + ); + + return newState; + } + case "EDIT_STAGE": { + const newState = clone(oldState); + const roundToEdit = newState[action.data.side].find( + (_, i) => i === action.data.index + ); + invariant( + roundToEdit?.editing, + "roundToEdit is undefined or doesn't have editing attribute" + ); + + let newMapList = roundToEdit.newMapList; + if (!newMapList) { + newMapList = roundToEdit.mapList; + } + + newMapList[action.data.stageNumber - 1] = action.data.newStage; + + return newState; + } + case "REGENERATE_MAP_LIST": { return regenMapList(oldState); - case "SET_ROUND_BEST_OF": - const newState = { ...oldState }; + } + case "SET_ROUND_BEST_OF": { + const newState = clone(oldState); newState[action.data.side][action.data.index].bestOf = action.data.newBestOf; return regenMapList(newState); - default: + } + default: { return oldState; + } } }, args.initialState diff --git a/app/hooks/useTournamentRounds/types.ts b/app/hooks/useTournamentRounds/types.ts index eaf1920de..a22aca4cb 100644 --- a/app/hooks/useTournamentRounds/types.ts +++ b/app/hooks/useTournamentRounds/types.ts @@ -11,6 +11,8 @@ export type UseTournamentRoundsState = EliminationBracket< bestOf: BestOf; name: string; mapList: Stage[]; + newMapList?: Stage[]; + editing?: boolean; }[] >; @@ -21,6 +23,27 @@ export interface UseTournamentRoundsArgs { export type UseTournamentRoundsAction = | MyReducerAction<"REGENERATE_MAP_LIST"> + | MyReducerAction< + "START_EDITING_ROUND", + { side: EliminationBracketSide; index: number } + > + | MyReducerAction< + "EDIT_STAGE", + { + side: EliminationBracketSide; + index: number; + stageNumber: number; + newStage: Stage; + } + > + | MyReducerAction< + "SAVE_ROUND", + { side: EliminationBracketSide; index: number } + > + | MyReducerAction< + "CANCEL_EDITING_ROUND", + { side: EliminationBracketSide; index: number } + > | MyReducerAction< "SET_ROUND_BEST_OF", { newBestOf: BestOf; side: EliminationBracketSide; index: number } diff --git a/app/routes/to/$organization.$tournament/start.tsx b/app/routes/to/$organization.$tournament/start.tsx index dfed948ac..a7d052560 100644 --- a/app/routes/to/$organization.$tournament/start.tsx +++ b/app/routes/to/$organization.$tournament/start.tsx @@ -1,8 +1,10 @@ +import type { Mode, Stage } from ".prisma/client"; import classNames from "classnames"; import type { LinksFunction, LoaderFunction } from "remix"; import { json, useLoaderData } from "remix"; import invariant from "tiny-invariant"; import { Button } from "~/components/Button"; +import { modesShort, modesShortToLong } from "~/constants"; import { eliminationBracket } from "~/core/tournament/algorithms"; import { EliminationBracketSide, @@ -17,6 +19,8 @@ import type { import { findTournamentByNameForUrl } from "~/services/tournament"; import startBracketTabStylesUrl from "~/styles/tournament-start.css"; +// TODO: some warning if trying to continue and some round card is editing +// TODO: add round numbers // 4) can change any invidual map via dropdown (no regen) export const links: LinksFunction = () => { @@ -66,14 +70,14 @@ export default function StartBracketTab() {
{args.initialState.losers.length > 0 && ( )}
@@ -102,13 +106,15 @@ function ActionButtons({ function RoundsCollection({ side, - rounds, dispatch, + rounds, }: { side: EliminationBracketSide; - rounds: UseTournamentRoundsState["winners"]; dispatch: React.Dispatch; + rounds: UseTournamentRoundsState["winners"]; }) { + const { mapPool } = useLoaderData(); + return (

{side}

@@ -141,9 +147,54 @@ function RoundsCollection({ ))}
    - {round.mapList.map((stage) => { + {round.mapList.map((stage, stageI) => { + if (round.editing) { + return ( +
  1. + +
  2. + ); + } return ( -
  3. +
  4. +
    + {round.editing ? ( + <> + + + + ) : ( + + )} +
    ); })} ); + + function stagesForSelect() { + return [...mapPool] + .sort((a, b) => a.id - b.id) + .reduce((acc: Partial>, cur) => { + const stages = acc[cur.mode] ?? []; + stages.push(cur); + acc[cur.mode] = stages; + return acc; + }, {}); + } } diff --git a/app/styles/tournament-start.css b/app/styles/tournament-start.css index cd94f0bb3..abfea7d61 100644 --- a/app/styles/tournament-start.css +++ b/app/styles/tournament-start.css @@ -67,6 +67,13 @@ gap: var(--s-2); } +.tournament__start__round-card-buttons-container { + display: flex; + justify-content: center; + gap: var(--s-4); + padding-block-start: var(--s-4); +} + .tournament__start__mode-image { width: attr(width); }