import { Button, Input, Link, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Radio, RadioGroup, Table, TableContainer, Tbody, Text, Td, Th, Thead, Tr, VStack, useToast, } from "@chakra-ui/react"; import { useState } from "react"; export default function (props: { isOpen: boolean; onClose: () => void }) { const actionMap: { [k: string]: number } = {}; const [rows, setRows] = useState([] as JSX.Element[]); const toast = useToast(); const fileTypes: { [k: string]: string } = { gif: "image/gif", heic: "image/heic", heif: "image/heif", jfif: "image/jpeg", jpeg: "image/jpeg", jpg: "image/jpg", m4v: "video/x-m4v", mkv: "video/x-matroska", mov: "video/quicktime", mp4: "video/mp4", png: "image/png", webp: "image/webp", webm: "video/webm", wmv: "video/x-ms-wmv", }; function addUser(user: string) { const newRows = [...rows]; newRows.push( <Tr key={user}> <Td>{user}</Td> <Td> <RadioGroup onChange={(val) => Object.defineProperty(actionMap, user, { value: parseInt(val), }) } > <VStack> <Radio value="0">Do Nothing</Radio> <Radio value="1">Hide from Leaderboards</Radio> <Radio value="2">Ban</Radio> </VStack> </RadioGroup> </Td> <Td> <Link onClick={() => removeUser(user)}>Remove</Link> </Td> </Tr>, ); setRows(newRows); } function removeUser(user: string) { const newRows = [...rows]; const el = newRows.find((el) => el.key === user); if (!el) return; const elIdx = newRows.indexOf(el); if (elIdx === -1) return; newRows.splice(elIdx, 1); setRows(newRows); delete actionMap[user]; } function reset() { (document.getElementById("username") as HTMLInputElement).value = ""; (document.getElementById("evidence") as HTMLInputElement).value = ""; setRows([]); Object.keys(actionMap).forEach((k) => delete actionMap[k]); props.onClose(); } async function submit() { const actions: number[] = []; const usernames: string[] = []; for (const [u, a] of Object.entries(actionMap)) { actions.push(a); usernames.push(u); } if (!usernames.length || !actions.length) return; const files = (document.getElementById("evidence") as HTMLInputElement) .files; if (!files) return; const [evidence] = files; const submitReq = await fetch("/api/reports/submit", { body: JSON.stringify({ actions, bypass: true, filename: evidence.name, filesize: evidence.size, usernames, }), headers: { "content-type": "application/json", }, method: "POST", }); if (!submitReq.ok) { toast({ description: ((await submitReq.json()) as { error: string }).error, status: "error", title: "Failed to submit report", }); return; } const { id, upload_url }: { [k: string]: string } = await submitReq.json(); const fileUpload = await fetch(upload_url, { body: evidence, headers: { "content-type": evidence.type || fileTypes[ evidence.name.split(".")[evidence.name.split(".").length - 1] ], }, method: "PUT", }); if (!fileUpload.ok) { await fetch("/api/reports/recall", { body: JSON.stringify({ id }), headers: { "content-type": "application/json", }, method: "POST", }); toast({ description: "Failed to upload file", status: "error", title: "Error", }); return; } await fetch("/api/reports/complete", { body: JSON.stringify({ id }), headers: { "content-type": "application/json", }, method: "POST", }); toast({ description: "User moderated", status: "success", title: "Success", }); } return ( <Modal isCentered isOpen={props.isOpen} onClose={props.onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>New Game Ban</ModalHeader> <ModalCloseButton /> <ModalBody> <Text>Username(s)</Text> <Input id="username" mb="8px" placeholder="builderman" /> <Button onClick={function () { const user = ( document.getElementById("username") as HTMLInputElement ).value; if ( !user || user.length < 3 || user.length > 20 || // @ts-expect-error user.match(/_/g)?.length > 1 || user.match(/\W/) ) { toast({ description: "Check the username and try again", duration: 5000, isClosable: true, status: "error", title: "Invalid Username", }); return; } addUser(user); }} > Add </Button> <br /> <br /> <TableContainer> <Table variant="simple"> <Thead> <Tr> <Th>Username</Th> <Th>Punishment</Th> <Th>Remove</Th> </Tr> </Thead> <Tbody>{rows}</Tbody> </Table> </TableContainer> <br /> <br /> <Text>Evidence</Text> <Button mr="8px" onClick={() => document.getElementById("evidence")?.click()} > Select Files </Button> <input id="evidence" type="file" /> </ModalBody> </ModalContent> <ModalFooter> <Button onClick={reset}>Cancel</Button> <Button colorScheme="blue" disabled={ !( Object.entries(actionMap).length && (document.getElementById("evidence") as HTMLInputElement).files ?.length ) } ml="8px" onClick={async () => await submit()} > Submit </Button> </ModalFooter> </Modal> ); }