import { Box, Button, Card, CardBody, CardFooter, Container, Flex, Heading, Link, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Stack, StackDivider, Text, useDisclosure, useToast, VStack, } from "@chakra-ui/react"; import { useLoaderData } from "@remix-run/react"; import { type ReactNode, useState } from "react"; 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, }); const now = new Date(); const monthEventList = await context.env.D1.prepare( "SELECT answer, approved, created_by, day, details, id, month, pending, type, year FROM events WHERE month = ? AND year = ?;", ) .bind(now.getUTCMonth() + 1, now.getUTCFullYear()) .all(); if (monthEventList.error) throw new Response(null, { status: 500, }); return { can_approve: Boolean( [1 << 4, 1 << 12].find((p) => context.data.current_user.permissions & p), ), events: monthEventList.results, }; } export default function () { const { can_approve, events, }: { can_approve: boolean; events: { [k: string]: any }[]; } = useLoaderData<typeof loader>(); const eventCards: ReactNode[] = []; const { isOpen, onClose, onOpen } = useDisclosure(); const toast = useToast(); const [selectedEvent, setSelectedEvent] = useState(""); async function decide(approved: boolean, eventId: string) { const decisionResp = await fetch( `/api/events-team/events/${eventId}/decision`, { body: JSON.stringify({ approved }), headers: { "content-type": "application/json", }, method: "POST", }, ); if (!decisionResp.ok) { let errorMsg = "Unknown error"; try { errorMsg = ((await decisionResp.json()) as { error: string }).error; } catch {} toast({ description: errorMsg, status: "error", title: "Oops!", }); return; } toast({ description: `Event ${approved ? "approved" : "rejected"}`, status: "success", title: "Success", }); events.find((e, i) => { if (e.id === eventId) events[i].approved = approved; }); } async function certify(eventId: string) { const certifyResp = await fetch( `/api/events-team/events/${eventId}/certify`, { body: "{}", headers: { "content-type": "application/json", }, method: "POST", }, ); if (!certifyResp.ok) { let errorMsg = "Unknown error"; try { errorMsg = ((await certifyResp.json()) as { error: string }).error; } catch {} toast({ description: errorMsg, status: "error", title: "Failed to certify game night", }); return; } toast({ description: "Game night certified", title: "Success", }); events.find((e, i) => { if (e.id === eventId) events[i].reached_minimum_player_count = true; }); } for (const event of events) { eventCards.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"> {event.created_by} </Text> </Box> </Stack> </CardBody> <CardFooter> <Flex gap="8px"> {can_approve && event.pending ? ( <> <Button colorScheme="red" onClick={async () => await decide(false, event.id)} > Reject </Button> <Button colorScheme="blue" onClick={async () => await decide(true, event.id)} > Approve </Button> </> ) : null} {can_approve && event.approved && event.type === "gamenight" && !event.reached_minimum_player_count ? ( <> <Button colorScheme="blue" onClick={() => { setSelectedEvent(event.id); onOpen(); }} > Certify Game Night </Button> </> ) : null} </Flex> <Text alignSelf="center" fontSize="sm" pl="8px"> Status:{" "} {event.pending ? "Pending" : event.approved ? "Approved" : "Denied"} </Text> </CardFooter> </Card>, ); } return ( <Container maxW="container.lg"> <Modal isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>Certify Game Night</ModalHeader> <ModalCloseButton /> <ModalBody> <Text> By certifying this game night, you confirm that the minimum number of players was met and you were provided proof. </Text> </ModalBody> <ModalFooter> <Button colorScheme="red" onClick={onClose}> Cancel </Button> <Button colorScheme="blue" ml="8px" onClick={async () => await certify(selectedEvent)} > Certify </Button> </ModalFooter> </ModalContent> </Modal> <VStack spacing="8">{eventCards}</VStack> <Link color="#646cff" href="/book-event" pt="16px"> Book an Event </Link> <br /> <Link color="#646cff" href="/et-members" pt="8px"> Events Team Member Management </Link> </Container> ); }