From 5382942047fc50bad6e895138a74a657402591eb Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Tue, 11 Nov 2025 14:50:36 -0500 Subject: [PATCH 01/16] Refactoring visual elements --- .../src/containers/volunteerManagement.tsx | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index b2c9a43f..b84689ee 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -3,7 +3,6 @@ import { Table, TableCaption, Text, - Center, Button, Flex, Input, @@ -14,10 +13,12 @@ import { Portal, NativeSelect, NativeSelectIndicator, + InputGroup, } from '@chakra-ui/react'; import { VolunteerType } from '../types/types'; import { Link } from 'react-router-dom'; import { ChevronDownIcon } from 'lucide-react'; +import { SearchIcon } from 'lucide-react'; import { User } from '../types/types'; import ApiClient from '@api/apiClient'; @@ -136,8 +137,8 @@ const VolunteerManagement: React.FC = () => { }; return ( -
- Pantry Volunteer Management + + Volunteer Management { overflowY="hidden" whiteSpace="nowrap" > - - + + }> + + - @@ -190,21 +193,20 @@ const VolunteerManagement: React.FC = () => { - - + - + - Volunteer Name - Email - Phone + Volunteer Type - Assigned Pantries - + Email + Actions + {filteredVolunteers?.map((volunteer) => ( @@ -212,8 +214,6 @@ const VolunteerManagement: React.FC = () => { {volunteer.firstName} {volunteer.lastName} - {volunteer.email} - {volunteer.phone} {volunteerTypeDropdown({ volunteerType: @@ -224,10 +224,11 @@ const VolunteerManagement: React.FC = () => { })} - @@ -235,7 +236,7 @@ const VolunteerManagement: React.FC = () => { -
+ ); }; From f745231d96bdf193fc3a66f7fbd86cb50d04284d Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Tue, 11 Nov 2025 16:15:36 -0500 Subject: [PATCH 02/16] pagination functionality --- .../src/containers/volunteerManagement.tsx | 73 +++++++++++++++---- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index b84689ee..e1117bc4 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -14,20 +14,25 @@ import { NativeSelect, NativeSelectIndicator, InputGroup, + Pagination, + ButtonGroup, + IconButton } from '@chakra-ui/react'; import { VolunteerType } from '../types/types'; import { Link } from 'react-router-dom'; -import { ChevronDownIcon } from 'lucide-react'; -import { SearchIcon } from 'lucide-react'; +import { ChevronDownIcon, SearchIcon, ChevronRight, ChevronLeft } from 'lucide-react'; import { User } from '../types/types'; import ApiClient from '@api/apiClient'; const VolunteerManagement: React.FC = () => { + const [currentPage, setCurrentPage] = useState(1); const [volunteers, setVolunteers] = useState([]); const [changedVolunteers, setChangedVolunteers] = useState([]); const [searchName, setSearchName] = useState(''); const [checkedTypes, setCheckedTypes] = useState([]); + const pageSize = 2; + useEffect(() => { const fetchVolunteers = async () => { try { @@ -43,6 +48,10 @@ const VolunteerManagement: React.FC = () => { fetchVolunteers(); }, []); + useEffect(() => { + setCurrentPage(1); + }, [searchName, checkedTypes]); + const filteredVolunteers = changedVolunteers.filter((a) => { const fullName = `${a.firstName} ${a.lastName}`.toLowerCase(); return ( @@ -51,6 +60,11 @@ const VolunteerManagement: React.FC = () => { ); }); + const paginatedVolunteers = filteredVolunteers.slice( + (currentPage - 1) * pageSize, + currentPage * pageSize + ); + const volunteerTypeDropdown = ({ volunteerType, volunteerId, @@ -147,15 +161,20 @@ const VolunteerManagement: React.FC = () => { overflowY="hidden" whiteSpace="nowrap" > - - }> - - - + + + } maxW={300}> + + + + + - @@ -209,7 +225,7 @@ const VolunteerManagement: React.FC = () => { - {filteredVolunteers?.map((volunteer) => ( + {paginatedVolunteers?.map((volunteer) => ( {volunteer.firstName} {volunteer.lastName} @@ -235,6 +251,33 @@ const VolunteerManagement: React.FC = () => { ))} + + setCurrentPage(page)}> + + + setCurrentPage((prev) => Math.max(prev - 1, 1))}> + + + + + ( + setCurrentPage(page.value)} + > + {page.value} + + )} + /> + + + setCurrentPage((prev) => Math.min(prev + 1, Math.ceil(filteredVolunteers.length / pageSize)))}> + + + + + ); From d22d1268305fba7b82814a1e95ea1dccf0be50fb Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Tue, 11 Nov 2025 22:23:29 -0500 Subject: [PATCH 03/16] Removing volunteer type filtering and display --- .../src/containers/volunteerManagement.tsx | 150 +----------------- 1 file changed, 4 insertions(+), 146 deletions(-) diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index e1117bc4..87f1ed93 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -1,35 +1,26 @@ import { useEffect, useState } from 'react'; import { Table, - TableCaption, Text, Button, Flex, Input, - Menu, - Checkbox, VStack, Box, - Portal, - NativeSelect, - NativeSelectIndicator, InputGroup, Pagination, ButtonGroup, IconButton } from '@chakra-ui/react'; -import { VolunteerType } from '../types/types'; import { Link } from 'react-router-dom'; -import { ChevronDownIcon, SearchIcon, ChevronRight, ChevronLeft } from 'lucide-react'; +import { SearchIcon, ChevronRight, ChevronLeft } from 'lucide-react'; import { User } from '../types/types'; import ApiClient from '@api/apiClient'; const VolunteerManagement: React.FC = () => { const [currentPage, setCurrentPage] = useState(1); const [volunteers, setVolunteers] = useState([]); - const [changedVolunteers, setChangedVolunteers] = useState([]); const [searchName, setSearchName] = useState(''); - const [checkedTypes, setCheckedTypes] = useState([]); const pageSize = 2; @@ -38,7 +29,6 @@ const VolunteerManagement: React.FC = () => { try { const allVolunteers = await ApiClient.getVolunteers(); setVolunteers(allVolunteers); - setChangedVolunteers(allVolunteers); } catch (error) { alert('Error fetching volunteers'); console.error('Error fetching volunteers: ', error); @@ -50,14 +40,11 @@ const VolunteerManagement: React.FC = () => { useEffect(() => { setCurrentPage(1); - }, [searchName, checkedTypes]); + }, [searchName]); - const filteredVolunteers = changedVolunteers.filter((a) => { + const filteredVolunteers = volunteers.filter((a) => { const fullName = `${a.firstName} ${a.lastName}`.toLowerCase(); - return ( - fullName.includes(searchName.toLowerCase()) && - (checkedTypes.includes(a.role.toUpperCase()) || checkedTypes.length === 0) - ); + return (fullName.includes(searchName.toLowerCase())); }); const paginatedVolunteers = filteredVolunteers.slice( @@ -65,91 +52,12 @@ const VolunteerManagement: React.FC = () => { currentPage * pageSize ); - const volunteerTypeDropdown = ({ - volunteerType, - volunteerId, - }: { - volunteerType: VolunteerType; - volunteerId: number; - }) => { - return ( - - - handleVolunteerTypeChange( - e.target.value as VolunteerType, - volunteerId, - ) - } - > - {Object.entries(DISPLAY_VOLUNTEER_TYPES).map(([key, label]) => ( - - ))} - - - - ); - }; - const handleSearchNameChange = ( event: React.ChangeEvent, ) => { setSearchName(event.target.value); }; - const handleVolunteerFilterChange = (type: string, checked: boolean) => { - if (checked) { - setCheckedTypes([...checkedTypes, type.toUpperCase()]); - } else { - setCheckedTypes( - checkedTypes.filter( - (checkedType) => checkedType !== type.toUpperCase(), - ), - ); - } - }; - - const handleReset = () => { - setSearchName(''); - setCheckedTypes([]); - - setChangedVolunteers(volunteers); - }; - - const handleSaveChanges = async () => { - try { - await Promise.all( - changedVolunteers.map((volunteer) => - ApiClient.updateUserVolunteerRole(volunteer.id, { - role: String(volunteer.role), - }), - ), - ); - setVolunteers(changedVolunteers); - alert('successful save!'); - } catch (error) { - alert('Error updating volunteer type'); - console.error('Error updating volunteer type: ', error); - } - }; - - const handleVolunteerTypeChange = ( - type: VolunteerType, - volunteerId: number, - ) => { - setChangedVolunteers((prev) => - prev.map((a) => (a.id === volunteerId ? { ...a, role: type } : a)), - ); - }; - - const DISPLAY_VOLUNTEER_TYPES: Record = { - LEAD_VOLUNTEER: 'Lead Volunteer', - STANDARD_VOLUNTEER: 'Standard Volunteer', - }; - return ( Volunteer Management @@ -174,52 +82,11 @@ const VolunteerManagement: React.FC = () => { + Add - - - - - - - - {Object.values(VolunteerType).map((volunteerType) => ( - - - handleVolunteerFilterChange(volunteerType, e.checked) - } - > - - - - {DISPLAY_VOLUNTEER_TYPES[ - volunteerType.toUpperCase() - ] || volunteerType} - - - - ))} - - - - - - - - - - Volunteer - Type Email Actions @@ -230,15 +97,6 @@ const VolunteerManagement: React.FC = () => { {volunteer.firstName} {volunteer.lastName} - - {volunteerTypeDropdown({ - volunteerType: - VolunteerType[ - volunteer.role.toUpperCase() as keyof typeof VolunteerType - ], - volunteerId: volunteer.id, - })} - {volunteer.email} From 1d24d7ce754a73f77595a827be103612ffd21980 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 12 Nov 2025 14:12:35 -0500 Subject: [PATCH 04/16] visual updates --- .../src/containers/volunteerManagement.tsx | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 87f1ed93..99b76ad6 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -22,7 +22,7 @@ const VolunteerManagement: React.FC = () => { const [volunteers, setVolunteers] = useState([]); const [searchName, setSearchName] = useState(''); - const pageSize = 2; + const pageSize = 8; useEffect(() => { const fetchVolunteers = async () => { @@ -60,7 +60,7 @@ const VolunteerManagement: React.FC = () => { return ( - Volunteer Management + Volunteer Management { overflowY="hidden" whiteSpace="nowrap" > - + } maxW={300}> { {volunteer.email} - @@ -109,33 +109,34 @@ const VolunteerManagement: React.FC = () => { ))} - - setCurrentPage(page)}> - - - setCurrentPage((prev) => Math.max(prev - 1, 1))}> - - - + + setCurrentPage(page)}> + + + setCurrentPage((prev) => Math.max(prev - 1, 1))}> + + + - ( - setCurrentPage(page.value)} - > - {page.value} - - )} - /> + ( + setCurrentPage(page.value)} + > + {page.value} + + )} + /> - - setCurrentPage((prev) => Math.min(prev + 1, Math.ceil(filteredVolunteers.length / pageSize)))}> - - - - - + + setCurrentPage((prev) => Math.min(prev + 1, Math.ceil(filteredVolunteers.length / pageSize)))}> + + + + + + ); From a941c4133e399607c3ed151ecb55f243cf8e0dd5 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sun, 16 Nov 2025 14:32:14 -0500 Subject: [PATCH 05/16] modal setup for add volunteer --- apps/frontend/src/api/apiClient.ts | 5 ++ .../components/forms/addNewVolunteerModal.tsx | 53 +++++++++++++++++++ .../src/containers/volunteerManagement.tsx | 5 +- apps/frontend/src/types/types.ts | 8 +++ 4 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 apps/frontend/src/components/forms/addNewVolunteerModal.tsx diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 74d2c812..11ea560a 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -11,6 +11,7 @@ import { CreateFoodRequestBody, Pantry, PantryApplicationDto, + UserDto, } from 'types/types'; const defaultBaseUrl = @@ -96,6 +97,10 @@ export class ApiClient { .then((response) => response.data); } + public async postUser(data: UserDto): Promise> { + return this.axiosInstance.post(`/api/users`, data); + } + public async getPantrySSFRep(pantryId: number): Promise { return this.get(`/api/pantries/${pantryId}/ssf-contact`) as Promise; } diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx new file mode 100644 index 00000000..f88f8e54 --- /dev/null +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -0,0 +1,53 @@ +import { + Dialog, + Button, + Text, + Flex, +} from '@chakra-ui/react'; +import { useState } from 'react'; +import ApiClient from '@api/apiClient'; + +const NewVolunteerModal: React.FC = () => { + + const handleSubmit = async () => { + + } + + return ( + + + + + + + + + + Add New Volunteer + + + + + Complete all information in the form to register a new volunteer. + + Log a new donation + + + + + + + + + + ); +}; + + + + + + +export default NewVolunteerModal; \ No newline at end of file diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 99b76ad6..e2fbf9dc 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -16,6 +16,7 @@ import { Link } from 'react-router-dom'; import { SearchIcon, ChevronRight, ChevronLeft } from 'lucide-react'; import { User } from '../types/types'; import ApiClient from '@api/apiClient'; +import NewVolunteerModal from '@components/forms/addNewVolunteerModal'; const VolunteerManagement: React.FC = () => { const [currentPage, setCurrentPage] = useState(1); @@ -78,9 +79,7 @@ const VolunteerManagement: React.FC = () => { onChange={handleSearchNameChange} /> - + diff --git a/apps/frontend/src/types/types.ts b/apps/frontend/src/types/types.ts index cb901272..237f09e8 100644 --- a/apps/frontend/src/types/types.ts +++ b/apps/frontend/src/types/types.ts @@ -115,6 +115,14 @@ export interface User { phone: string; } +export interface UserDto { + email: string, + firstName: string, + lastName: string, + phone: string, + role: Role, +} + export interface FoodRequest { requestId: number; requestedAt: string; From 5a39ddb4c2dd131a9c191042f289363a9d6bea34 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sun, 16 Nov 2025 16:06:10 -0500 Subject: [PATCH 06/16] Modal frontend setup --- .../components/forms/addNewVolunteerModal.tsx | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index f88f8e54..2c048242 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -3,18 +3,59 @@ import { Button, Text, Flex, + Field, + Input, + CloseButton, } from '@chakra-ui/react'; import { useState } from 'react'; +import { Role, UserDto } from "../../types/types"; import ApiClient from '@api/apiClient'; const NewVolunteerModal: React.FC = () => { + const [firstName, setFirstName] = useState(""); + const [lastName, setLastName] = useState(""); + const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); + + const [isOpen, setIsOpen] = useState(false); + + const [error, setError] = useState(""); const handleSubmit = async () => { + if (!firstName || !lastName || !email || !phone) { + setError("Please fill in all fields.*"); + return; + } + + setError(""); + + const newVolunteer: UserDto = { + firstName, + lastName, + email, + phone, + role: Role.STANDARD_VOLUNTEER + }; + + try { + await ApiClient.postUser(newVolunteer); + } catch (error) { + alert('Error creating new user'); + } } + const handleCancel = () => { + setFirstName(""); + setLastName(""); + setEmail(""); + setPhone(""); + setError(""); + setIsOpen(false); + }; + return ( - + - + + + First Name + setFirstName(e.target.value)}/> + + + Last Name + setLastName(e.target.value)}/> + + + + Email + setEmail(e.target.value)}/> + + + Phone Number + setPhone(e.target.value)}/> + + {error && ( + + {error} + + )} + + + - From b10d9ebb9cefaaf4971f33bfed34906461c98afa Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 19 Nov 2025 14:37:03 -0500 Subject: [PATCH 07/16] callbacks and email/phone invalid handling --- .../components/forms/addNewVolunteerModal.tsx | 29 ++++++++++++++----- .../src/containers/volunteerManagement.tsx | 17 +++++++++-- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index 2c048242..912e3ea2 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -11,7 +11,12 @@ import { useState } from 'react'; import { Role, UserDto } from "../../types/types"; import ApiClient from '@api/apiClient'; -const NewVolunteerModal: React.FC = () => { +interface NewVolunteerModalProps { + onSubmitSuccess?: () => void; + onSubmitFail?: () => void; +} + +const NewVolunteerModal: React.FC = ({ onSubmitSuccess, onSubmitFail }) => { const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); const [email, setEmail] = useState(""); @@ -39,13 +44,21 @@ const NewVolunteerModal: React.FC = () => { try { await ApiClient.postUser(newVolunteer); - } catch (error) { - alert('Error creating new user'); + if (onSubmitSuccess) onSubmitSuccess(); + handleClear(); + } catch (error: any) { + const message = error.response?.data?.message; + const hasEmailOrPhoneError = Array.isArray(message) && message.some((msg: any) => typeof msg === "string" && (msg.toLowerCase().includes("email") || msg.toLowerCase().includes("phone"))); + if (hasEmailOrPhoneError) { + setError("Please specify a valid email and phone number") + } else { + if (onSubmitFail) onSubmitFail(); + handleClear(); + } } - } - const handleCancel = () => { + const handleClear = () => { setFirstName(""); setLastName(""); setEmail(""); @@ -65,7 +78,7 @@ const NewVolunteerModal: React.FC = () => { - + Add New Volunteer setIsOpen(false)} size="sm" position="absolute" top={3} right={3}/> @@ -98,8 +111,8 @@ const NewVolunteerModal: React.FC = () => { )} - - + + diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index e2fbf9dc..00a065ed 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -10,7 +10,8 @@ import { InputGroup, Pagination, ButtonGroup, - IconButton + IconButton, + Alert } from '@chakra-ui/react'; import { Link } from 'react-router-dom'; import { SearchIcon, ChevronRight, ChevronLeft } from 'lucide-react'; @@ -23,6 +24,8 @@ const VolunteerManagement: React.FC = () => { const [volunteers, setVolunteers] = useState([]); const [searchName, setSearchName] = useState(''); + const [alertMessage, setAlertMessage] = useState(''); + const pageSize = 8; useEffect(() => { @@ -62,6 +65,12 @@ const VolunteerManagement: React.FC = () => { return ( Volunteer Management + {alertMessage && ( + + + {alertMessage} + + )} { onChange={handleSearchNameChange} /> - + { + setAlertMessage("Volunteer added"); + setTimeout(() => setAlertMessage(""), 3000); + }} + /> From c85a3fa3a50df12f4a24692083e02457cb83ade2 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sun, 30 Nov 2025 11:48:13 -0500 Subject: [PATCH 08/16] refactoring to closer align with figma --- .../components/forms/addNewVolunteerModal.tsx | 4 +-- .../src/containers/volunteerManagement.tsx | 34 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index 912e3ea2..b082e721 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -70,8 +70,8 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, return ( - diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 00a065ed..a1c3eaa5 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react'; import { Table, Text, - Button, Flex, Input, VStack, @@ -11,9 +10,9 @@ import { Pagination, ButtonGroup, IconButton, - Alert + Alert, + Link, } from '@chakra-ui/react'; -import { Link } from 'react-router-dom'; import { SearchIcon, ChevronRight, ChevronLeft } from 'lucide-react'; import { User } from '../types/types'; import ApiClient from '@api/apiClient'; @@ -63,8 +62,8 @@ const VolunteerManagement: React.FC = () => { }; return ( - - Volunteer Management + + Volunteer Management {alertMessage && ( @@ -72,7 +71,7 @@ const VolunteerManagement: React.FC = () => { )} { > - } maxW={300}> + } maxW={300}> { @@ -95,15 +101,15 @@ const VolunteerManagement: React.FC = () => { /> - + - Volunteer - Email - Actions + Volunteer + Email + Actions - + {paginatedVolunteers?.map((volunteer) => ( @@ -113,9 +119,9 @@ const VolunteerManagement: React.FC = () => { {volunteer.email} - + ))} From 91ff9fdd42999c36692377aa624ae1534427644e Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 3 Dec 2025 21:15:27 -0500 Subject: [PATCH 09/16] Aligning add volunteer modal with figma --- .../components/forms/addNewVolunteerModal.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index b082e721..a29229fe 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -77,42 +77,42 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, - - + + Add New Volunteer - setIsOpen(false)} size="sm" position="absolute" top={3} right={3}/> + setIsOpen(false)} size="md" position="absolute" top={3} right={3}/> - - + + Complete all information in the form to register a new volunteer. First Name - setFirstName(e.target.value)}/> + setFirstName(e.target.value)}/> Last Name - setLastName(e.target.value)}/> + setLastName(e.target.value)}/> Email - setEmail(e.target.value)}/> + setEmail(e.target.value)}/> Phone Number - setPhone(e.target.value)}/> + setPhone(e.target.value)}/> {error && ( - + {error} )} - - + + From 21d9104b048b9a7ddcdf1d89e0a0c7ef28eeebeb Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 3 Dec 2025 21:41:25 -0500 Subject: [PATCH 10/16] Refining submit status alerts --- .../src/components/forms/addNewVolunteerModal.tsx | 2 +- .../src/containers/volunteerManagement.tsx | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index a29229fe..62b4de93 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -50,7 +50,7 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, const message = error.response?.data?.message; const hasEmailOrPhoneError = Array.isArray(message) && message.some((msg: any) => typeof msg === "string" && (msg.toLowerCase().includes("email") || msg.toLowerCase().includes("phone"))); if (hasEmailOrPhoneError) { - setError("Please specify a valid email and phone number") + setError("Please specify a valid email and phone number*") } else { if (onSubmitFail) onSubmitFail(); handleClear(); diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index a1c3eaa5..6f2b09c3 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -24,6 +24,7 @@ const VolunteerManagement: React.FC = () => { const [searchName, setSearchName] = useState(''); const [alertMessage, setAlertMessage] = useState(''); + const [submitSuccess, setSubmitSuccess] = useState(false); const pageSize = 8; @@ -39,7 +40,7 @@ const VolunteerManagement: React.FC = () => { }; fetchVolunteers(); - }, []); + }, [alertMessage]); useEffect(() => { setCurrentPage(1); @@ -65,9 +66,9 @@ const VolunteerManagement: React.FC = () => { Volunteer Management {alertMessage && ( - + - {alertMessage} + {alertMessage} )} { /> { - setAlertMessage("Volunteer added"); + setAlertMessage("Volunteer added."); + setSubmitSuccess(true); + setTimeout(() => setAlertMessage(""), 3000); + }} onSubmitFail={() => { + setAlertMessage("Volunteer could not be added."); + setSubmitSuccess(false); setTimeout(() => setAlertMessage(""), 3000); }} /> From a5b8bf365fe4058f0791ed3829bfddc9986e2c37 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sat, 6 Dec 2025 11:20:27 -0500 Subject: [PATCH 11/16] minor visual updates --- apps/frontend/src/containers/volunteerManagement.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 6f2b09c3..23fda66d 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -63,8 +63,8 @@ const VolunteerManagement: React.FC = () => { }; return ( - - Volunteer Management + + Volunteer Management {alertMessage && ( @@ -89,10 +89,9 @@ const VolunteerManagement: React.FC = () => { ps="7" onChange={handleSearchNameChange} color="neutral.600" - fontFamily="Karrik" + fontFamily="ibm" fontWeight={400} fontSize="14px" - _placeholder={{ color: "neutral.600", fontFamily: "Karrik", fontWeight: 400, fontSize: 14 }} /> { @@ -126,7 +125,7 @@ const VolunteerManagement: React.FC = () => { - View assigned pantries + View Assigned Pantries From e6f850272630b13408403c5829535d54598ccb3c Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 10 Dec 2025 21:09:07 -0500 Subject: [PATCH 12/16] Adding volunteer initial icons --- .../src/containers/volunteerManagement.tsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 23fda66d..d2b81220 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -28,6 +28,8 @@ const VolunteerManagement: React.FC = () => { const pageSize = 8; + const USER_ICON_COLORS = ['#F89E19', '#CC3538', '#2795A5', '#2B4E60']; + useEffect(() => { const fetchVolunteers = async () => { try { @@ -118,7 +120,27 @@ const VolunteerManagement: React.FC = () => { {paginatedVolunteers?.map((volunteer) => ( - {volunteer.firstName} {volunteer.lastName} + + + {volunteer.firstName + .charAt(0) + .toUpperCase()} + {volunteer.lastName + .charAt(0) + .toUpperCase()} + + {volunteer.firstName} {volunteer.lastName} + {volunteer.email} From 66888e46694e7582162bde9050d689349793c797 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Fri, 12 Dec 2025 11:22:45 -0500 Subject: [PATCH 13/16] minor visual refactoring --- .../src/components/forms/addNewVolunteerModal.tsx | 14 +++++++------- .../src/containers/volunteerManagement.tsx | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index 62b4de93..2fd869cb 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -75,8 +75,8 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, - - + + Add New Volunteer @@ -89,20 +89,20 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, - First Name + First Name setFirstName(e.target.value)}/> - Last Name + Last Name setLastName(e.target.value)}/> - Email + Email setEmail(e.target.value)}/> - Phone Number + Phone Number setPhone(e.target.value)}/> {error && ( @@ -111,8 +111,8 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, )} - + diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index d2b81220..8ac25056 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -65,7 +65,7 @@ const VolunteerManagement: React.FC = () => { }; return ( - + Volunteer Management {alertMessage && ( @@ -92,8 +92,9 @@ const VolunteerManagement: React.FC = () => { onChange={handleSearchNameChange} color="neutral.600" fontFamily="ibm" - fontWeight={400} + fontWeight="semibold" fontSize="14px" + _focusVisible={{ boxShadow: "none", outline: "none" }} /> { From 2d286af3720f6ba2cff6f96ddab49cc5799ad653 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sat, 13 Dec 2025 11:59:23 -0500 Subject: [PATCH 14/16] Refactoring add volunteer modal to use USPhoneInput --- .../src/components/forms/addNewVolunteerModal.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index 2fd869cb..c6510ded 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -10,6 +10,7 @@ import { import { useState } from 'react'; import { Role, UserDto } from "../../types/types"; import ApiClient from '@api/apiClient'; +import { USPhoneInput } from './usPhoneInput'; interface NewVolunteerModalProps { onSubmitSuccess?: () => void; @@ -103,7 +104,15 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, Phone Number - setPhone(e.target.value)}/> + {error && ( From 96fb6d6cf8f9461732b0b96aa7702ada07e19340 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sun, 14 Dec 2025 15:59:28 -0500 Subject: [PATCH 15/16] visual refactoring --- .../components/forms/addNewVolunteerModal.tsx | 19 ++++++++++++------- .../src/containers/volunteerManagement.tsx | 4 ++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index c6510ded..6fe848b1 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -28,8 +28,9 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, const [error, setError] = useState(""); const handleSubmit = async () => { - if (!firstName || !lastName || !email || !phone) { - setError("Please fill in all fields.*"); + console.log("RAW phone value:", phone); + if (!firstName || !lastName || !email || !phone || phone === "+1") { + setError("Please fill in all fields. *"); return; } @@ -49,9 +50,13 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, handleClear(); } catch (error: any) { const message = error.response?.data?.message; - const hasEmailOrPhoneError = Array.isArray(message) && message.some((msg: any) => typeof msg === "string" && (msg.toLowerCase().includes("email") || msg.toLowerCase().includes("phone"))); - if (hasEmailOrPhoneError) { - setError("Please specify a valid email and phone number*") + const hasEmailError = Array.isArray(message) && message.some((msg: any) => typeof msg === "string" && (msg.toLowerCase().includes("email"))); + const hasPhoneError = Array.isArray(message) && message.some((msg: any) => typeof msg === "string" && (msg.toLowerCase().includes("phone"))); + + if (hasEmailError) { + setError("Please specify a valid email. *") + } else if (hasPhoneError) { + setError("Please specify a valid phone number. *") } else { if (onSubmitFail) onSubmitFail(); handleClear(); @@ -71,7 +76,7 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, return ( - @@ -119,7 +124,7 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, {error} )} - + diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 8ac25056..94885fe2 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -124,7 +124,7 @@ const VolunteerManagement: React.FC = () => { { ))} - + setCurrentPage(page)}> From 23d500f449f98d36ca2b64bff41081a3857fed5a Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sun, 21 Dec 2025 12:12:04 -0500 Subject: [PATCH 16/16] visual refactoring --- apps/frontend/src/api/apiClient.ts | 2 +- .../src/components/forms/addNewVolunteerModal.tsx | 9 ++++++--- apps/frontend/src/containers/volunteerManagement.tsx | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 11ea560a..bdd49bbd 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -97,7 +97,7 @@ export class ApiClient { .then((response) => response.data); } - public async postUser(data: UserDto): Promise> { + public async postUser(data: UserDto): Promise { return this.axiosInstance.post(`/api/users`, data); } diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index 6fe848b1..9ca2221e 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -6,11 +6,13 @@ import { Field, Input, CloseButton, + Box } from '@chakra-ui/react'; import { useState } from 'react'; import { Role, UserDto } from "../../types/types"; import ApiClient from '@api/apiClient'; import { USPhoneInput } from './usPhoneInput'; +import { PlusIcon } from 'lucide-react'; interface NewVolunteerModalProps { onSubmitSuccess?: () => void; @@ -76,8 +78,9 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, return ( - @@ -124,7 +127,7 @@ const NewVolunteerModal: React.FC = ({ onSubmitSuccess, {error} )} - + diff --git a/apps/frontend/src/containers/volunteerManagement.tsx b/apps/frontend/src/containers/volunteerManagement.tsx index 94885fe2..c227b567 100644 --- a/apps/frontend/src/containers/volunteerManagement.tsx +++ b/apps/frontend/src/containers/volunteerManagement.tsx @@ -147,7 +147,7 @@ const VolunteerManagement: React.FC = () => { {volunteer.email} - + View Assigned Pantries