Add strike creation capabilities

This commit is contained in:
Regalijan 2024-02-28 14:20:58 -05:00
parent 3e06541b30
commit 0334dc1678
Signed by: regalijan
GPG Key ID: 5D4196DA269EF520
2 changed files with 255 additions and 2 deletions

View File

@ -325,7 +325,8 @@ export default function () {
<TableContainer mt="16px"> <TableContainer mt="16px">
<Table variant="simple"> <Table variant="simple">
<TableCaption> <TableCaption>
Points are updated at the end of the month Click/tap on a user's points count to change their points, their
user id to see and manage strikes.
</TableCaption> </TableCaption>
<Thead> <Thead>
<Tr> <Tr>
@ -339,7 +340,19 @@ export default function () {
<Tbody> <Tbody>
{memberData.map((member) => ( {memberData.map((member) => (
<Tr> <Tr>
<Td>{member.id}</Td> <Td>
{isManagement ? (
<Link
onClick={() =>
location.assign(`/et-members/strikes/${member.id}`)
}
>
{member.id}
</Link>
) : (
member.id
)}
</Td>
<Td>{member.name}</Td> <Td>{member.name}</Td>
<Td>{member.roblox_id}</Td> <Td>{member.roblox_id}</Td>
<Td> <Td>

View File

@ -0,0 +1,240 @@
import {
Button,
Container,
Heading,
Link,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Table,
TableContainer,
Tbody,
Td,
Text,
Textarea,
Th,
Thead,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
import { useState } from "react";
export async function loader({
context,
params,
}: {
context: RequestContext;
params: LoaderFunctionArgs & { uid: string };
}) {
const { current_user: user } = context.data;
if (!user)
throw new Response(null, {
status: 401,
});
if (![1 << 3, 1 << 4, 1 << 12].find((p) => user.permissions & p))
throw new Response(null, {
status: 403,
});
const strikeData = await context.env.D1.prepare(
"SELECT * FROM et_strikes WHERE user = ?;",
)
.bind(params.uid)
.all();
return {
can_manage: Boolean([1 << 4, 1 << 12].find((p) => user.permissions & p)),
strikes: strikeData.results,
user: params.uid,
};
}
export default function () {
const { can_manage, strikes, user } = useLoaderData<typeof loader>();
const [strikeData, setStrikeData] = useState(strikes);
const toast = useToast();
const [rmStrikeId, setRmStrikeId] = useState("");
const [strikeReason, setStrikeReason] = useState("");
async function removeStrike(id: string) {
const removeResp = await fetch(`/api/events-team/strikes/${id}`, {
method: "DELETE",
});
if (!removeResp.ok) {
let msg = "Unknown error";
try {
msg = ((await removeResp.json()) as { error: string }).error;
} catch {}
toast({
description: msg,
status: "error",
title: "Failed to remove strike",
});
return;
}
toast({
description: `Strike ${id} was removed`,
status: "success",
title: "Strike Removed",
});
setStrikeData(strikeData.filter((strike) => strike.id !== id));
closeRmStrike();
}
async function addStrike() {
const addStrikeResp = await fetch("/api/events-team/strikes/new", {
body: JSON.stringify({
reason: strikeReason,
user,
}),
headers: {
"content-type": "application/json",
},
method: "POST",
});
if (!addStrikeResp.ok) {
let msg = "Unknown error";
try {
msg = ((await addStrikeResp.json()) as { error: string }).error;
} catch {}
toast({
description: msg,
status: "error",
title: "Failed to add strike",
});
return;
}
toast({
description: "Strike added",
status: "success",
title: "Success",
});
const newStrikeData = strikeData;
newStrikeData.push(await addStrikeResp.json());
setStrikeData(newStrikeData);
closeAddStrike();
}
const {
isOpen: rmStrikeOpen,
onClose: closeRmStrike,
onOpen: openRmStrike,
} = useDisclosure();
const {
isOpen: addStrikeOpen,
onClose: closeAddStrike,
onOpen: openAddStrike,
} = useDisclosure();
return (
<Container maxW="container.lg">
<Modal isOpen={rmStrikeOpen} onClose={closeRmStrike}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Remove Strike</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text>Are you sure you want to remove this strike?</Text>
</ModalBody>
<ModalFooter>
<Button mr="8px">No</Button>
<Button
colorScheme="red"
onClick={async () => await removeStrike(rmStrikeId)}
>
Yes
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Modal isOpen={addStrikeOpen} onClose={closeAddStrike}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Add Strike</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Heading mb="8px" size="xs">
Reason
</Heading>
<Textarea
onChange={(e) => setStrikeReason(e.target.value)}
placeholder="Strike reason"
value={strikeReason}
/>
</ModalBody>
<ModalFooter>
<Button
mr="8px"
onClick={() => {
closeAddStrike();
setStrikeReason("");
}}
>
Cancel
</Button>
<Button colorScheme="red" onClick={async () => await addStrike()}>
Add Strike
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Heading my="16px">Strikes</Heading>
<TableContainer>
<Table variant="simple">
<Thead>
<Th>Time Added</Th>
<Th>Added By</Th>
<Th>Reason</Th>
<Th>Remove</Th>
</Thead>
<Tbody>
{strikeData.map((strike: { [k: string]: any }) => (
<Tr>
<Td>{new Date(strike.created_at).toUTCString()}</Td>
<Td>{strike.created_by}</Td>
<Td>{strike.reason}</Td>
<Td>
{can_manage ? (
<Link
onClick={() => {
setRmStrikeId(strike.id);
openRmStrike();
}}
>
Remove
</Link>
) : null}
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<Link color="#646cff" onClick={openAddStrike} py="16px">
Add Strike
</Link>
</Container>
);
}