197 lines
5.3 KiB
TypeScript
197 lines
5.3 KiB
TypeScript
import {
|
|
Box,
|
|
Button,
|
|
Card,
|
|
CardBody,
|
|
CardFooter,
|
|
Container,
|
|
Heading,
|
|
HStack,
|
|
Link,
|
|
NumberDecrementStepper,
|
|
NumberIncrementStepper,
|
|
NumberInput,
|
|
NumberInputField,
|
|
NumberInputStepper,
|
|
Select,
|
|
Stack,
|
|
StackDivider,
|
|
Text,
|
|
useToast,
|
|
VStack,
|
|
} from "@chakra-ui/react";
|
|
import { type LinksFunction } from "@remix-run/cloudflare";
|
|
import { type ReactNode, useEffect, useState } from "react";
|
|
import stylesheet from "../styles/events-team.css";
|
|
import { useLoaderData } from "@remix-run/react";
|
|
|
|
export const links: LinksFunction = () => {
|
|
return [{ href: stylesheet, rel: "stylesheet" }];
|
|
};
|
|
|
|
export async function loader({ context }: { context: RequestContext }) {
|
|
if (!context.data.current_user)
|
|
throw new Response(null, {
|
|
status: 401,
|
|
});
|
|
|
|
if (
|
|
![1 << 3, 1 << 4, 1 << 12].find(
|
|
(p) => context.data.current_user.permissions & p,
|
|
)
|
|
)
|
|
throw new Response(null, {
|
|
status: 403,
|
|
});
|
|
|
|
return (
|
|
await context.env.D1.prepare("SELECT id, name FROM et_members;").all()
|
|
).results;
|
|
}
|
|
|
|
export default function () {
|
|
const memberData = useLoaderData<typeof loader>();
|
|
const [month, setMonth] = useState(new Date().getUTCMonth() + 1);
|
|
const [year, setYear] = useState(new Date().getUTCFullYear());
|
|
const [eventCards, setEventCards] = useState([] as ReactNode[]);
|
|
const toast = useToast();
|
|
|
|
async function getEvents() {
|
|
const eventsResp = await fetch(
|
|
`/api/events-team/events/list?month=${month}&year=${year}`,
|
|
).catch(() => {});
|
|
|
|
if (!eventsResp?.ok) {
|
|
let errorMsg = "Unknown error";
|
|
|
|
try {
|
|
errorMsg = ((await eventsResp?.json()) as { error: string }).error;
|
|
} catch {}
|
|
|
|
toast({
|
|
description: errorMsg,
|
|
status: "error",
|
|
title: "Failed to load events",
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
const eventsData: { [k: string]: any }[] = await eventsResp.json();
|
|
const newEventCards = [];
|
|
|
|
for (const event of eventsData) {
|
|
let memberName = event.created_by;
|
|
const memberDataIdx = memberData.findIndex(
|
|
(m) => m.id === event.created_by,
|
|
);
|
|
|
|
if (memberDataIdx !== -1)
|
|
memberName = `${memberData[memberDataIdx].name} (${event.created_by})`;
|
|
|
|
newEventCards.push(
|
|
<Card w="100%">
|
|
<CardBody>
|
|
<Stack divider={<StackDivider />} spacing="4">
|
|
<Box>
|
|
<Heading size="sm">Date</Heading>
|
|
<Text fontSize="sm" pt="2">
|
|
{event.year}-{event.month}-{event.day}
|
|
</Text>
|
|
</Box>
|
|
<Box>
|
|
<Heading size="sm">Event Type</Heading>
|
|
<Text fontSize="sm" pt="2">
|
|
{event.type.toUpperCase()}
|
|
</Text>
|
|
</Box>
|
|
<Box>
|
|
<Heading size="sm">Event Details</Heading>
|
|
<Text fontSize="sm" pt="2">
|
|
{event.details}
|
|
</Text>
|
|
</Box>
|
|
{event.type === "rotw" ? (
|
|
<Box>
|
|
<Heading size="sm">Riddle Answer</Heading>
|
|
<Text fontSize="sm" pt="2">
|
|
{event.answer}
|
|
</Text>
|
|
</Box>
|
|
) : null}
|
|
<Box>
|
|
<Heading size="sm">Host</Heading>
|
|
<Text fontSize="sm" pt="2">
|
|
{memberName}
|
|
</Text>
|
|
</Box>
|
|
</Stack>
|
|
</CardBody>
|
|
<CardFooter>
|
|
<Text alignSelf="center" fontSize="sm">
|
|
Status:{" "}
|
|
{event.pending
|
|
? "Not completed"
|
|
: event.approved
|
|
? event.performed_at
|
|
? "Completed"
|
|
: "Approved"
|
|
: "Denied"}
|
|
</Text>
|
|
</CardFooter>
|
|
</Card>,
|
|
);
|
|
}
|
|
|
|
setEventCards(newEventCards);
|
|
}
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
await getEvents();
|
|
})();
|
|
}, []);
|
|
|
|
return (
|
|
<Container maxW="container.lg">
|
|
<HStack gap="8px" pb="16px">
|
|
<Select
|
|
onChange={(e) => setMonth(parseInt(e.target.value))}
|
|
value={month}
|
|
>
|
|
<option value={1}>January</option>
|
|
<option value={2}>February</option>
|
|
<option value={3}>March</option>
|
|
<option value={4}>April</option>
|
|
<option value={5}>May</option>
|
|
<option value={6}>June</option>
|
|
<option value={7}>July</option>
|
|
<option value={8}>August</option>
|
|
<option value={9}>September</option>
|
|
<option value={10}>October</option>
|
|
<option value={11}>November</option>
|
|
<option value={12}>December</option>
|
|
</Select>
|
|
<NumberInput
|
|
defaultValue={year}
|
|
max={new Date().getUTCFullYear()}
|
|
precision={0}
|
|
>
|
|
<NumberInputField />
|
|
<NumberInputStepper>
|
|
<NumberIncrementStepper />
|
|
<NumberDecrementStepper />
|
|
</NumberInputStepper>
|
|
</NumberInput>
|
|
<Button colorScheme="blue" onClick={async () => await getEvents()}>
|
|
Go
|
|
</Button>
|
|
</HStack>
|
|
<VStack spacing="8">{eventCards}</VStack>
|
|
<Link color="#646cff" href="/events-team" mt="16px">
|
|
Back to Current Events
|
|
</Link>
|
|
</Container>
|
|
);
|
|
}
|