From 72d89cef0aa8491d3265a7a0351d8e2f43d1f9f9 Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Sun, 28 Nov 2021 14:21:55 +0200 Subject: [PATCH] Register to tournament action --- app/root.tsx | 7 +- app/routes/to/$organization.$tournament.tsx | 47 +++++----- .../to/$organization.$tournament/map-pool.tsx | 4 +- .../to/$organization.$tournament/register.tsx | 87 +++++++++++++++++++ .../to/$organization.$tournament/teams.tsx | 4 +- app/services/tournament.ts | 6 +- app/styles/global.css | 3 +- .../{map-pool.css => tournament-map-pool.css} | 0 app/styles/tournament-register.css | 22 +++++ .../{teams.css => tournament-teams.css} | 0 app/utils.ts | 18 ++++ 11 files changed, 163 insertions(+), 35 deletions(-) create mode 100644 app/routes/to/$organization.$tournament/register.tsx rename app/styles/{map-pool.css => tournament-map-pool.css} (100%) create mode 100644 app/styles/tournament-register.css rename app/styles/{teams.css => tournament-teams.css} (100%) diff --git a/app/root.tsx b/app/root.tsx index e961b1cb3..0a2501901 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -15,6 +15,7 @@ import normalizeStylesUrl from "~/styles/normalize.css"; import globalStylesUrl from "~/styles/global.css"; import layoutStylesUrl from "~/styles/layout.css"; import { Layout } from "./components/Layout"; +import type { LoggedInUser } from "./utils"; export const links: LinksFunction = () => { return [ @@ -30,11 +31,7 @@ export const loader: LoaderFunction = ({ context }) => { return user ?? null; }; -type LoggedInUser = { - id: number; - discordId: string; - discordAvatar: string; -} | null; +export let unstable_shouldReload = () => false; const UserContext = React.createContext(null); diff --git a/app/routes/to/$organization.$tournament.tsx b/app/routes/to/$organization.$tournament.tsx index 15b07c5ac..d8a665a72 100644 --- a/app/routes/to/$organization.$tournament.tsx +++ b/app/routes/to/$organization.$tournament.tsx @@ -7,7 +7,7 @@ import { NavLink, Link, } from "remix"; -import { useLoaderData, Outlet } from "remix"; +import { useLoaderData, Outlet, useLocation } from "remix"; import invariant from "tiny-invariant"; import { DiscordIcon } from "~/components/icons/Discord"; import { TwitterIcon } from "~/components/icons/Twitter"; @@ -49,30 +49,35 @@ export const meta: MetaFunction = (props) => { export default function TournamentPage() { const data = useLoaderData(); + const location = useLocation(); + + const displayNavLinks = !location.pathname.endsWith("register"); return (
-
} - className="tournament__links-container" - > - - Overview - - - Map Pool - - - Bracket - - - Teams ({data.teams.length}) - - - Streams (4) - -
+ {displayNavLinks && ( +
} + className="tournament__links-container" + > + + Overview + + + Map Pool + + + Bracket + + + Teams ({data.teams.length}) + + + Streams (4) + +
+ )}
); diff --git a/app/routes/to/$organization.$tournament/map-pool.tsx b/app/routes/to/$organization.$tournament/map-pool.tsx index 48c51c397..1ca4e0ad3 100644 --- a/app/routes/to/$organization.$tournament/map-pool.tsx +++ b/app/routes/to/$organization.$tournament/map-pool.tsx @@ -1,4 +1,4 @@ -import stylesUrl from "~/styles/map-pool.css"; +import styles from "~/styles/tournament-map-pool.css"; import type { Mode } from ".prisma/client"; import classNames from "classnames"; import { modesShort, stages } from "~/constants"; @@ -6,7 +6,7 @@ import { LinksFunction, useMatches } from "remix"; import { FindTournamentByNameForUrlI } from "~/services/tournament"; export const links: LinksFunction = () => { - return [{ rel: "stylesheet", href: stylesUrl }]; + return [{ rel: "stylesheet", href: styles }]; }; export default function MapPoolTab() { diff --git a/app/routes/to/$organization.$tournament/register.tsx b/app/routes/to/$organization.$tournament/register.tsx new file mode 100644 index 000000000..55a416041 --- /dev/null +++ b/app/routes/to/$organization.$tournament/register.tsx @@ -0,0 +1,87 @@ +import styles from "~/styles/tournament-register.css"; +import { + Form, + redirect, + LinksFunction, + ActionFunction, + useMatches, +} from "remix"; +import invariant from "tiny-invariant"; +import { + createTournamentTeam, + FindTournamentByNameForUrlI, +} from "~/services/tournament"; +import { requireUser } from "~/utils"; + +export const links: LinksFunction = () => { + return [{ rel: "stylesheet", href: styles }]; +}; + +const TEAM_NAME_MIN_LENGTH = 2; +const TEAM_NAME_MAX_LENGTH = 40; + +export const action: ActionFunction = async ({ request, context }) => { + const formData = await request.formData(); + const teamName = formData.get("team-name"); + const tournamentId = Number(formData.get("tournament-id")); + invariant(typeof teamName === "string", "Invalid type for team name."); + invariant( + typeof tournamentId === "number", + "Invalid type for tournament id." + ); + + if ( + teamName.length < TEAM_NAME_MIN_LENGTH || + teamName.length > TEAM_NAME_MAX_LENGTH + ) { + throw new Error( + `Team name length has to be between min ${TEAM_NAME_MIN_LENGTH} and max ${TEAM_NAME_MAX_LENGTH}. Was ${teamName.length}.` + ); + } + + const user = requireUser(context); + + // TODO: validate can register for tournament + + await createTournamentTeam({ + teamName, + tournamentId, + userId: user.id, + }); + + // TODO: redirect to add players to page + return redirect("/"); +}; + +// TODO: redirect if not logged in + +export default function RegisterPage() { + const [, parentRoute] = useMatches(); + const { id } = parentRoute.data as FindTournamentByNameForUrlI; + + return ( +
+

Register now

+
+
+ + + + {/* */} +
+ + +
+ +
+
+ ); +} diff --git a/app/routes/to/$organization.$tournament/teams.tsx b/app/routes/to/$organization.$tournament/teams.tsx index 762d4869d..8d7ca5507 100644 --- a/app/routes/to/$organization.$tournament/teams.tsx +++ b/app/routes/to/$organization.$tournament/teams.tsx @@ -1,10 +1,10 @@ import { useMatches, LinksFunction } from "remix"; import type { FindTournamentByNameForUrlI } from "~/services/tournament"; -import stylesUrl from "~/styles/teams.css"; +import styles from "~/styles/tournament-teams.css"; import { Fragment } from "react"; export const links: LinksFunction = () => { - return [{ rel: "stylesheet", href: stylesUrl }]; + return [{ rel: "stylesheet", href: styles }]; }; export default function TeamsTab() { diff --git a/app/services/tournament.ts b/app/services/tournament.ts index b20998dbc..887aec6d0 100644 --- a/app/services/tournament.ts +++ b/app/services/tournament.ts @@ -91,16 +91,16 @@ function discordInviteToUrl(discordInvite: string) { export function createTournamentTeam({ userId, - name, + teamName, tournamentId, }: { userId: number; - name: string; + teamName: string; tournamentId: number; }) { return prisma.tournamentTeam.create({ data: { - name: name.trim(), + name: teamName.trim(), tournamentId, members: { create: { diff --git a/app/styles/global.css b/app/styles/global.css index 2897b0944..60a225f2c 100644 --- a/app/styles/global.css +++ b/app/styles/global.css @@ -110,9 +110,8 @@ button.outlined { input:not(.plain) { height: 1rem; padding: var(--s-4) var(--s-3); - border: none; border: 1px solid var(--border); - background-color: var(--bg-lighter); + background-color: transparent; border-radius: var(--rounded); color: var(--text); outline: none; diff --git a/app/styles/map-pool.css b/app/styles/tournament-map-pool.css similarity index 100% rename from app/styles/map-pool.css rename to app/styles/tournament-map-pool.css diff --git a/app/styles/tournament-register.css b/app/styles/tournament-register.css new file mode 100644 index 000000000..7575deccf --- /dev/null +++ b/app/styles/tournament-register.css @@ -0,0 +1,22 @@ +.tournament__register__container { + padding-block-start: var(--s-4); +} + +.tournament__register__header { + margin-top: 0; + font-size: var(--fonts-lg); + font-weight: var(--bold); + list-style: none; + user-select: none; +} + +.tournament__register__content { + width: min(24rem, 100%); + margin-block-start: var(--s-4); +} + +.tournament__register__buttons-container { + display: flex; + gap: var(--s-2); + margin-block-start: var(--s-6); +} diff --git a/app/styles/teams.css b/app/styles/tournament-teams.css similarity index 100% rename from app/styles/teams.css rename to app/styles/tournament-teams.css diff --git a/app/utils.ts b/app/utils.ts index 0a896973b..d499fb3e1 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -1,5 +1,23 @@ +import { json } from "remix"; + export const makeTitle = (endOfTitle: string) => `sendou.ink | ${endOfTitle}`; +export const requireUser = (ctx: any) => { + const user = ctx.user; + + if (!user) { + throw json("Log in required", { status: 401 }); + } + + return user as NonNullable; +}; + +export type LoggedInUser = { + id: number; + discordId: string; + discordAvatar: string; +} | null; + export type Serialized = { [P in keyof T]: T[P] extends Date ? string : Serialized; };