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.env.D1.prepare( "SELECT day, details, id FROM events WHERE approved = 1 AND month = ? AND year = ? AND (performed_at IS NULL OR reached_minimum_player_count = 0);", ) .bind(month, year) .all(); return { events: data.results as Record[], past_cutoff: now.getUTCDate() > 7, }; } export default function () { const { events, past_cutoff } = useLoaderData(); const { isOpen, onClose, onOpen } = useDisclosure(); const [eventData, setEventData] = useState({} as { [k: string]: any }); 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: { [k: string]: string | number }) { if (!event.performed_at) return "Approved"; if (event.type === "rotw" && event.answered_at) return "Solved"; if (event.type === "gamenight" && event.areached_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 = 1; 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); newData.performed_at = Date.now(); setEventData(newData); } 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); newData.performed_at = 0; setEventData(newData); } 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); newData.answered_at = Date.now(); setEventData(newData); } 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 as string).length > 100 ? `${(event.details as string).substring(0, 97)}...` : event.details} {getStatus(event)}
); }