Skip to content

Commit 204a04d

Browse files
authored
Merge pull request #104 from hack4impact-calpoly/103-entry-deletion
Add feature to delete volunteer entries (Resolves #103)
2 parents fc582d6 + c0b6cc2 commit 204a04d

File tree

3 files changed

+121
-54
lines changed

3 files changed

+121
-54
lines changed

src/app/components/AdminEventDetails/AdminEventDetails.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ type Props = {
3030
onClose: () => void;
3131
};
3232

33-
34-
3533
// get event by id
3634
async function getEvent(_id: string) {
3735
try {
@@ -44,7 +42,7 @@ async function getEvent(_id: string) {
4442
}
4543
return res.json();
4644
} catch (err: unknown) {
47-
console.log(`error: ${err}`);
45+
console.log(`error: ${err}` );
4846
return null;
4947
}
5048
}
Lines changed: 105 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import React, { useState, useEffect } from "react";
2-
import type {
3-
IVolunteerEntry,
4-
IFormAnswer,
5-
} from "../../../database/volunteerEntrySchema";
6-
import type { IVolunteer } from "../../../database/volunteerSchema";
2+
import type { IFormAnswer } from "../../../database/volunteerEntrySchema";
73
import type {
84
IVolunteerRole,
9-
IVolunteerRoleTimeslot,
105
} from "../../../database/volunteerRoleSchema";
116
import style from "./AdminEventDetails.module.css";
127
import {
@@ -15,35 +10,111 @@ import {
1510
Heading,
1611
Flex,
1712
Avatar,
13+
IconButton,
14+
Spacer,
15+
useToast,
1816
} from "@chakra-ui/react";
19-
import { useUser } from "@clerk/nextjs";
17+
import { DeleteIcon } from "@chakra-ui/icons";
2018

2119
type Props = {
2220
name: string;
2321
roles: IVolunteerRole[];
2422
responses: IFormAnswer[];
23+
volunteerId: string;
24+
entryId: string;
25+
onDelete: () => void;
2526
};
2627

27-
export default function DisplayVolunteerInformation({ name, roles, responses }: Props) {
28-
const { user } = useUser();
28+
export default function DisplayVolunteerInformation({
29+
name,
30+
roles,
31+
responses,
32+
volunteerId,
33+
entryId,
34+
onDelete,
35+
}: Props) {
36+
const toast = useToast();
37+
2938
function parseDate(date: Date) {
3039
return new Date(date).toLocaleTimeString("en-US", {
3140
hour: "2-digit",
3241
minute: "2-digit",
3342
hour12: true,
3443
});
3544
}
45+
46+
async function handleEntryDelete() {
47+
if (
48+
window.confirm(
49+
"Are you sure you want to delete this volunteer submission?"
50+
)
51+
) {
52+
try {
53+
// Remove volunteer from timeslots in roles
54+
console.log("roles before deletion", roles);
55+
const rolesWithUpdatedTimeslots = roles.map((role) => ({
56+
...role,
57+
timeslots: role.timeslots.map((timeslot) => ({
58+
...timeslot,
59+
volunteers: timeslot.volunteers.filter(
60+
(volunteer) => volunteer !== volunteerId
61+
),
62+
})),
63+
}));
64+
65+
console.log("rolesWithUpdatedTimeslots", rolesWithUpdatedTimeslots);
66+
67+
for (const updatedRole of rolesWithUpdatedTimeslots) {
68+
await fetch(`/api/role/${updatedRole._id}`, {
69+
method: "PATCH",
70+
headers: {
71+
"Content-Type": "application/json",
72+
},
73+
body: JSON.stringify({ timeslots: updatedRole.timeslots }),
74+
});
75+
}
76+
77+
// Delete the volunteer entry (api endpoint already
78+
// removes entry from volunteer.entries array)
79+
const deleteResponse = await fetch(`/api/entry/${entryId}`, {
80+
method: "DELETE",
81+
});
82+
if (!deleteResponse.ok) {
83+
throw new Error("Failed to delete volunteer entry");
84+
} else {
85+
toast({
86+
title: "Submission removed",
87+
description: "The volunteer submission has been successfully removed.",
88+
status: "success",
89+
duration: 3000,
90+
isClosable: true,
91+
});
92+
}
93+
94+
console.log("Volunteer entry deleted successfully");
95+
onDelete();
96+
} catch (error) {
97+
console.error("Deletion error:", error);
98+
}
99+
}
100+
}
101+
36102
return (
37103
<Box p={4} overflow="auto">
38104
<Flex direction="row" alignItems="center">
39-
<Avatar
40-
name={name}
41-
marginRight={3}
105+
<Avatar name={name} marginRight={3} size="sm" />
106+
<Heading size="md">{name}</Heading>
107+
<Spacer />
108+
<IconButton
109+
aria-label="Delete volunteer"
110+
icon={<DeleteIcon />}
111+
onClick={() => handleEntryDelete()}
112+
variant="ghost"
42113
size="sm"
114+
colorScheme="green"
43115
/>
44-
<Heading size="md">{name}</Heading>
45116
</Flex>
46-
117+
47118
{roles.map((role, index) => (
48119
<Box key={index} ml={4}>
49120
<Flex align="center">
@@ -58,37 +129,35 @@ export default function DisplayVolunteerInformation({ name, roles, responses }:
58129
<Heading size="sm" mb={2}>
59130
{role.roleName}
60131
</Heading>
61-
<Flex
62-
className={style.openTime}
63-
ml={4}>
132+
<Flex className={style.openTime} ml={4}>
64133
{role.timeslots.map((timeslot, subIndex) => (
65134
<Text key={subIndex} mb={1} fontSize="sm">
66-
{parseDate(timeslot.startTime)} - {parseDate(timeslot.endTime)}{"\xa0"}
135+
{parseDate(timeslot.startTime)} -{" "}
136+
{parseDate(timeslot.endTime)}
137+
{"\xa0"}
67138
</Text>
68-
69139
))}
70140
</Flex>
71141
</Flex>
72142
</Box>
73143
))}
74144
{responses.map((response, index2) => (
75-
<Flex key={index2} align="center" ml={4}>
76-
<Box
77-
w={4}
78-
h={4}
79-
borderRadius="full"
80-
border="1px solid"
81-
borderColor="gray.400"
82-
mr={2}
83-
mb={5}
84-
/>
85-
<Box>
86-
<Text fontWeight="bold">{response.question}</Text>
87-
<Text>{response.answer}</Text>
88-
</Box>
89-
</Flex>
90-
))}
145+
<Flex key={index2} align="center" ml={4}>
146+
<Box
147+
w={4}
148+
h={4}
149+
borderRadius="full"
150+
border="1px solid"
151+
borderColor="gray.400"
152+
mr={2}
153+
mb={5}
154+
/>
155+
<Box>
156+
<Text fontWeight="bold">{response.question}</Text>
157+
<Text>{response.answer}</Text>
158+
</Box>
159+
</Flex>
160+
))}
91161
</Box>
92162
);
93-
94163
}

src/app/components/AdminEventDetails/VolunteerDetails.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import React, { useState, useEffect } from "react";
2-
import type { IEvent } from "../../../database/eventSchema";
32
import type {
4-
IVolunteerEntry,
53
IFormAnswer,
64
} from "../../../database/volunteerEntrySchema";
75
import type { IVolunteer } from "../../../database/volunteerSchema";
86
import type {
97
IVolunteerRole,
10-
IVolunteerRoleTimeslot,
118
} from "../../../database/volunteerRoleSchema";
129
import style from "./AdminEventDetails.module.css";
1310
import {
@@ -28,7 +25,6 @@ import {
2825
Avatar
2926
} from "@chakra-ui/react";
3027
import DisplayVolunteerInformation from "./DisplayVolunteerInformation";
31-
import { useUser } from "@clerk/nextjs";
3228

3329
type Props = {
3430
_id: string;
@@ -55,7 +51,6 @@ export default function VolunteerDetails({ _id, isOpen, onOpen, onClose}: Props)
5551
const [filteredEntries, setFilteredEntries] = useState<VolunteerEntry[]>([]);
5652
const [loading, setLoading] = useState<Boolean>(true);
5753
const [error, setError] = useState(null);
58-
const { user } = useUser();
5954

6055
function parseDate(date: Date) {
6156
return new Date(date).toLocaleTimeString("en-US", {
@@ -71,16 +66,7 @@ export default function VolunteerDetails({ _id, isOpen, onOpen, onClose}: Props)
7166

7267
const filteredItems = volunteerEntries.filter(
7368
(entry) =>
74-
entry.volunteer.name.toLowerCase().includes(searchTerm.toLowerCase()) //||
75-
// entry.roles.map((role: IVolunteerRole) =>
76-
// role.roleName?.toLowerCase().includes(searchTerm.toLowerCase())
77-
// )
78-
// ||
79-
// entry.responses?.map(
80-
// (resp: IFormAnswer) =>
81-
// resp.question?.toLowerCase().includes(searchTerm.toLowerCase()) ||
82-
// resp.answer?.toLowerCase().includes(searchTerm.toLowerCase())
83-
// )
69+
entry.volunteer.name.toLowerCase().includes(searchTerm.toLowerCase())
8470
);
8571

8672
setFilteredEntries(filteredItems);
@@ -122,6 +108,17 @@ export default function VolunteerDetails({ _id, isOpen, onOpen, onClose}: Props)
122108
});
123109
}, []);
124110

111+
const handleVolunteerDelete = async () => {
112+
const newData = await fetchEntries();
113+
if (newData) {
114+
setVolunteerEntries(newData);
115+
setFilteredEntries(newData);
116+
} else {
117+
setVolunteerEntries([]);
118+
setFilteredEntries([]);
119+
}
120+
};
121+
125122
return (
126123
<Box p={4}>
127124
<Box>
@@ -190,6 +187,9 @@ export default function VolunteerDetails({ _id, isOpen, onOpen, onClose}: Props)
190187
name={entry.volunteer.name}
191188
roles={entry.roles}
192189
responses={entry.responses}
190+
volunteerId={entry.volunteer._id}
191+
entryId={entry._id}
192+
onDelete={() => handleVolunteerDelete()}
193193
/>
194194
</Box>
195195
</ListItem>

0 commit comments

Comments
 (0)