Add name/roblox change support

This commit is contained in:
2024-03-05 23:37:51 -05:00
parent 5fe23184c9
commit 2f8ff3b3cc
2 changed files with 238 additions and 78 deletions

View File

@ -78,8 +78,7 @@ export async function loader({ context }: { context: RequestContext }) {
}
}
return { can_manage: true, members } as {
can_manage: boolean;
return { members } as {
members: { [k: string]: any }[];
};
}
@ -171,7 +170,26 @@ export default function () {
onClose: closeNameChange,
onOpen: openNameChange,
} = useDisclosure();
const isManagement = data.can_manage;
const {
isOpen: isChangeRobloxOpen,
onClose: closeChangeRoblox,
onOpen: openChangeRoblox,
} = useDisclosure();
function validateRobloxName(e: FormEvent<HTMLInputElement>) {
const data = (e.target as HTMLInputElement).value as string;
if (!data) return;
if (
data.match(/\W/) ||
data.length > 20 ||
// Need Number pseudo-constructor since matches might be null
(data.match(/_/g)?.length || 0) > 1 ||
data.startsWith("_")
)
e.preventDefault();
}
async function updatePoints(id: string, points: number) {
const updateResp = await fetch(`/api/events-team/points/${id}`, {
@ -207,6 +225,93 @@ export default function () {
return (
<Container maxW="container.lg">
<Modal isOpen={isChangeRobloxOpen} onClose={closeChangeRoblox}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Change Roblox User</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Heading mb="8px" size="xs">
New Roblox Username
</Heading>
<Input
maxLength={20}
onBeforeInput={validateRobloxName}
onChange={(e) => setAddingMemberRoblox(e.target.value)}
placeholder="builderman"
/>
</ModalBody>
<ModalFooter>
<Button
onClick={() => {
setAddingMemberRoblox("");
closeChangeRoblox();
}}
>
Cancel
</Button>
<Button
colorScheme="blue"
ml="8px"
onClick={async () => {
const changeResp = await fetch(
"/api/events-team/team-members/user",
{
body: JSON.stringify({
id: currentModalMember,
roblox_username: addingMemberRoblox,
}),
headers: {
"content-type": "application/json",
},
method: "PATCH",
},
);
if (!changeResp.ok) {
let errorMsg = "Unknown error";
try {
errorMsg = ((await changeResp.json()) as { error: string })
.error;
} catch {}
toast({
description: errorMsg,
status: "error",
title: "Failed to change",
});
return;
}
toast({
description: "Roblox information updated",
status: "success",
title: "Change successful",
});
const newMemberData = memberData;
newMemberData[
memberData.findIndex((m) => m.id === currentModalMember)
].roblox_id = (
(await changeResp.json()) as {
name: string;
roblox_id: number;
}
).roblox_id;
setMemberData([...newMemberData]);
closeChangeRoblox();
setModalMember("");
setAddingMemberRoblox("");
}}
>
Change
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Modal isOpen={isNameChangeOpen} onClose={closeNameChange}>
<ModalOverlay />
<ModalContent>
@ -253,8 +358,16 @@ export default function () {
setAddingMemberName("");
if (!nameUpdateResp.ok) {
let errorMsg = "Unknown error";
try {
errorMsg = (
(await nameUpdateResp.json()) as { error: string }
).error;
} catch {}
toast({
description: "Failed to update name, try again later.",
description: errorMsg,
status: "error",
title: "Error",
});
@ -298,27 +411,25 @@ export default function () {
</NumberInputStepper>
</NumberInput>
</ModalBody>
{isManagement ? (
<ModalFooter>
<Button
onClick={() => {
setRealtimePoints(0);
onClose();
}}
>
Cancel
</Button>
<Button
colorScheme="blue"
ml="8px"
onClick={async () =>
await updatePoints(currentModalMember, realtimePoints)
}
>
Update Points
</Button>
</ModalFooter>
) : null}
<ModalFooter>
<Button
onClick={() => {
setRealtimePoints(0);
onClose();
}}
>
Cancel
</Button>
<Button
colorScheme="blue"
ml="8px"
onClick={async () =>
await updatePoints(currentModalMember, realtimePoints)
}
>
Update Points
</Button>
</ModalFooter>
</ModalContent>
</Modal>
<Modal isOpen={isDelConfirmOpen} onClose={closeDelConfirm}>
@ -386,20 +497,7 @@ export default function () {
<Heading size="xs">Roblox Username (optional)</Heading>
<Input
maxLength={20}
onBeforeInput={(e) => {
const data = (e.target as HTMLInputElement).value as string;
if (!data) return;
if (
data.match(/\W/) ||
data.length > 20 ||
// Need Number pseudo-constructor since matches might be null
(data.match(/_/g)?.length || 0) > 1 ||
data.startsWith("_")
)
e.preventDefault();
}}
onBeforeInput={validateRobloxName}
onChange={(e) => setAddingMemberRoblox(e.target.value)}
/>
</ModalBody>
@ -435,52 +533,58 @@ export default function () {
{memberData.map((member) => (
<Tr>
<Td>
{isManagement ? (
<Link href={`/et-members/strikes/${member.id}`}>
{member.id}
</Link>
) : (
member.id
)}
</Td>
<Td>{member.name}</Td>
<Td>{member.roblox_id}</Td>
<Td>
{isManagement ? (
<Link
onClick={() => {
setModalMember(member.id);
onOpen();
}}
>
{member.points}
</Link>
) : (
member.points
)}
<Link href={`/et-members/strikes/${member.id}`}>
{member.id}
</Link>
</Td>
<Td>
{isManagement ? (
<Link
onClick={() => {
setDelMember({ id: member.id, name: member.name });
openDelConfirm();
}}
>
Remove
</Link>
) : null}
<Link
onClick={() => {
setModalMember(member.id);
openNameChange();
}}
>
{member.name}
</Link>
</Td>
<Td>
<Link
onClick={() => {
setModalMember(member.id);
openChangeRoblox();
}}
>
{member.roblox_id}
</Link>
</Td>
<Td>
<Link
onClick={() => {
setModalMember(member.id);
onOpen();
}}
>
{member.points}
</Link>
</Td>
<Td>
<Link
onClick={() => {
setDelMember({ id: member.id, name: member.name });
openDelConfirm();
}}
>
Remove
</Link>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
{isManagement ? (
<Link color="#646cff" onClick={openAddMember} mt="16px">
Add Member
</Link>
) : null}
<Link color="#646cff" onClick={openAddMember} mt="16px">
Add Member
</Link>
</Container>
);
}