import { Alert, AlertDescription, AlertIcon, AlertTitle, Box, Button, Container, Flex, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Spacer, Text, Textarea, useDisclosure, useToast, } from "@chakra-ui/react"; import { useLoaderData } from "@remix-run/react"; import { useState } from "react"; import Success from "../../components/Success.js"; export async function loader({ context }: { context: RequestContext }) { if (!context.data.current_user) throw new Response(null, { status: 401, }); const { current_user: currentUser } = context.data; const dataKV = context.env.DATA; const disabled = await dataKV.get("appeal_disabled"); return { can_appeal: !Boolean(disabled) && !Boolean(await dataKV.get(`blockedappeal_${currentUser.id}`)) && !Boolean( await context.env.D1.prepare( "SELECT * FROM appeals WHERE open = 1 AND user = ? LIMIT 1;", ) .bind(currentUser.id) .first(), ), can_toggle: currentUser.permissions & (1 << 0) || currentUser.permissions & (1 << 11), disabled: Boolean(disabled), }; } export function meta() { return [ { title: "Appeals - Car Crushers", }, ]; } export default function () { const pageProps = useLoaderData<typeof loader>(); const { isOpen, onClose, onOpen } = useDisclosure(); const [showSuccess, setShowSuccess] = useState(false); const [loading, setLoading] = useState(false); const toast = useToast(); async function submit() { setLoading(true); const learned = (document.getElementById("learned") as HTMLInputElement) .value; const whyBanned = (document.getElementById("whyBanned") as HTMLInputElement) .value; const whyUnban = (document.getElementById("whyUnban") as HTMLInputElement) .value; const submitReq = await fetch("/api/appeals/submit", { body: JSON.stringify({ learned, whyBanned, whyUnban, }), headers: { "content-type": "application/json", }, method: "POST", }).catch(() => {}); if (!submitReq) { setLoading(false); return toast({ description: "Please check your internet and try again", duration: 10000, isClosable: true, status: "error", title: "Request Failed", }); } if (!submitReq.ok) { setLoading(false); return toast({ description: ((await submitReq.json()) as { error: string }).error, duration: 10000, isClosable: true, status: "error", title: "Error", }); } setShowSuccess(true); setLoading(false); } async function toggle(active: boolean) { const toggleReq = await fetch("/api/appeals/toggle", { body: JSON.stringify({ active }), headers: { "content-type": "application/json", }, method: "POST", }); if (!toggleReq.ok) return toast({ description: ((await toggleReq.json()) as { error: string }).error, duration: 10000, isClosable: true, status: "error", title: "Error", }); toast({ description: `The appeals form is now ${active ? "opened" : "closed"}.`, isClosable: true, status: "success", title: `Appeals ${active ? "enabled" : "disabled"}`, }); onClose(); pageProps.can_appeal = !pageProps.can_appeal; pageProps.disabled = !pageProps.disabled; } return showSuccess ? ( <Success heading="Appeal Submitted" message="You will receive an email when we reach a decision." /> ) : ( <Container maxW="container.md" pt="4vh" textAlign="start"> <Alert borderRadius="8px" display={pageProps.disabled ? "flex" : "none"} mb="16px" status="error" > <AlertIcon /> <Box> <AlertTitle>Appeals Closed</AlertTitle> <AlertDescription> We are currently not accepting appeals. </AlertDescription> </Box> </Alert> <Flex> <Spacer /> <Button display={pageProps.can_toggle ? "" : "none"} onClick={onOpen}> {pageProps.disabled ? "Enable" : "Disable"} Appeals </Button> </Flex> <br /> <Modal isCentered isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>Toggle appeals?</ModalHeader> <ModalCloseButton /> <ModalBody> <Text> Are you sure you want to{" "} {pageProps.disabled ? "enable" : "disable"} appeals? </Text> </ModalBody> <ModalFooter style={{ gap: "8px" }}> <Button onClick={onClose} variant="ghost"> No </Button> <Button onClick={async () => await toggle(pageProps.disabled)} variant="danger" > Yes </Button> </ModalFooter> </ModalContent> </Modal> <Heading size="xl">Discord Appeals</Heading> <br /> <Text fontSize="md"> This is for Discord bans only! See the support page if you were banned from the game. </Text> <br /> <br /> <Heading size="md">Why were you banned?</Heading> <br /> <Textarea disabled={!pageProps.can_appeal} id="whyBanned" maxLength={500} placeholder="Your response" /> <br /> <br /> <br /> <Heading size="md">Why should we unban you?</Heading> <br /> <Textarea disabled={!pageProps.can_appeal} id="whyUnban" maxLength={2000} placeholder="Your response" /> <br /> <br /> <br /> <Heading size="md">What have you learned from your mistake?</Heading> <br /> <Textarea disabled={!pageProps.can_appeal} id="learned" maxLength={2000} placeholder="Your response" /> <br /> <br /> <Button disabled={pageProps.can_appeal} onClick={async () => await submit()} loadingText="Submitting" isLoading={loading} > Submit </Button> </Container> ); }