Add strike creation capabilities
This commit is contained in:
parent
3e06541b30
commit
0334dc1678
@ -325,7 +325,8 @@ export default function () {
|
|||||||
<TableContainer mt="16px">
|
<TableContainer mt="16px">
|
||||||
<Table variant="simple">
|
<Table variant="simple">
|
||||||
<TableCaption>
|
<TableCaption>
|
||||||
Points are updated at the end of the month
|
Click/tap on a user's points count to change their points, their
|
||||||
|
user id to see and manage strikes.
|
||||||
</TableCaption>
|
</TableCaption>
|
||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
@ -339,7 +340,19 @@ export default function () {
|
|||||||
<Tbody>
|
<Tbody>
|
||||||
{memberData.map((member) => (
|
{memberData.map((member) => (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td>{member.id}</Td>
|
<Td>
|
||||||
|
{isManagement ? (
|
||||||
|
<Link
|
||||||
|
onClick={() =>
|
||||||
|
location.assign(`/et-members/strikes/${member.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{member.id}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
member.id
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
<Td>{member.name}</Td>
|
<Td>{member.name}</Td>
|
||||||
<Td>{member.roblox_id}</Td>
|
<Td>{member.roblox_id}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
|
240
app/routes/et-members_.strikes_.$uid.tsx
Normal file
240
app/routes/et-members_.strikes_.$uid.tsx
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user