241 lines
6.0 KiB
TypeScript
241 lines
6.0 KiB
TypeScript
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 dataKV.list({
|
|
prefix: `appeal_${currentUser.id}`,
|
|
})
|
|
).keys.length,
|
|
),
|
|
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 toast = useToast();
|
|
|
|
async function submit() {
|
|
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)
|
|
return toast({
|
|
description: "Please check your internet and try again",
|
|
duration: 10000,
|
|
isClosable: true,
|
|
status: "error",
|
|
title: "Request Failed",
|
|
});
|
|
|
|
if (!submitReq.ok)
|
|
return toast({
|
|
description: ((await submitReq.json()) as { error: string }).error,
|
|
duration: 10000,
|
|
isClosable: true,
|
|
status: "error",
|
|
title: "Error",
|
|
});
|
|
|
|
setShowSuccess(true);
|
|
}
|
|
|
|
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()}
|
|
>
|
|
Submit
|
|
</Button>
|
|
</Container>
|
|
);
|
|
}
|