-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
wkim10
committed
Dec 11, 2024
1 parent
f5dd5de
commit 6a50cf9
Showing
6 changed files
with
438 additions
and
110 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,131 @@ | ||
"use client"; | ||
|
||
import VolunteerTable from "@components/VolunteerTable/VolunteerTable"; | ||
import SearchBar from "@components/SearchBar"; | ||
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; | ||
import { Icon } from "@iconify/react/dist/iconify.js"; | ||
import { Button } from "@mui/material"; | ||
import React from "react"; | ||
import { Role, User } from "@prisma/client"; | ||
import { deleteUser, getUsersByRole } from "@api/user/route.client"; | ||
import Image from "next/image"; | ||
|
||
export default function VolunteersPage() { | ||
const [users, setUsers] = React.useState<User[]>(); | ||
const [selected, setSelected] = React.useState<string[]>([]); | ||
const [searchText, setSearchText] = React.useState(""); | ||
|
||
React.useEffect(() => { | ||
const fetchUsers = async () => { | ||
try { | ||
const response = await getUsersByRole(Role.VOLUNTEER); | ||
setUsers(response.data); | ||
} catch (error) { | ||
console.error("Error fetching volunteers:", error); | ||
} | ||
}; | ||
|
||
fetchUsers(); | ||
}, []); | ||
|
||
// Filter users based on the search text | ||
const filteredUsers = users?.filter( | ||
(user) => | ||
user.firstName.toLowerCase().includes(searchText.toLowerCase()) || | ||
user.lastName.toLowerCase().includes(searchText.toLowerCase()) || | ||
user.email.toLowerCase().includes(searchText.toLowerCase()) | ||
); | ||
|
||
const deleteUsers = async (selectedIds: string[]) => { | ||
try { | ||
const deletePromises = selectedIds.map((id) => deleteUser(id)); | ||
const responses = await Promise.all(deletePromises); | ||
const allDeleted = responses.every( | ||
(response) => response.code === "SUCCESS" | ||
); | ||
|
||
if (allDeleted) { | ||
setUsers((prevUsers) => | ||
prevUsers | ||
? prevUsers.filter((user) => !selectedIds.includes(user.id)) | ||
: [] | ||
); | ||
setSelected([]); | ||
console.log("All users deleted successfully", responses); | ||
} else { | ||
console.error("Not all deletions succeeded"); | ||
} | ||
} catch (error) { | ||
console.error("Error deleting users:", error); | ||
} | ||
}; | ||
|
||
return ( | ||
<div> | ||
<h1> Volunteer Home</h1> | ||
<SearchBar Title="hi" Subtext="yo" /> | ||
<VolunteerTable showPagination={true}></VolunteerTable> | ||
<div className="flex flex-col gap-8"> | ||
<div className="flex items-center justify-between"> | ||
<div className="flex items-center gap-3"> | ||
<Icon icon="mdi:people" width="44" height="44" /> | ||
<div className="text-4xl font-['Kepler_Std'] font-semibold"> | ||
Volunteer List ({users ? users.length : 0}) | ||
</div> | ||
</div> | ||
{selected.length > 0 ? ( | ||
<div className="flex items-center gap-4"> | ||
<div>{selected.length} Selected</div> | ||
<Button | ||
sx={{ | ||
display: "flex", | ||
padding: "10px 18px", | ||
alignItems: "center", | ||
gap: "8px", | ||
borderRadius: "8px", | ||
backgroundColor: "var(--Rose-600, #E61932)", | ||
color: "white", | ||
fontWeight: 600, | ||
textTransform: "none", | ||
"&:hover": { | ||
backgroundColor: "var(--Rose-700, #C11429)", | ||
}, | ||
}} | ||
onClick={() => deleteUsers(selected)} | ||
> | ||
<DeleteOutlineIcon sx={{ width: 20, color: "whitesmoke" }} /> | ||
<div>Delete</div> | ||
</Button> | ||
</div> | ||
) : ( | ||
<div className="h-[44.5px]"></div> | ||
)} | ||
</div> | ||
<SearchBar | ||
onSearchChange={(value) => { | ||
setSearchText(value); | ||
setSelected([]); | ||
}} | ||
/> | ||
{filteredUsers && filteredUsers.length > 0 ? ( | ||
<VolunteerTable | ||
showPagination={true} | ||
fromVolunteerPage | ||
users={filteredUsers} | ||
selected={selected} | ||
setSelected={setSelected} | ||
/> | ||
) : ( | ||
<div className="text-center"> | ||
<div className="relative w-full h-[50vh]"> | ||
<Image | ||
src="/empty_list.png" | ||
alt="Empty List" | ||
layout="fill" | ||
objectFit="contain" | ||
/> | ||
</div> | ||
<div className="text-[#344054] font-['Kepler_Std'] text-3xl font-semibold mt-8"> | ||
No volunteers found! | ||
</div> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,46 @@ | ||
import React from "react"; | ||
import { Icon } from "@iconify/react/dist/iconify.js"; | ||
import { Box, InputBase } from "@mui/material"; | ||
import SearchIcon from "@mui/icons-material/Search"; | ||
|
||
interface SearchBarProps { | ||
Title: string; | ||
Subtext: number | string; | ||
onSearchChange: (searchText: string) => void; | ||
} | ||
|
||
const SearchBar = ({ Title, Subtext }: SearchBarProps) => { | ||
return ( | ||
<div className="flex items-start relative"> | ||
{/* Main Content Box */} | ||
<div className="w-full flex justify-center flex-col shadow-md h-[44px] w-[480px] items-start gap-[16px] rounded-tr-[8px] rounded-tl-[8px] rounded-br-[8px] rounded-bl-[8px] border border-[#Grey/500] bg-[#FFF] relative"> | ||
{/* Close Icon positioned in the top-right corner */} | ||
<div className="flex justify-start"> | ||
<Icon icon="material-symbols-light:search" width="24" height="24" style={{ color: 'grey/500' }} /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
const SearchBar = ({ onSearchChange }: SearchBarProps) => { | ||
return ( | ||
<Box | ||
sx={{ | ||
display: "flex", | ||
padding: "5px 7px", | ||
alignItems: "center", | ||
gap: "8px", | ||
borderRadius: "8px", | ||
border: "1px solid var(--Grey-300, #D0D5DD)", | ||
background: "var(--White, #FFF)", | ||
boxShadow: "0px 1px 2px 0px rgba(16, 24, 40, 0.05)", | ||
width: "100%", | ||
maxWidth: 400, | ||
}} | ||
> | ||
<SearchIcon sx={{ color: "var(--Grey-500, #667085)" }} /> | ||
<InputBase | ||
placeholder="Search" | ||
onChange={(e) => onSearchChange(e.target.value)} | ||
sx={{ | ||
width: "100%", | ||
fontSize: "14px", | ||
color: "var(--Grey-700, #344054)", | ||
"& input::placeholder": { | ||
color: "var(--Grey-500, #667085)", | ||
fontFamily: "Inter, sans-serif", | ||
fontSize: "16px", | ||
fontWeight: 400, | ||
lineHeight: "24px", | ||
}, | ||
}} | ||
/> | ||
</Box> | ||
); | ||
}; | ||
|
||
export default SearchBar; | ||
|
||
|
Oops, something went wrong.