Can unregister from tournament before start

This commit is contained in:
Kalle 2022-04-16 10:52:40 +03:00
parent 695894b1de
commit 93a7ae9c1e
4 changed files with 83 additions and 25 deletions

View File

@ -1,11 +1,15 @@
import { Form, useTransition } from "@remix-run/react";
import { Form, useMatches, useTransition } from "@remix-run/react";
import { tournamentHasNotStarted } from "~/core/tournament/validators";
import { useUser } from "~/hooks/common";
import { FindTournamentByNameForUrlI } from "~/services/tournament";
import { Avatar } from "../Avatar";
import { Button } from "../Button";
import { SubmitButton } from "../SubmitButton";
export function TeamRoster({
team,
deleteMode: deleteMode,
showUnregister = false,
deleteMode = false,
}: {
team: {
id: string;
@ -21,19 +25,51 @@ export function TeamRoster({
}[];
};
deleteMode?: boolean;
showUnregister?: boolean;
}) {
const [, parentRoute] = useMatches();
const tournament = parentRoute.data as FindTournamentByNameForUrlI;
const user = useUser();
const showDeleteButtons = (userToDeleteId: string) => {
// TODO:
const tournamentHasStarted = false;
return (
tournamentHasNotStarted(tournament) &&
deleteMode &&
userToDeleteId !== user?.id
);
};
return !tournamentHasStarted && deleteMode && userToDeleteId !== user?.id;
const showUnregisterButton = () => {
return tournamentHasNotStarted(tournament) && showUnregister;
};
return (
<div className="teams-tab__team-container">
<div className="teams-tab__team-name">{team.name}</div>
<div className="teams-tab__team-name">
{team.name}
{showUnregisterButton() ? (
<Form method="post" className="flex justify-center">
<input type="hidden" name="_action" value="UNREGISTER" />
<input type="hidden" name="teamId" value={team.id} />
<SubmitButton
actionType="UNREGISTER"
tiny
variant="minimal-destructive"
loadingText="Unregistering..."
onClick={(e) => {
if (
!confirm(`Unregister ${team.name} from ${tournament.name}?`)
) {
e.preventDefault();
}
}}
data-cy="unregister-button"
>
Unregister
</SubmitButton>
</Form>
) : null}
</div>
<div className="teams-tab__members-container">
{team.members
.sort((a, b) => Number(b.captain) - Number(a.captain))

View File

@ -7,6 +7,7 @@ import {
redirect,
} from "@remix-run/node";
import { useActionData, useLoaderData, useLocation } from "@remix-run/react";
import invariant from "tiny-invariant";
import { z } from "zod";
import { AddPlayers } from "~/components/AddPlayers";
import { Alert } from "~/components/Alert";
@ -16,6 +17,7 @@ import { TOURNAMENT_TEAM_ROSTER_MAX_SIZE } from "~/constants";
import {
isCaptainOfTheTeam,
teamHasNotCheckedIn,
tournamentHasNotStarted,
tournamentTeamIsNotFull,
} from "~/core/tournament/validators";
import * as Tournament from "~/models/Tournament.server";
@ -42,6 +44,10 @@ const actionSchema = z.union([
userId: z.string().uuid(),
teamId: z.string().uuid(),
}),
z.object({
_action: z.literal("UNREGISTER"),
teamId: z.string().uuid(),
}),
]);
type ActionData = {
@ -59,18 +65,14 @@ export const action: ActionFunction = async ({
});
const user = requireUser(context);
const tournamentTeam = await TournamentTeam.findById(data.teamId);
validate(tournamentTeam, "Invalid tournament team id");
validate(isCaptainOfTheTeam(user, tournamentTeam), "Not captain of the team");
switch (data._action) {
case "ADD_PLAYER": {
try {
const tournamentTeam = await TournamentTeam.findById(data.teamId);
// TODO: Validate if tournament already started / concluded (depending on if tournament allows mid-event roster additions)
validate(tournamentTeam, "Invalid tournament team id");
validate(tournamentTeamIsNotFull(tournamentTeam), "Team is full");
validate(
isCaptainOfTheTeam(user, tournamentTeam),
"Not captain of the team"
);
await TournamentTeamMember.joinTeam({
tournamentId: tournamentTeam.tournament.id,
@ -91,14 +93,7 @@ export const action: ActionFunction = async ({
return { ok: "ADD_PLAYER" };
}
case "DELETE_PLAYER": {
const tournamentTeam = await TournamentTeam.findById(data.teamId);
validate(tournamentTeam, "Invalid team id");
validate(data.userId !== user.id, "Can't remove self");
validate(
isCaptainOfTheTeam(user, tournamentTeam),
"Not captain of the team"
);
validate(
teamHasNotCheckedIn(tournamentTeam),
"Can't remove players after checking in"
@ -111,6 +106,16 @@ export const action: ActionFunction = async ({
return { ok: "DELETE_PLAYER" };
}
case "UNREGISTER": {
const tournament = await Tournament.findById(tournamentTeam.tournamentId);
invariant(tournament, "!tournament");
validate(tournamentHasNotStarted(tournament), "Tournament is ongoing");
await TournamentTeam.unregister(data.teamId);
return { ok: "UNREGISTER" };
}
default: {
const exhaustive: never = data;
throw new Response(`Unknown action: ${JSON.stringify(exhaustive)}`, {
@ -173,7 +178,11 @@ export default function ManageTeamPage() {
</Alert>
)}
<div className="tournament__manage-team__roster-container">
<TeamRoster team={ownTeam} deleteMode={!ownTeam.checkedInTime} />
<TeamRoster
team={ownTeam}
deleteMode={!ownTeam.checkedInTime}
showUnregister
/>
</div>
{ownTeam.members.length < TOURNAMENT_TEAM_ROSTER_MAX_SIZE && (
<AddPlayers

View File

@ -242,10 +242,12 @@
.teams-tab__team-name {
display: flex;
flex-direction: column;
align-self: center;
border-block-end: 2px solid var(--theme);
font-size: var(--fonts-lg);
font-weight: var(--extra-bold);
gap: var(--s-2);
justify-self: center;
}

View File

@ -7,14 +7,17 @@ describe("Before tournament starts", () => {
).as("tournaments");
});
it("Registers a new team", () => {
function registerToTournament(teamName: string) {
cy.logIn("sendou");
cy.visit("/to/sendou/in-the-zone-x");
cy.title().should("include", "In The Zone X");
cy.getCy("register-button").click();
cy.getCy("team-name-input").type("Team Olive");
cy.getCy("team-name-input").type(teamName);
cy.getCy("register-submit-button").click();
cy.contains("Team name already taken");
}
it("Registers a new team", () => {
registerToTournament("Team Olive");
cy.wait("@tournaments");
cy.getCy("team-name-input").clear().type("Team Olive V2");
@ -33,4 +36,12 @@ describe("Before tournament starts", () => {
"map-pool__stage-image-disabled"
);
});
it("Can unregister from tournament", () => {
registerToTournament("Team Olive V2");
cy.on("window:confirm", () => true);
cy.getCy("unregister-button").click();
cy.getCy("register-button");
});
});