car-crushers-portal/app/routes/et-members_.strikes_.$uid.tsx

241 lines
5.7 KiB
TypeScript

import {
Button,
Container,
Heading,
Link,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Table,
TableContainer,
Tbody,
Td,
Text,
Textarea,
Th,
Thead,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
import { useState } from "react";
export async function loader({
context,
params,
}: {
context: RequestContext;
params: LoaderFunctionArgs & { uid: string };
}) {
const { current_user: user } = context.data;
if (!user)
throw new Response(null, {
status: 401,
});
if (![1 << 3, 1 << 4, 1 << 12].find((p) => user.permissions & p))
throw new Response(null, {
status: 403,
});
const strikeData = await context.env.D1.prepare(
"SELECT * FROM et_strikes WHERE user = ?;",
)
.bind(params.uid)
.all();
return {
can_manage: Boolean([1 << 4, 1 << 12].find((p) => user.permissions & p)),
strikes: strikeData.results,
user: params.uid,
};
}
export default function () {
const { can_manage, strikes, user } = useLoaderData<typeof loader>();
const [strikeData, setStrikeData] = useState(strikes);
const toast = useToast();
const [rmStrikeId, setRmStrikeId] = useState("");
const [strikeReason, setStrikeReason] = useState("");
async function removeStrike(id: string) {
const removeResp = await fetch(`/api/events-team/strikes/${id}`, {
method: "DELETE",
});
if (!removeResp.ok) {
let msg = "Unknown error";
try {
msg = ((await removeResp.json()) as { error: string }).error;
} catch {}
toast({
description: msg,
status: "error",
title: "Failed to remove strike",
});
return;
}
toast({
description: `Strike ${id} was removed`,
status: "success",
title: "Strike Removed",
});
setStrikeData(strikeData.filter((strike) => strike.id !== id));
closeRmStrike();
}
async function addStrike() {
const addStrikeResp = await fetch("/api/events-team/strikes/new", {
body: JSON.stringify({
reason: strikeReason,
user,
}),
headers: {
"content-type": "application/json",
},
method: "POST",
});
if (!addStrikeResp.ok) {
let msg = "Unknown error";
try {
msg = ((await addStrikeResp.json()) as { error: string }).error;
} catch {}
toast({
description: msg,
status: "error",
title: "Failed to add strike",
});
return;
}
toast({
description: "Strike added",
status: "success",
title: "Success",
});
const newStrikeData = strikeData;
newStrikeData.push(await addStrikeResp.json());
setStrikeData(newStrikeData);
closeAddStrike();
}
const {
isOpen: rmStrikeOpen,
onClose: closeRmStrike,
onOpen: openRmStrike,
} = useDisclosure();
const {
isOpen: addStrikeOpen,
onClose: closeAddStrike,
onOpen: openAddStrike,
} = useDisclosure();
return (
<Container maxW="container.lg">
<Modal isOpen={rmStrikeOpen} onClose={closeRmStrike}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Remove Strike</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text>Are you sure you want to remove this strike?</Text>
</ModalBody>
<ModalFooter>
<Button mr="8px">No</Button>
<Button
colorScheme="red"
onClick={async () => await removeStrike(rmStrikeId)}
>
Yes
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Modal isOpen={addStrikeOpen} onClose={closeAddStrike}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Add Strike</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Heading mb="8px" size="xs">
Reason
</Heading>
<Textarea
onChange={(e) => setStrikeReason(e.target.value)}
placeholder="Strike reason"
value={strikeReason}
/>
</ModalBody>
<ModalFooter>
<Button
mr="8px"
onClick={() => {
closeAddStrike();
setStrikeReason("");
}}
>
Cancel
</Button>
<Button colorScheme="red" onClick={async () => await addStrike()}>
Add Strike
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Heading my="16px">Strikes</Heading>
<TableContainer>
<Table variant="simple">
<Thead>
<Th>Time Added</Th>
<Th>Added By</Th>
<Th>Reason</Th>
<Th>Remove</Th>
</Thead>
<Tbody>
{strikeData.map((strike: { [k: string]: any }) => (
<Tr>
<Td>{new Date(strike.created_at).toUTCString()}</Td>
<Td>{strike.created_by}</Td>
<Td>{strike.reason}</Td>
<Td>
{can_manage ? (
<Link
onClick={() => {
setRmStrikeId(strike.id);
openRmStrike();
}}
>
Remove
</Link>
) : null}
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<Link color="#646cff" onClick={openAddStrike} py="16px">
Add Strike
</Link>
</Container>
);
}