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> <Tr> <Th>Time Added</Th> <Th>Added By</Th> <Th>Reason</Th> <Th>Remove</Th> </Tr> </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> ); }