Skip to content
Permalink
91a2f2ea5c
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
355 lines (326 sloc) 9.29 KB
import {
Button,
Container,
Divider,
Heading,
Link,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Table,
TableContainer,
Tbody,
Td,
Text,
Th,
Thead,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { type Dispatch, type SetStateAction, useEffect, useState } from "react";
import { useLoaderData } from "@remix-run/react";
export async function loader({ context }: { context: RequestContext }) {
const { current_user: currentUser } = context.data;
if (!currentUser) throw new Response(null, { status: 401 });
const d1Promises = [];
for (const itemType of ["appeals", "inactivity_notices", "reports"])
d1Promises.push(
context.env.D1.prepare(
`SELECT *
FROM ${itemType}
WHERE user = ?
ORDER BY created_at DESC;`,
)
.bind(currentUser.id)
.all(),
);
const settledPromises = await Promise.allSettled(d1Promises);
return {
items: settledPromises.map((p) => {
if (p.status === "fulfilled") return p.value.results;
return null;
}) as any as ({ [k: string]: any }[] | null)[],
permissions: currentUser.permissions as number,
};
}
export default function () {
const data: {
items: ({ [k: string]: any }[] | null)[];
permissions: number;
} = useLoaderData<typeof loader>();
const timeStates: {
[k: number]: { data: string; set: Dispatch<SetStateAction<string>> };
} = {};
const toast = useToast();
for (const result of data.items) {
if (!result) continue;
for (const row of result) {
const [data, set] = useState(new Date(row.created_at).toUTCString());
timeStates[row.created_at] = {
data,
set,
};
useEffect(() => {
timeStates[row.created_at].set(
new Date(row.created_at).toLocaleString(),
);
}, [row.created_at]);
}
}
async function fetchItem(id: string, type: string) {
const itemResp = await fetch(`/api/me/items/${type}/${id}`);
if (!itemResp.ok) {
let error: string;
try {
error = ((await itemResp.json()) as { error: string }).error;
} catch {
error = "Unknown error";
}
toast({
description: error,
isClosable: true,
status: "error",
title: "Oops",
});
return;
}
const data: { [k: string]: any } = await itemResp.json();
switch (type) {
case "appeal":
setModalBody(
<ModalContent>
<ModalHeader>View Appeal</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Heading size="lg">Why were you banned?</Heading>
<br />
<Text>
<i>{data.ban_reason}</i>
</Text>
<br />
<Divider />
<br />
<Heading size="lg">Why should we unban you?</Heading>
<br />
<Text>
<i>{data.reason_for_unban}</i>
</Text>
<br />
<Divider />
<br />
<Heading size="lg">
What have you learned from your mistake?
</Heading>
<br />
<Text>
<i>{data.learned}</i>
</Text>
</ModalBody>
</ModalContent>,
);
break;
case "inactivity":
setModalBody(
<ModalContent>
<ModalHeader>View Inactivity Notice</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Heading size="lg">Reason for Inactivity</Heading>
<br />
<Text>
<i>{data.reason}</i>
</Text>
<br />
<Divider />
<br />
<Heading size="lg">Start Date</Heading>
<br />
<Text>
<i>{new Date(data.start).toLocaleDateString()}</i>
</Text>
<br />
<Divider />
<br />
<Heading size="lg">End Date</Heading>
<br />
<Text>
<i>{new Date(data.end).toLocaleDateString()}</i>
</Text>
</ModalBody>
</ModalContent>,
);
break;
case "report":
setModalBody(
<ModalContent>
<ModalHeader>View Report</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Heading size="lg">Username(s)</Heading>
<br />
<Text>
<i>{data.target_usernames.toString()}</i>
</Text>
<br />
<Divider />
<br />
<Heading size="lg">Description</Heading>
<br />
<Text>
<i>{data.description ?? "No description"}</i>
</Text>
<br />
<Divider />
<br />
<Heading size="lg">Media Links</Heading>
<br />
{data.resolved_attachments.map((attachment: string) => (
<Link color="#646cff" href={attachment} target="_blank">
View media here
</Link>
))}
</ModalBody>
</ModalContent>,
);
break;
default:
setModalBody(<ModalContent></ModalContent>);
break;
}
onOpen();
}
const { isOpen, onClose, onOpen } = useDisclosure();
const [modalBody, setModalBody] = useState(<ModalContent></ModalContent>);
function resetModal() {
onClose();
setModalBody(<ModalContent></ModalContent>);
}
return (
<Container maxW="container.lg">
<Modal isCentered isOpen={isOpen} onClose={resetModal} size="lg">
<ModalOverlay />
{modalBody}
</Modal>
<Heading mb={8}>My Data</Heading>
<br />
<br />
<Heading size="lg">Discord Appeals</Heading>
<TableContainer mb="16px">
<Table variant="simple">
<Thead>
<Tr>
<Th>Date</Th>
<Th>ID</Th>
<Th>Status</Th>
<Th>View</Th>
</Tr>
</Thead>
<Tbody>
{data.items[0]?.map((result) => {
return (
<Tr>
<Td>{timeStates[result.created_at].data}</Td>
<Td>{result.id}</Td>
<Td>
{result.open
? "Pending"
: typeof result.approved === "number"
? `${result.approved ? "Accepted" : "Denied"}`
: "Unknown"}
</Td>
<Td>
<Button
onClick={async () => await fetchItem(result.id, "appeal")}
>
View
</Button>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
<br />
{[1 << 2, 1 << 3, 1 << 9, 1 << 10].find((p) => data.permissions & p) ? (
<>
<Heading size="lg">Inactivity Notices</Heading>
<TableContainer mb="16px">
<Table variant="simple">
<Thead>
<Tr>
<Th>Date</Th>
<Th>ID</Th>
<Th>Status</Th>
<Th>View</Th>
</Tr>
</Thead>
<Tbody>
{data.items[1]?.map((result) => {
return (
<Tr>
<Td>{timeStates[result.created_at].data}</Td>
<Td>{result.id}</Td>
<Td>
{result.open
? "Pending"
: result.approved
? "Approved"
: "Denied"}
</Td>
<Td>
<Button
onClick={async () =>
await fetchItem(result.id, "inactivity")
}
>
View
</Button>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
<br />
</>
) : null}
<Heading size="lg">Reports</Heading>
<TableContainer>
<Table variant="simple">
<Thead>
<Tr>
<Th>Date</Th>
<Th>ID</Th>
<Th>Status</Th>
<Th>View</Th>
</Tr>
</Thead>
<Tbody>
{data.items[2]?.map((result) => {
return (
<Tr>
<Td>{timeStates[result.created_at].data}</Td>
<Td>{result.id}</Td>
<Td>{result.open ? "Pending" : "Reviewed"}</Td>
<Td>
<Button
onClick={async () => await fetchItem(result.id, "report")}
>
View
</Button>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
</TableContainer>
</Container>
);
}