Add name/roblox change support
This commit is contained in:
@ -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>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user