Skip to content

Commit

Permalink
Merge pull request #215 from hack4impact-calpoly/admin-delete
Browse files Browse the repository at this point in the history
Admin delete
  • Loading branch information
SilveerDusk authored Feb 16, 2025
2 parents 7890b68 + fabab20 commit 4171f93
Show file tree
Hide file tree
Showing 8 changed files with 992 additions and 29 deletions.
688 changes: 688 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

53 changes: 31 additions & 22 deletions src/app/admin/users/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const capitalizeFirstLetter = (str: string): string => {
const UserList = () => {
// states
const [customUser, setUsers] = useState<IUserWithHours[]>([]);
const [filteredUsers, setFilteredUsers] = useState<IUserWithHours[]>([]);
const {users, isLoading, isError} = useUsers()
const [searchTerm, setSearchTerm] = useState("");
const [sortOrder, setSortOrder] = useState<{ value: string; label: string }>({
Expand Down Expand Up @@ -123,7 +124,6 @@ const UserList = () => {
const eventsAttendedNames = await Promise.all(
user.eventsAttended.map((event) => fetchEventName(event.eventId))
);

return {
...user,
totalHoursFormatted: formatHours(
Expand All @@ -149,7 +149,8 @@ const UserList = () => {
fetchUsers();
}, [isError, isLoading]);

const filteredUsers = customUser
useEffect(() => {
setFilteredUsers(customUser
.filter((user) =>
`${user.firstName.toLowerCase()} ${user.lastName.toLowerCase()}`.includes(
searchTerm.toLowerCase()
Expand All @@ -159,13 +160,21 @@ const UserList = () => {
sortOrder.value === "firstName"
? a.firstName.localeCompare(b.firstName)
: a.lastName.localeCompare(b.lastName)
);
));
}, [customUser]);



const sortOptions = [
{ value: "firstName", label: "First Name" },
{ value: "lastName", label: "Last Name" },
];

const removeUser = (userId: string) => {
const newUsers = customUser.filter((user) => user._id != userId);
setUsers(newUsers);
}


const csvData = customUser.map((user) => ({
firstName: user.firstName,
Expand Down Expand Up @@ -287,22 +296,22 @@ const UserList = () => {
{/* {isLoading && !users && !isError && <div>Loading...</div>}
{isError && <div>Error occurred.</div>}
*/}
<Box>
<Box>
<Table
variant="striped"
size={tableSize}
className={style.customTable}
variant="striped"
size={tableSize}
className={style.customTable}
>
<Thead>
<Tr>
<Th>Name</Th>
<Th>Email</Th>
<Th>Total Hours</Th>
<Th>Role</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
<Thead>
<Tr>
<Th>Name</Th>
<Th>Email</Th>
<Th>Total Hours</Th>
<Th>Role</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{filteredUsers.length === 0 ? (
<Tr>
<Td colSpan={5} textAlign="center">
Expand All @@ -316,17 +325,17 @@ const UserList = () => {
<Td>{`${user.firstName} ${user.lastName}`}</Td>
<Td>{user.email}</Td>
<Td>{user.totalHoursFormatted}</Td>

<Td>{capitalizeFirstLetter(user.role)}</Td>
<Td>
<SingleVisitorComponent visitorData={user} />
<SingleVisitorComponent visitorData={user} removeFunction={removeUser} />
</Td>
</Tr>
))
)}
</Tbody>
</Table>
</Box>
</Tbody>
</Table>
</Box>
</div>
</div>
);
Expand Down
60 changes: 60 additions & 0 deletions src/app/api/user/[userId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import connectDB from "@database/db";
import User, { IUser } from "@database/userSchema";
import Group from "@database/groupSchema";
import Event from "@database/eventSchema";
import { NextResponse, NextRequest } from "next/server";
import { revalidateTag } from "next/cache";
import { clerkClient } from '@clerk/nextjs/server'


type IParams = {
params: {
Expand Down Expand Up @@ -98,3 +102,59 @@ export async function PATCH(req: NextRequest, { params }: IParams) {
);
}
}

export async function DELETE(req: NextRequest, {params}: IParams) {

await connectDB(); // Connect to the database

const { userId } = params; // Destructure the userId from params

const bodyText = await new Response(req.body).text();
const email = JSON.parse(bodyText);


try {


const clerkUser = await clerkClient.users.getUserList({emailAddress: [email]});
await clerkClient.users.deleteUser(clerkUser.data[0].id);

const user = await User.findByIdAndDelete(userId).orFail();


if (!user) {
return NextResponse.json(
{ error: "User not found" },
{ status: 404 }
);
}


// Remove user from groups
await Group.updateMany({groupees: userId},
{$pull: {groupees: userId}});


// Update events - set attendee ID to be null
await Event.updateMany({attendeeIds: userId},
{$pull: {attendeeIds: userId}, $push: {attendeeIds: null}}
)

await Event.updateMany({registeredIds: userId},
{$pull: {registeredIds: userId}, $push: {registeredIds: null}}
)

return NextResponse.json("User deleted: " + userId, { status: 200 });


} catch (err) {
console.error("Error deleting user (UserId = " + userId + "):", err);
return NextResponse.json(
"User not deleted (UserId = " + userId + ") " + err,
{ status: 400 }
);
}



}
155 changes: 155 additions & 0 deletions src/app/components/DeleteConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
"use client";
import React, { useState, useEffect } from "react";
import {
Box,
Text,
Flex,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalCloseButton,
Table,
Thead,
Tr,
Th,
Tbody,
Td,
useDisclosure,
Icon,
Center,
} from "@chakra-ui/react";
import styles from "../styles/admin/editEvent.module.css";
import { IUser } from "@database/userSchema";
import { eventIndividualHours } from ".././lib/hours";
import { Schema } from "mongoose";
import { FaRegTrashAlt } from "react-icons/fa";
import {useUser, useClerk} from "@clerk/nextjs";
import { removeUserCookie } from "app/actions/cookieactions";
import { mutate } from "swr";


interface DeleteProps {
closeFromChild: React.MouseEventHandler<HTMLButtonElement>;
userData: IUser | null;
children?: React.ReactNode;
isSelf: boolean;
removeFunction?: (userId: string) => void;

}


function DeleteConfirmation({closeFromChild, userData, isSelf, removeFunction}: DeleteProps) {

const { isOpen, onOpen, onClose } = useDisclosure();


const {signOut} = useClerk();
// const {user} = useUser();


async function handleDelete(){


// Remove cookies when the user signs out
if (isSelf) {
await removeUserCookie();
signOut({ redirectUrl: '/' });
}


if (userData!=null) {
//const clerk_id = user.id;
const email = userData.email;

const res = await fetch(`/api/user/${userData._id}`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(email),
});

if (removeFunction) {
removeFunction(userData._id);
}
}

onClose();


}


return (<>
<Button
mt={2}
color="#d93636"
bg="white"
border="2px"
_hover={{ bg: "#d93636", color: "white" }}
onClick={onOpen}
variant="ghost"
>
<Icon color="36d936" fontSize="1.4rem" p={0.5}>
<FaRegTrashAlt />
</Icon>
Delete User
</Button>

<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay/>
<ModalContent
style={{ width: "40vw", height: "30vh", overflow: "auto" }}
maxW="100rem"
>

<ModalHeader
style={{
padding: "1% 5%",
textAlign: "left",
fontSize: "35px",
fontWeight: "bold",
fontFamily: "Lato",
width: "100%",
}}
>
<Flex direction="column" align="left" p={4}>
<Text>
Delete Account
</Text>

</Flex>
</ModalHeader>
<ModalCloseButton />
<hr />
<ModalBody
style={{ display: "flex", flexDirection: "column", padding: "0%" }}
className={styles.parentContainer}
>
<Flex direction="column" align="center">
<Text className={styles.boldText} p={2}>
Are you sure you want to delete this account?
</Text>

</Flex>
<Flex direction="column" align="center" p={4}>
<Button
mt={2}
bg="#d93636"
color="white"
_hover={{ bg: "#d93636", color: "white" }}
onClick={async() => {await handleDelete(); closeFromChild}}
>
Yes
</Button>
</Flex>
</ModalBody>
</ModalContent>
</Modal>
</>
);
}

export default DeleteConfirmation;

Loading

0 comments on commit 4171f93

Please sign in to comment.