Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
car-crushers-portal/app/routes/events-team.tsx
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
241 lines (222 sloc)
5.92 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
}); | |
const now = new Date(); | |
const monthEventList = await context.env.D1.prepare( | |
"SELECT approved, created_by, day, 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"}`, | |
title: "Success", | |
}); | |
} | |
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", | |
}); | |
} | |
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">Host</Heading> | |
<Text fontSize="sm" pt="2"> | |
{event.created_by} | |
</Text> | |
</Box> | |
</Stack> | |
</CardBody> | |
<CardFooter> | |
<Flex gap="16px"> | |
{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"> | |
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" | |
mr="8px" | |
onClick={async () => await certify(selectedEvent)} | |
> | |
Certify | |
</Button> | |
</ModalFooter> | |
</ModalContent> | |
</Modal> | |
<VStack spacing="8">{eventCards}</VStack> | |
<Link color="#646cff" href="/book-event" mt="16px"> | |
Book an Event | |
</Link> | |
<br /> | |
<Link color="#646cff" href="/et-members" mt="8px"> | |
Events Team Member Management | |
</Link> | |
</Container> | |
); | |
} |