Add actual functionality
This commit is contained in:
parent
63802c409a
commit
81b411ffa9
@ -1,7 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
AlertIcon,
|
AlertIcon,
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
Heading,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
ModalOverlay,
|
||||||
|
StackDivider,
|
||||||
Table,
|
Table,
|
||||||
TableCaption,
|
TableCaption,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
@ -10,8 +20,12 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
Thead,
|
Thead,
|
||||||
Tr,
|
Tr,
|
||||||
|
useDisclosure,
|
||||||
|
useToast,
|
||||||
|
VStack,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useLoaderData } from "@remix-run/react";
|
import { useLoaderData } from "@remix-run/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export async function loader({ context }: { context: RequestContext }) {
|
export async function loader({ context }: { context: RequestContext }) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@ -37,6 +51,24 @@ export async function loader({ context }: { context: RequestContext }) {
|
|||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
const { events, past_cutoff } = useLoaderData<typeof loader>();
|
const { events, past_cutoff } = useLoaderData<typeof loader>();
|
||||||
|
const { isOpen, onClose, onOpen } = useDisclosure();
|
||||||
|
const [eventData, setEventData] = useState({} as { [k: string]: any });
|
||||||
|
const [isBrowserSupported, setIsBrowserSupported] = useState(true);
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
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 }) {
|
function getStatus(event: { [k: string]: string | number }) {
|
||||||
if (!event.performed_at) return "Approved";
|
if (!event.performed_at) return "Approved";
|
||||||
@ -47,12 +79,182 @@ export default function () {
|
|||||||
return "Completed";
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Alert display={past_cutoff ? undefined : "none"} status="warning">
|
<Alert display={past_cutoff ? undefined : "none"} status="warning">
|
||||||
<AlertIcon />
|
<AlertIcon />
|
||||||
The cutoff period for retroactively actioning events has passed.
|
The cutoff period for retroactively actioning events has passed.
|
||||||
</Alert>
|
</Alert>
|
||||||
|
<Alert display={isBrowserSupported ? undefined : "none"} status="error">
|
||||||
|
<AlertIcon />
|
||||||
|
This browser is unsupported. Please upgrade to a browser not several
|
||||||
|
years out of date.
|
||||||
|
</Alert>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>Action Menu</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
<VStack divider={<StackDivider />}>
|
||||||
|
<Box gap="8px">
|
||||||
|
<Heading size="xs">Completion</Heading>
|
||||||
|
<Button
|
||||||
|
disabled={typeof eventData.completed_at === "number"}
|
||||||
|
onClick={async () => await completed()}
|
||||||
|
>
|
||||||
|
Mark as Complete
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={typeof eventData.completed_at === "number"}
|
||||||
|
onClick={async () => await forgotten()}
|
||||||
|
>
|
||||||
|
Mark as Forgotten
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
{eventData.type === "rotw" ? (
|
||||||
|
<Box gap="8px">
|
||||||
|
<Heading size="xs">Solved Status</Heading>
|
||||||
|
<Button
|
||||||
|
disabled={Boolean(eventData.answered_at)}
|
||||||
|
onClick={async () => await solve()}
|
||||||
|
>
|
||||||
|
{eventData.answered_at ? "Solved" : "Mark as Solved"}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
{eventData.type === "gamenight" ? (
|
||||||
|
<Box gap="8px">
|
||||||
|
<Heading size="xs">Certified Status</Heading>
|
||||||
|
<Button
|
||||||
|
disabled={Boolean(eventData.reached_minimum_player_count)}
|
||||||
|
onClick={async () => await certify()}
|
||||||
|
>
|
||||||
|
{eventData.reached_minimum_player_count
|
||||||
|
? "Certified"
|
||||||
|
: "Certify"}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
) : null}
|
||||||
|
</VStack>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onClick={onClose}>Close</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table variant="simple">
|
<Table variant="simple">
|
||||||
<TableCaption>
|
<TableCaption>
|
||||||
@ -73,7 +275,15 @@ export default function () {
|
|||||||
<Td>{event.details}</Td>
|
<Td>{event.details}</Td>
|
||||||
<Td>{getStatus(event)}</Td>
|
<Td>{getStatus(event)}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<Button>Action Menu</Button>
|
<Button
|
||||||
|
disabled={past_cutoff}
|
||||||
|
onClick={() => {
|
||||||
|
setEventData(event);
|
||||||
|
onOpen();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Action Menu
|
||||||
|
</Button>
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
))}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user