mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 06:58:10 -05:00
Join team action
This commit is contained in:
parent
0a56f72d8b
commit
87e53abf42
|
|
@ -48,3 +48,6 @@ export const navItems = [
|
|||
export const ADMIN_TEST_UUID = "846e12eb-d373-4002-a0c3-e23077e1c88c";
|
||||
export const ADMIN_TEST_DISCORD_ID = "79237403620945920";
|
||||
export const ADMIN_TEST_AVATAR = "fcfd65a3bea598905abb9ca25296816b";
|
||||
|
||||
export const TOURNAMENT_TEAM_ROSTER_MIN_SIZE = 4;
|
||||
export const TOURNAMENT_TEAM_ROSTER_MAX_SIZE = 6;
|
||||
|
|
|
|||
|
|
@ -6,16 +6,37 @@ import {
|
|||
useNavigate,
|
||||
useMatches,
|
||||
} from "remix";
|
||||
import type { LoaderFunction, LinksFunction } from "remix";
|
||||
import { findTournamentWithInviteCodes } from "~/services/tournament";
|
||||
import type { LoaderFunction, LinksFunction, ActionFunction } from "remix";
|
||||
import {
|
||||
FindTournamentByNameForUrlI,
|
||||
findTournamentWithInviteCodes,
|
||||
joinTeam,
|
||||
} from "~/services/tournament";
|
||||
import styles from "~/styles/tournament-join-team.css";
|
||||
import invariant from "tiny-invariant";
|
||||
import { getUser } from "~/utils";
|
||||
import { getUser, requireUser } from "~/utils";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
};
|
||||
|
||||
export const action: ActionFunction = async ({ request, context, params }) => {
|
||||
const formData = await request.formData();
|
||||
const inviteCode = formData.get("inviteCode");
|
||||
const tournamentId = formData.get("tournamentId");
|
||||
invariant(typeof inviteCode === "string", "Invalid type for inviteCode.");
|
||||
invariant(
|
||||
typeof tournamentId === "string",
|
||||
"Invalid type for tournament id."
|
||||
);
|
||||
|
||||
const user = requireUser(context);
|
||||
|
||||
await joinTeam({ inviteCode, userId: user.id, tournamentId: tournamentId });
|
||||
|
||||
return redirect(`/to/${params.organization}/${params.tournament}/teams`);
|
||||
};
|
||||
|
||||
const INVITE_CODE_LENGTH = 36;
|
||||
|
||||
type ResponseObject =
|
||||
|
|
@ -24,7 +45,7 @@ type ResponseObject =
|
|||
| { status: "LOG_IN" }
|
||||
| { status: "ALREADY_JOINED"; teamName: string }
|
||||
| { status: "INVALID" }
|
||||
| { status: "OK"; teamName: string; inviterName: string };
|
||||
| { status: "OK"; teamName: string; inviterName: string; inviteCode: string };
|
||||
|
||||
const typedJson = (args: ResponseObject) => json(args);
|
||||
|
||||
|
|
@ -87,6 +108,7 @@ export const loader: LoaderFunction = async ({ request, params, context }) => {
|
|||
return typedJson({
|
||||
status: "OK",
|
||||
teamName: teamInvitedTo.name,
|
||||
inviteCode,
|
||||
inviterName: teamInvitedTo.members.find(({ captain }) => captain)!.member
|
||||
.discordName,
|
||||
});
|
||||
|
|
@ -105,6 +127,7 @@ export default function JoinTeamPage() {
|
|||
function Contents({ data }: { data: ResponseObject }) {
|
||||
const navigate = useNavigate();
|
||||
const [, parentRoute] = useMatches();
|
||||
const parentRouteData = parentRoute.data as FindTournamentByNameForUrlI;
|
||||
|
||||
switch (data.status) {
|
||||
case "NO_CODE":
|
||||
|
|
@ -140,6 +163,12 @@ function Contents({ data }: { data: ResponseObject }) {
|
|||
{data.inviterName} invited you to join {data.teamName} for this
|
||||
tournament. Accept invite?
|
||||
<Form method="post">
|
||||
<input
|
||||
type="hidden"
|
||||
name="tournamentId"
|
||||
value={parentRouteData.id}
|
||||
/>
|
||||
<input type="hidden" name="inviteCode" value={data.inviteCode} />
|
||||
<div className="tournament__join-team__buttons">
|
||||
<button type="submit">Join</button>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ import * as React from "react";
|
|||
import { LinksFunction, useMatches } from "remix";
|
||||
import { Alert } from "~/components/Alert";
|
||||
import { TeamRoster } from "~/components/tournament/TeamRoster";
|
||||
import {
|
||||
TOURNAMENT_TEAM_ROSTER_MAX_SIZE,
|
||||
TOURNAMENT_TEAM_ROSTER_MIN_SIZE,
|
||||
} from "~/constants";
|
||||
import { FindTournamentByNameForUrlI } from "~/services/tournament";
|
||||
import styles from "~/styles/tournament-manage-roster.css";
|
||||
|
||||
|
|
@ -43,7 +47,8 @@ export default function ManageRosterPage() {
|
|||
<div className="tournament__manage-roster">
|
||||
{ownTeam.members.length < 4 && (
|
||||
<Alert type="warning">
|
||||
You need at least 4 players in your roster to play (max 6)
|
||||
You need at least {TOURNAMENT_TEAM_ROSTER_MIN_SIZE} players in your
|
||||
roster to play (max {TOURNAMENT_TEAM_ROSTER_MAX_SIZE})
|
||||
</Alert>
|
||||
)}
|
||||
<TeamRoster team={ownTeam} />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Prisma } from ".prisma/client";
|
||||
import * as React from "react";
|
||||
import {
|
||||
ActionFunction,
|
||||
Form,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useMatches, LinksFunction } from "remix";
|
||||
import { useMatches } from "remix";
|
||||
import type { FindTournamentByNameForUrlI } from "~/services/tournament";
|
||||
import { TeamRoster } from "~/components/tournament/TeamRoster";
|
||||
|
||||
|
|
@ -8,48 +8,11 @@ export default function TeamsTab() {
|
|||
|
||||
if (!teams.length) return null;
|
||||
|
||||
const sortedTeams = teams
|
||||
// TODO: user id here
|
||||
.sort(sortOwnTeamsAndFullTeamsFirst(""))
|
||||
.map((team) => {
|
||||
return {
|
||||
...team,
|
||||
members: team.members.sort(sortCaptainFirst),
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="teams-tab">
|
||||
{sortedTeams.map((team) => (
|
||||
{teams.map((team) => (
|
||||
<TeamRoster team={team} key={team.id} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function sortCaptainFirst(a: { captain: boolean }, b: { captain: boolean }) {
|
||||
return Number(b.captain) - Number(a.captain);
|
||||
}
|
||||
|
||||
function sortOwnTeamsAndFullTeamsFirst(userId?: string) {
|
||||
return function (
|
||||
a: { members: { member: { id: string } }[] },
|
||||
b: { members: { member: { id: string } }[] }
|
||||
) {
|
||||
if (userId) {
|
||||
const aSortValue = Number(
|
||||
a.members.some(({ member }) => userId === member.id)
|
||||
);
|
||||
const bSortValue = Number(
|
||||
b.members.some(({ member }) => userId === member.id)
|
||||
);
|
||||
|
||||
if (aSortValue !== bSortValue) return bSortValue - aSortValue;
|
||||
}
|
||||
|
||||
const aSortValue = a.members.length >= 4 ? 1 : 0;
|
||||
const bSortValue = b.members.length >= 4 ? 1 : 0;
|
||||
|
||||
return bSortValue - aSortValue;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { Prisma } from ".prisma/client";
|
||||
import { json } from "remix";
|
||||
import {
|
||||
TOURNAMENT_TEAM_ROSTER_MAX_SIZE,
|
||||
TOURNAMENT_TEAM_ROSTER_MIN_SIZE,
|
||||
} from "~/constants";
|
||||
import { Serialized } from "~/utils";
|
||||
import { db } from "~/utils/db.server";
|
||||
|
||||
|
|
@ -87,6 +91,31 @@ export async function findTournamentByNameForUrl({
|
|||
})),
|
||||
};
|
||||
|
||||
if (userId) {
|
||||
result.teams.sort((teamA, teamB) => {
|
||||
// show team the user is member of first
|
||||
let aSortValue = Number(
|
||||
teamB.members.some(({ member }) => member.id === userId)
|
||||
);
|
||||
let bSortValue = Number(
|
||||
teamA.members.some(({ member }) => member.id === userId)
|
||||
);
|
||||
if (aSortValue !== bSortValue) return aSortValue - bSortValue;
|
||||
|
||||
// TODO: show stronger teams first
|
||||
|
||||
// otherwise let's show full teams first
|
||||
aSortValue = Number(
|
||||
teamB.members.length >= TOURNAMENT_TEAM_ROSTER_MIN_SIZE
|
||||
);
|
||||
bSortValue = Number(
|
||||
teamA.members.length >= TOURNAMENT_TEAM_ROSTER_MIN_SIZE
|
||||
);
|
||||
console.log({ aSortValue, bSortValue });
|
||||
return aSortValue - bSortValue;
|
||||
});
|
||||
}
|
||||
|
||||
result.organizer.twitter = twitterToUrl(result.organizer.twitter);
|
||||
result.organizer.discordInvite = discordInviteToUrl(
|
||||
result.organizer.discordInvite
|
||||
|
|
@ -182,3 +211,36 @@ export function createTournamentTeam({
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function joinTeam({
|
||||
tournamentId,
|
||||
inviteCode,
|
||||
userId,
|
||||
}: {
|
||||
tournamentId: string;
|
||||
inviteCode: string;
|
||||
userId: string;
|
||||
}) {
|
||||
const tournament = await db.tournament.findUnique({
|
||||
where: { id: tournamentId },
|
||||
include: { teams: { include: { members: true } } },
|
||||
});
|
||||
|
||||
if (!tournament) throw json("Invalid tournament id", { status: 400 });
|
||||
|
||||
const tournamentTeamToJoin = tournament.teams.find(
|
||||
(team) => team.inviteCode === inviteCode
|
||||
);
|
||||
if (!tournamentTeamToJoin) throw json("Invalid invite code", { status: 400 });
|
||||
if (tournamentTeamToJoin.members.length >= TOURNAMENT_TEAM_ROSTER_MAX_SIZE) {
|
||||
throw json("Team is already full", { status: 400 });
|
||||
}
|
||||
|
||||
return db.tournamentTeamMember.create({
|
||||
data: {
|
||||
tournamentId,
|
||||
teamId: tournamentTeamToJoin.id,
|
||||
memberId: userId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user