mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-03 00:27:30 -05:00
leave team
This commit is contained in:
@@ -43,8 +43,6 @@ const CreateNewTeamModal = () => {
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
console.log(getError());
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setIsOpen(true)}>
|
||||
|
||||
50
components/t/TeamManagementModal.tsx
Normal file
50
components/t/TeamManagementModal.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
} from "@chakra-ui/react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { useState } from "react";
|
||||
import { FiUsers } from "react-icons/fi";
|
||||
|
||||
const TeamManagementModal = ({}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Button leftIcon={<FiUsers />} onClick={() => setIsOpen(true)}>
|
||||
<Trans>Manage team</Trans>
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<Modal isOpen onClose={() => setIsOpen(false)} size="xl">
|
||||
<ModalOverlay>
|
||||
<ModalContent>
|
||||
<ModalHeader>
|
||||
<Trans>Managing team</Trans>
|
||||
</ModalHeader>
|
||||
<ModalCloseButton borderRadius="50%" />
|
||||
<ModalBody pb={6}>
|
||||
{/* <FormControl isInvalid={!!getError() && buttonClicked}>
|
||||
<FormLabel htmlFor="teamName">
|
||||
<Trans>Team name</Trans>
|
||||
</FormLabel>
|
||||
<Input
|
||||
name="teamName"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
<FormErrorMessage>{getError()}</FormErrorMessage>
|
||||
</FormControl> */}
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</ModalOverlay>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamManagementModal;
|
||||
14
lib/validators/team.ts
Normal file
14
lib/validators/team.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as z from "zod";
|
||||
|
||||
const TEAM_BIO_CHARACTER_LIMIT = 7000;
|
||||
const TEAM_RECRUITING_POST_CHARACTER_LIMIT = 2000;
|
||||
|
||||
export const teamSchema = z.object({
|
||||
twitterName: z.string().max(15).optional().nullable(),
|
||||
bio: z.string().max(TEAM_BIO_CHARACTER_LIMIT).optional().nullable(),
|
||||
recruitingPost: z
|
||||
.string()
|
||||
.max(TEAM_RECRUITING_POST_CHARACTER_LIMIT)
|
||||
.optional()
|
||||
.nullable(),
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { getMySession } from "lib/getMySession";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const joinHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
await postHandler(req, res);
|
||||
@@ -50,4 +50,4 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
export default handler;
|
||||
export default joinHandler;
|
||||
|
||||
35
pages/api/teams/leave.ts
Normal file
35
pages/api/teams/leave.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { getMySession } from "lib/getMySession";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
const leaveHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
switch (req.method) {
|
||||
case "POST":
|
||||
await postHandler(req, res);
|
||||
break;
|
||||
default:
|
||||
res.status(405).end();
|
||||
}
|
||||
};
|
||||
|
||||
async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const user = await getMySession(req);
|
||||
if (!user) return res.status(401).end();
|
||||
|
||||
const userFromDb = await prisma.user.findUnique({
|
||||
where: { id: user.id },
|
||||
include: { team: true },
|
||||
});
|
||||
if (!userFromDb?.team || userFromDb.team.captainId === user.id) {
|
||||
return res.status(400).end();
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: user.id },
|
||||
data: { team: { disconnect: true } },
|
||||
});
|
||||
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
export default leaveHandler;
|
||||
@@ -1,15 +1,80 @@
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import { Box, Button, Heading, useToast } from "@chakra-ui/react";
|
||||
import { t, Trans } from "@lingui/macro";
|
||||
import MyContainer from "components/common/MyContainer";
|
||||
import TeamManagementModal from "components/t/TeamManagementModal";
|
||||
import { getEmojiFlag } from "countries-list";
|
||||
import { getToastOptions } from "lib/getToastOptions";
|
||||
import { sendData } from "lib/postData";
|
||||
import useUser from "lib/useUser";
|
||||
import { GetStaticPaths, GetStaticProps } from "next";
|
||||
import { getTeam, GetTeamData } from "prisma/queries/getTeam";
|
||||
import { useState } from "react";
|
||||
import { FiTrash } from "react-icons/fi";
|
||||
|
||||
interface Props {
|
||||
team: GetTeamData;
|
||||
}
|
||||
|
||||
const TeamPage: React.FC<Props> = (props) => {
|
||||
const [sending, setSending] = useState(false);
|
||||
const [user] = useUser();
|
||||
const toast = useToast();
|
||||
|
||||
const team = props.team!;
|
||||
console.log({ team });
|
||||
return <Box>{team.name}</Box>;
|
||||
|
||||
const leaveTeam = async () => {
|
||||
if (!window.confirm(t`Leave the team?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSending(true);
|
||||
|
||||
const success = await sendData("POST", "/api/teams/leave");
|
||||
setSending(false);
|
||||
if (!success) return;
|
||||
|
||||
//mutate("/api/freeagents");
|
||||
|
||||
toast(getToastOptions(t`Left the team`, "success"));
|
||||
};
|
||||
|
||||
return (
|
||||
<MyContainer>
|
||||
<Heading fontFamily="'Rubik', sans-serif" textAlign="center">
|
||||
{team.name}
|
||||
</Heading>
|
||||
<Box textAlign="center">
|
||||
{team.roster
|
||||
.reduce((acc: [string, number][], cur) => {
|
||||
if (!cur.profile?.country) return acc;
|
||||
|
||||
const countryTuple = acc.find(
|
||||
([country]) => country === cur.profile?.country
|
||||
);
|
||||
if (!countryTuple) acc.push([cur.profile.country, 1]);
|
||||
else countryTuple[1]++;
|
||||
|
||||
return acc;
|
||||
}, [])
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([country]) => getEmojiFlag(country))}
|
||||
</Box>
|
||||
{user?.id === team.captainId && <TeamManagementModal />}
|
||||
{user &&
|
||||
user.id !== team.captainId &&
|
||||
team.roster.some((teamMember) => user.id === teamMember.id) && (
|
||||
<Button
|
||||
leftIcon={<FiTrash />}
|
||||
variant="outline"
|
||||
colorScheme="red"
|
||||
onClick={leaveTeam}
|
||||
isLoading={sending}
|
||||
>
|
||||
<Trans>Leave team</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</MyContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ export const getTeam = async (nameForUrl: string) =>
|
||||
captainId: true,
|
||||
roster: {
|
||||
select: {
|
||||
id: true,
|
||||
discordId: true,
|
||||
discordAvatar: true,
|
||||
username: true,
|
||||
|
||||
Reference in New Issue
Block a user