import { Alert, AlertIcon, Box, Button, Heading, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, StackDivider, Table, TableCaption, TableContainer, Tbody, Td, Th, Thead, Tr, useDisclosure, useToast, VStack, } from "@chakra-ui/react"; import { useLoaderData } from "@remix-run/react"; import { useEffect, useState } from "react"; export async function loader({ context }: { context: RequestContext }) { const now = new Date(); let month = now.getUTCMonth(); let year = now.getUTCFullYear(); if (month === 0) { month = 12; year--; } const data = await context.data.prisma.event.findMany({ orderBy: { day: "asc", }, select: { answered_at: true, day: true, details: true, id: true, performed_at: true, reached_minimum_player_count: true, type: true, }, where: { AND: { approved: true, month, year, OR: [ { AND: [ { reached_minimum_player_count: false, type: "gamenight", }, ], }, ], }, }, }); return { events: data, past_cutoff: now.getUTCDate() > 7, }; } export default function () { const { events, past_cutoff } = useLoaderData(); const { isOpen, onClose, onOpen } = useDisclosure(); const [eventData, setEventData] = useState({} as (typeof events)[number]); const [isBrowserSupported, setIsBrowserSupported] = useState(true); const toast = useToast(); useEffect(() => { if (typeof structuredClone === "undefined") setIsBrowserSupported(false); }, []); async function displayErrorToast(response: Response, title: string) { let msg = "Unknown error"; try { msg = ((await response.json()) as { error: string }).error; } catch {} toast({ description: msg, status: "error", title, }); } function getStatus(event: (typeof events)[number]) { if (!event.performed_at) return "Approved"; if (event.type === "rotw" && event.answered_at) return "Solved"; if (event.type === "gamenight" && event.reached_minimum_player_count) return "Certified"; return "Completed"; } async function certify() { const response = await fetch( `/api/events-team/events/${eventData.id}/certify`, { body: "{}", headers: { "content-type": "application/json", }, method: "POST", }, ); if (!response.ok) { await displayErrorToast(response, "Failed to certify game night"); return; } toast({ status: "success", title: "Game night certified", }); const newData = structuredClone(eventData); newData.reached_minimum_player_count = true; setEventData(newData); } async function completed() { const response = await fetch( `/api/events-team/events/${eventData.id}/complete`, { body: "{}", headers: { "content-type": "application/json", }, method: "POST", }, ); if (!response.ok) { await displayErrorToast(response, "Failed to mark as completed"); return; } toast({ status: "success", title: "Event marked as complete", }); const newData = structuredClone(eventData); setEventData( Object.defineProperty(newData, "performed_at", { value: new Date().toISOString(), }), ); } async function forgotten() { const response = await fetch( `/api/events-team/events/${eventData.id}/forgotten`, { body: "{}", headers: { "content-type": "application/json", }, method: "POST", }, ); if (!response.ok) { await displayErrorToast(response, "Failed to mark as forgotten"); return; } toast({ title: "Event marked as forgotten", status: "success", }); const newData = structuredClone(eventData); setEventData( Object.defineProperty(newData, "performed_at", { value: new Date().toISOString(), }), ); } async function solve() { const response = await fetch( `/api/events-team/events/${eventData.id}/solve`, { body: "{}", headers: { "content-type": "application/json", }, method: "POST", }, ); if (!response.ok) { await displayErrorToast(response, "Failed to mark as solved"); return; } toast({ status: "success", title: "Riddle marked as solved", }); const newData = structuredClone(eventData); setEventData( Object.defineProperty(newData, "performed_at", { value: new Date().toISOString(), }), ); } return ( <> The cutoff period for retroactively actioning events has passed. This browser is unsupported. Please upgrade to a browser not several years out of date. Action Menu }> Completion {eventData.type === "rotw" ? ( Solved Status ) : null} {eventData.type === "gamenight" ? ( Certified Status ) : null} Events that are not denied or left pending which need to be actioned {events.map((event) => ( ))}
Day Details Current Status Action
{event.day} {event.details.length > 100 ? `${event.details.substring(0, 97)}...` : event.details} {getStatus(event)}
); }