diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index 6e5dbab9b..ab9ab8434 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -72,6 +72,7 @@ export class OrdersService { 'assignee.id', 'assignee.firstName', 'assignee.lastName', + 'assignee.active', ]); if (filters?.status) { @@ -109,6 +110,7 @@ export class OrdersService { 'assignee.id', 'assignee.firstName', 'assignee.lastName', + 'assignee.active', ]) .where('order.assigneeId = :volunteerId', { volunteerId }) .getMany(); @@ -150,6 +152,7 @@ export class OrdersService { 'assignee.id', 'assignee.firstName', 'assignee.lastName', + 'assignee.active', ]) .where('order.assigneeId = :volunteerId', { volunteerId }) .orderBy('order.createdAt', 'DESC') @@ -655,6 +658,7 @@ ${request.pantry.shipmentAddressCity}, ${request.pantry.shipmentAddressState} ${ id: order.assignee.id, firstName: order.assignee.firstName, lastName: order.assignee.lastName, + active: order.assignee.active, }, })); } diff --git a/apps/backend/src/pantries/pantries.controller.spec.ts b/apps/backend/src/pantries/pantries.controller.spec.ts index e943c5d28..c55aa1fa3 100644 --- a/apps/backend/src/pantries/pantries.controller.spec.ts +++ b/apps/backend/src/pantries/pantries.controller.spec.ts @@ -394,6 +394,7 @@ describe('PantriesController', () => { lastName: 'Johnson', email: 'alice.johnson@example.com', phone: '(617) 555-0100', + active: true, }, { userId: 11, @@ -401,6 +402,7 @@ describe('PantriesController', () => { lastName: 'Williams', email: 'bob.williams@example.com', phone: '(617) 555-0101', + active: false, }, ], }, diff --git a/apps/backend/src/pantries/pantries.service.spec.ts b/apps/backend/src/pantries/pantries.service.spec.ts index a5387fee4..c73969c0f 100644 --- a/apps/backend/src/pantries/pantries.service.spec.ts +++ b/apps/backend/src/pantries/pantries.service.spec.ts @@ -8,7 +8,6 @@ import { ConflictException, ForbiddenException, InternalServerErrorException, - Logger, NotFoundException, } from '@nestjs/common'; import { PantryApplicationDto } from './dtos/pantry-application.dto'; @@ -943,10 +942,32 @@ describe('PantriesService', () => { expect(v.lastName).toBeDefined(); expect(v.email).toBeDefined(); expect(v.phone).toBeDefined(); + expect(typeof v.active).toBe('boolean'); }); }); }); + it('returns the active status for each volunteer', async () => { + await testDataSource.query( + `UPDATE users SET active = false WHERE email = 'james.t@volunteer.org'`, + ); + + const result = await service.getApprovedPantriesWithVolunteers(); + const volunteers = result.flatMap((p) => p.volunteers); + + const inactiveVolunteer = volunteers.find( + (v) => v.email === 'james.t@volunteer.org', + ); + expect(inactiveVolunteer).toBeDefined(); + expect(inactiveVolunteer?.active).toBe(false); + + const activeVolunteer = volunteers.find( + (v) => v.email === 'maria.g@volunteer.org', + ); + expect(activeVolunteer).toBeDefined(); + expect(activeVolunteer?.active).toBe(true); + }); + it('should return empty volunteers array when pantry has no volunteers', async () => { await service.addPantry({ contactFirstName: 'Test', diff --git a/apps/backend/src/pantries/pantries.service.ts b/apps/backend/src/pantries/pantries.service.ts index e8b42e2ea..36d341e83 100644 --- a/apps/backend/src/pantries/pantries.service.ts +++ b/apps/backend/src/pantries/pantries.service.ts @@ -531,6 +531,7 @@ export class PantriesService { lastName: volunteer.lastName, email: volunteer.email, phone: volunteer.phone, + active: volunteer.active, })), })); } diff --git a/apps/backend/src/pantries/types.ts b/apps/backend/src/pantries/types.ts index 543f6883c..f671dbcd7 100644 --- a/apps/backend/src/pantries/types.ts +++ b/apps/backend/src/pantries/types.ts @@ -30,6 +30,7 @@ export interface OrderSummary { id: number; firstName: string; lastName: string; + active: boolean; }; } @@ -39,6 +40,7 @@ export interface AssignedVolunteer { lastName: string; email: string; phone: string; + active: boolean; } export enum RefrigeratedDonation { diff --git a/apps/backend/src/volunteers/types.ts b/apps/backend/src/volunteers/types.ts index 1f67ad033..cab684fd0 100644 --- a/apps/backend/src/volunteers/types.ts +++ b/apps/backend/src/volunteers/types.ts @@ -24,4 +24,5 @@ export type OrderAssignee = { id: number; firstName: string; lastName: string; + active: boolean; }; diff --git a/apps/backend/src/volunteers/volunteers.controller.spec.ts b/apps/backend/src/volunteers/volunteers.controller.spec.ts index 29381e36f..8178871a1 100644 --- a/apps/backend/src/volunteers/volunteers.controller.spec.ts +++ b/apps/backend/src/volunteers/volunteers.controller.spec.ts @@ -103,14 +103,18 @@ describe('VolunteersController', () => { })[], ); - const result = await controller.getAllVolunteers(); + const req: AuthenticatedRequest = { + user: { id: 99 }, + } as AuthenticatedRequest; + + const result = await controller.getAllVolunteers(req); expect(result).toEqual(expectedVolunteers); expect(result.length).toBe(2); expect(result.every((u) => u.role === Role.VOLUNTEER)).toBe(true); expect( mockVolunteersService.getVolunteersAndPantryAssignments, - ).toHaveBeenCalled(); + ).toHaveBeenCalledWith(99); }); }); @@ -179,7 +183,12 @@ describe('VolunteersController', () => { const req: AuthenticatedRequest = { user: { id: 6 }, } as AuthenticatedRequest; - const assignee = { id: 6, firstName: 'James', lastName: 'Thomas' }; + const assignee = { + id: 6, + firstName: 'James', + lastName: 'Thomas', + active: true, + }; const recentOrders: Partial[] = [ { orderId: 4, diff --git a/apps/backend/src/volunteers/volunteers.controller.ts b/apps/backend/src/volunteers/volunteers.controller.ts index 70f05e3c8..2e176eef8 100644 --- a/apps/backend/src/volunteers/volunteers.controller.ts +++ b/apps/backend/src/volunteers/volunteers.controller.ts @@ -22,15 +22,19 @@ export class VolunteersController { @Roles(Role.ADMIN) @Get('/') - async getAllVolunteers(): Promise { - return this.volunteersService.getVolunteersAndPantryAssignments(); + async getAllVolunteers( + @Req() req: AuthenticatedRequest, + ): Promise { + return this.volunteersService.getVolunteersAndPantryAssignments( + req.user.id, + ); } @CheckOwnership({ idParam: 'id', resolver: resolveVolunteerAuthorizedUserIds, }) - @Roles(Role.VOLUNTEER) + @Roles(Role.VOLUNTEER, Role.ADMIN) @Get('/:id/pantries') async getVolunteerPantries( @Param('id', ParseIntPipe) id: number, diff --git a/apps/backend/src/volunteers/volunteers.service.spec.ts b/apps/backend/src/volunteers/volunteers.service.spec.ts index ab4b95679..8d09e34a1 100644 --- a/apps/backend/src/volunteers/volunteers.service.spec.ts +++ b/apps/backend/src/volunteers/volunteers.service.spec.ts @@ -145,7 +145,7 @@ describe('VolunteersService', () => { }); describe('getVolunteersAndPantryAssignments', () => { - it('returns an empty array when there are no volunteers', async () => { + it('returns only admins when there are no volunteers', async () => { await testDataSource.query(`DELETE FROM allocations`); await testDataSource.query(`DELETE FROM orders`); await testDataSource.query( @@ -154,14 +154,59 @@ describe('VolunteersService', () => { const result = await service.getVolunteersAndPantryAssignments(); - expect(result).toEqual([]); + expect(result).toEqual([ + { + id: 1, + firstName: 'John', + lastName: 'Smith', + email: 'john.smith@ssf.org', + phone: '555-010-0101', + role: 'admin', + userCognitoSub: '', + active: true, + pantryIds: [], + }, + { + id: 2, + firstName: 'Sarah', + lastName: 'Johnson', + email: 'sarah.j@ssf.org', + phone: '555-010-0102', + role: 'admin', + userCognitoSub: '', + active: true, + pantryIds: [], + }, + ]); }); - it('returns all volunteers with their pantry assignments', async () => { + it('returns all volunteers and admins with their pantry assignments', async () => { const result = await service.getVolunteersAndPantryAssignments(); - expect(result.length).toEqual(4); + expect(result.length).toEqual(6); expect(result).toEqual([ + { + id: 1, + firstName: 'John', + lastName: 'Smith', + email: 'john.smith@ssf.org', + phone: '555-010-0101', + role: 'admin', + userCognitoSub: '', + active: true, + pantryIds: [], + }, + { + id: 2, + firstName: 'Sarah', + lastName: 'Johnson', + email: 'sarah.j@ssf.org', + phone: '555-010-0102', + role: 'admin', + userCognitoSub: '', + active: true, + pantryIds: [], + }, { id: 6, firstName: 'James', diff --git a/apps/backend/src/volunteers/volunteers.service.ts b/apps/backend/src/volunteers/volunteers.service.ts index a904eca69..d2e1f836a 100644 --- a/apps/backend/src/volunteers/volunteers.service.ts +++ b/apps/backend/src/volunteers/volunteers.service.ts @@ -38,18 +38,23 @@ export class VolunteersService { return volunteer; } - async getVolunteersAndPantryAssignments(): Promise { + async getVolunteersAndPantryAssignments( + excludeUserId?: number, + ): Promise { const volunteers = await this.usersService.findUsersByRoles([ Role.VOLUNTEER, + Role.ADMIN, ]); - return volunteers.map((v) => { - const { pantries, ...volunteerWithoutPantries } = v; - return { - ...volunteerWithoutPantries, - pantryIds: pantries?.map((p) => p.pantryId) || [], - }; - }); + return volunteers + .filter((v) => v.id !== excludeUserId) + .map((v) => { + const { pantries, ...volunteerWithoutPantries } = v; + return { + ...volunteerWithoutPantries, + pantryIds: pantries?.map((p) => p.pantryId) || [], + }; + }); } async getVolunteerPantries(volunteerId: number): Promise { diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 5634ed44b..c87548f4d 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -293,6 +293,14 @@ export class ApiClient { await this.axiosInstance.patch(`/api/users/${userId}/promote-volunteer`); } + public async deactivateUser(userId: number): Promise { + await this.axiosInstance.patch(`/api/users/${userId}/deactivate`); + } + + public async reactivateUser(userId: number): Promise { + await this.axiosInstance.patch(`/api/users/${userId}/reactivate`); + } + public async getFoodRequest(requestId: number): Promise { return this.axiosInstance .get(`/api/requests/${requestId}`) diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index 063cffa7d..cfaf7a919 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -7,7 +7,7 @@ import ApplicationSubmitted from '@containers/applicationSubmitted'; import { submitPantryApplicationForm } from '@components/forms/pantryApplicationForm'; import ApprovePantries from '@containers/approvePantries'; import PantryApplicationDetails from '@containers/pantryApplicationDetails'; -import VolunteerManagement from '@containers/volunteerManagement'; +import VolunteerManagement from '@containers/userManagement'; import AdminDonation from '@containers/adminDonation'; import Homepage from '@containers/homepage'; import AdminOrderManagement from '@containers/adminOrderManagement'; diff --git a/apps/frontend/src/chakra-ui.d.ts b/apps/frontend/src/chakra-ui.d.ts index 98db62220..1a51c05d7 100644 --- a/apps/frontend/src/chakra-ui.d.ts +++ b/apps/frontend/src/chakra-ui.d.ts @@ -24,7 +24,7 @@ declare module '@chakra-ui/react' { // Menu components export interface MenuTriggerProps extends ComponentPropsStrictChildren {} - export interface MenuContentProps extends ComponentPropsStrictChildren {} + export interface MenuContentProps extends ComponentPropsLenientChildren {} export interface MenuItemProps extends ComponentPropsLenientChildren {} export interface MenuPositionerProps extends ComponentPropsStrictChildren {} export interface MenuRootProps extends ComponentPropsStrictChildren {} diff --git a/apps/frontend/src/components/Navbar.tsx b/apps/frontend/src/components/Navbar.tsx index 93e7fea56..88b658c2a 100644 --- a/apps/frontend/src/components/Navbar.tsx +++ b/apps/frontend/src/components/Navbar.tsx @@ -29,10 +29,8 @@ const ROLE_NAV_SECTIONS: Record = { [Role.ADMIN]: [ { type: 'group', - label: 'Volunteers', - children: [ - { label: 'Volunteer Management', to: ROUTES.VOLUNTEER_MANAGEMENT }, - ], + label: 'Users', + children: [{ label: 'User Management', to: ROUTES.VOLUNTEER_MANAGEMENT }], }, { type: 'group', diff --git a/apps/frontend/src/components/dashboardCard.tsx b/apps/frontend/src/components/dashboardCard.tsx index 406933496..251137601 100644 --- a/apps/frontend/src/components/dashboardCard.tsx +++ b/apps/frontend/src/components/dashboardCard.tsx @@ -197,7 +197,12 @@ const DashboardCard: React.FC = ({ w="30px" h="30px" borderRadius="full" - bg={USER_ICON_COLORS[assignee.id % USER_ICON_COLORS.length]} + bg={ + assignee.active + ? USER_ICON_COLORS[assignee.id % USER_ICON_COLORS.length] + : 'neutral.300' + } + opacity={assignee.active ? 1 : 0.6} color="white" display="flex" alignItems="center" diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx index e0dcafd34..1c0e3e96e 100644 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx @@ -128,7 +128,7 @@ const NewVolunteerModal: React.FC = ({ fontFamily="Inter" color="#000" > - Add New Volunteer + Add New User setIsOpen(false)} @@ -140,7 +140,7 @@ const NewVolunteerModal: React.FC = ({ - Complete all information in the form to register a new volunteer. + Complete all information in the form to register a new user. diff --git a/apps/frontend/src/components/forms/assignVolunteersModal.tsx b/apps/frontend/src/components/forms/assignVolunteersModal.tsx index 5b1012fab..e4a4dffe4 100644 --- a/apps/frontend/src/components/forms/assignVolunteersModal.tsx +++ b/apps/frontend/src/components/forms/assignVolunteersModal.tsx @@ -17,6 +17,7 @@ import { AlertStatus, ApprovedPantryResponse, Assignments, + Role, } from '../../types/types'; import { SearchIcon } from 'lucide-react'; import { getInitials, USER_ICON_COLORS } from '@utils/utils'; @@ -65,11 +66,13 @@ const AssignVolunteersModal: React.FC = ({ const assignedIds = new Set(pantry.volunteers.map((v) => v.userId)); - const normalized: VolunteerDisplay[] = allVolunteers.map((v) => ({ - userId: v.id, - firstName: v.firstName, - lastName: v.lastName, - })); + const normalized: VolunteerDisplay[] = allVolunteers + .filter((v) => v.active && v.role === Role.VOLUNTEER) + .map((v) => ({ + userId: v.id, + firstName: v.firstName, + lastName: v.lastName, + })); setVolunteers(normalized); setSelectedIds(new Set(assignedIds)); diff --git a/apps/frontend/src/components/forms/confirmActionModal.tsx b/apps/frontend/src/components/forms/confirmActionModal.tsx new file mode 100644 index 000000000..2bebeb0d7 --- /dev/null +++ b/apps/frontend/src/components/forms/confirmActionModal.tsx @@ -0,0 +1,80 @@ +import { Dialog, Text, Button, CloseButton } from '@chakra-ui/react'; +import { useModalBodyCleanup } from '../../hooks/modalBodyCleanup'; + +interface ConfirmActionModalProps { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; + volunteerName: string; + action: 'activate' | 'deactivate'; +} + +const ConfirmActionModal: React.FC = ({ + isOpen, + onClose, + onConfirm, + volunteerName, + action, +}) => { + useModalBodyCleanup(); + + return ( + !e.open && onClose()} + > + + + + + + {action === 'activate' ? 'Activate user' : 'Deactivate user'} + + + + + + + + + Are you sure you want to {action} {volunteerName}? + + + + + + + + + + + ); +}; + +export default ConfirmActionModal; diff --git a/apps/frontend/src/containers/adminDashboard.tsx b/apps/frontend/src/containers/adminDashboard.tsx index 24a148a86..eca2e7e8e 100644 --- a/apps/frontend/src/containers/adminDashboard.tsx +++ b/apps/frontend/src/containers/adminDashboard.tsx @@ -218,6 +218,7 @@ const AdminDashboard: React.FC = () => { id: order.assignee.id, firstName: order.assignee.firstName, lastName: order.assignee.lastName, + active: order.assignee.active, }} onLinkClick={() => navigate(`/admin-order-management?orderId=${order.orderId}`) diff --git a/apps/frontend/src/containers/adminOrderManagement.tsx b/apps/frontend/src/containers/adminOrderManagement.tsx index dc293f146..d267ae51e 100644 --- a/apps/frontend/src/containers/adminOrderManagement.tsx +++ b/apps/frontend/src/containers/adminOrderManagement.tsx @@ -769,7 +769,12 @@ const OrderStatusSection: React.FC = ({ { {pantry.volunteers && pantry.volunteers.length > 0 ? ( (() => { - const volunteers = pantry.volunteers; + const volunteers = pantry.volunteers.filter( + (volunteer) => volunteer.active, + ); const maxVisible = 3; const hasOverflow = volunteers.length > maxVisible; @@ -331,10 +333,14 @@ const AdminPantryManagement: React.FC = () => { key={volunteer.userId} borderRadius="full" bg={ - USER_ICON_COLORS[ - volunteer.userId % USER_ICON_COLORS.length - ] + volunteer.active + ? USER_ICON_COLORS[ + volunteer.userId % + USER_ICON_COLORS.length + ] + : 'neutral.300' } + opacity={volunteer.active ? 1 : 0.6} width="33px" height="33px" display="flex" diff --git a/apps/frontend/src/containers/homepage.tsx b/apps/frontend/src/containers/homepage.tsx index fca3ab8e0..0e9e931c7 100644 --- a/apps/frontend/src/containers/homepage.tsx +++ b/apps/frontend/src/containers/homepage.tsx @@ -148,7 +148,7 @@ const Homepage: React.FC = () => { - Volunteer Management + User Management diff --git a/apps/frontend/src/containers/loginPage.tsx b/apps/frontend/src/containers/loginPage.tsx index cc08a821e..a92fa73d5 100644 --- a/apps/frontend/src/containers/loginPage.tsx +++ b/apps/frontend/src/containers/loginPage.tsx @@ -75,6 +75,13 @@ const LoginPage: React.FC = () => { navigate(from, { replace: true }); return; } + if (error.message === 'User is disabled.') { + setAlertMessage( + 'Your account has been deactivated.', + AlertStatus.ERROR, + ); + return; + } if ( error.name === 'NotAuthorizedException' || error.name === 'UserNotFoundException' diff --git a/apps/frontend/src/containers/pantryDashboard.tsx b/apps/frontend/src/containers/pantryDashboard.tsx index 579775078..d59af080d 100644 --- a/apps/frontend/src/containers/pantryDashboard.tsx +++ b/apps/frontend/src/containers/pantryDashboard.tsx @@ -182,6 +182,7 @@ const PantryDashboard: React.FC = () => { id: order.assignee.id, firstName: order.assignee.firstName, lastName: order.assignee.lastName, + active: order.assignee.active, }} onLinkClick={() => navigate( diff --git a/apps/frontend/src/containers/pantryOrderManagement.tsx b/apps/frontend/src/containers/pantryOrderManagement.tsx index d4b66c447..b4b5532db 100644 --- a/apps/frontend/src/containers/pantryOrderManagement.tsx +++ b/apps/frontend/src/containers/pantryOrderManagement.tsx @@ -552,10 +552,13 @@ const OrderStatusSection: React.FC = ({ { const [searchName, setSearchName] = useState(''); const [selectedVolunteer, setSelectedVolunteer] = useState(null); const [isPromoteModalOpen, setIsPromoteModalOpen] = useState(false); + const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false); const [alertState, setAlertMessage] = useAlert(); @@ -77,10 +80,37 @@ const VolunteerManagement: React.FC = () => { } }; - const filteredVolunteers = volunteers.filter((a) => { - const fullName = `${a.firstName} ${a.lastName}`.toLowerCase(); - return fullName.includes(searchName.toLowerCase()); - }); + const handleToggleActive = async () => { + if (!selectedVolunteer) return; + + const wasActive = selectedVolunteer.active; + const fullName = `${selectedVolunteer.firstName} ${selectedVolunteer.lastName}`; + try { + if (wasActive) { + await ApiClient.deactivateUser(selectedVolunteer.id); + } else { + await ApiClient.reactivateUser(selectedVolunteer.id); + } + setAlertMessage( + `${fullName} has been ${wasActive ? 'deactivated' : 'activated'}.`, + AlertStatus.INFO, + ); + fetchVolunteers(); + } catch { + setAlertMessage( + `Failed to ${wasActive ? 'deactivate' : 'activate'} user.`, + AlertStatus.ERROR, + ); + } + }; + + const filteredVolunteers = volunteers + .filter((a) => { + const fullName = `${a.firstName} ${a.lastName}`.toLowerCase(); + return fullName.includes(searchName.toLowerCase()); + }) + // Deactivated users sort to the bottom of the list. + .sort((a, b) => Number(b.active) - Number(a.active)); const paginatedVolunteers = filteredVolunteers.slice( (currentPage - 1) * pageSize, @@ -96,7 +126,7 @@ const VolunteerManagement: React.FC = () => { return ( - Volunteer Management + User Management {alertState && ( { { - setAlertMessage('Volunteer added.', AlertStatus.INFO); + setAlertMessage('User added.', AlertStatus.INFO); }} onSubmitFail={() => { - setAlertMessage( - 'Volunteer could not be added.', - AlertStatus.ERROR, - ); + setAlertMessage('User could not be added.', AlertStatus.ERROR); }} /> @@ -160,7 +187,14 @@ const VolunteerManagement: React.FC = () => { textStyle="p2" fontWeight={600} > - Volunteer + Users + + + Status { { {volunteer.firstName} {volunteer.lastName} - {volunteer.email} - - + - navigate( - `${ROUTES.PANTRY_MANAGEMENT}?volunteerId=${volunteer.id}`, - ) - } + fontWeight={500} + fontSize="12px" + bg={volunteer.active ? 'teal.200' : 'neutral.300'} + color={volunteer.active ? 'teal.hover' : 'black'} > - View Assigned Pantries - + {volunteer.active ? 'Active' : 'Deactivated'} + + + {volunteer.email} + + {volunteer.role === Role.VOLUNTEER && ( + + navigate( + `${ROUTES.PANTRY_MANAGEMENT}?volunteerId=${volunteer.id}`, + ) + } + > + View Assigned Pantries + + )} @@ -235,14 +290,26 @@ const VolunteerManagement: React.FC = () => { + {volunteer.role === Role.VOLUNTEER && + volunteer.active && ( + { + setSelectedVolunteer(volunteer); + setIsPromoteModalOpen(true); + }} + > + Promote to Admin + + )} { setSelectedVolunteer(volunteer); - setIsPromoteModalOpen(true); + setIsConfirmModalOpen(true); }} > - Promote to Admin + {volunteer.active ? 'Deactivate' : 'Activate'} @@ -319,6 +386,19 @@ const VolunteerManagement: React.FC = () => { volunteerName={`${selectedVolunteer.firstName} ${selectedVolunteer.lastName}`} /> )} + + {selectedVolunteer && ( + { + setIsConfirmModalOpen(false); + setSelectedVolunteer(null); + }} + onConfirm={handleToggleActive} + volunteerName={`${selectedVolunteer.firstName} ${selectedVolunteer.lastName}`} + action={selectedVolunteer.active ? 'deactivate' : 'activate'} + /> + )} ); }; diff --git a/apps/frontend/src/containers/volunteerDashboard.tsx b/apps/frontend/src/containers/volunteerDashboard.tsx index 83b13f9fa..4f93283c3 100644 --- a/apps/frontend/src/containers/volunteerDashboard.tsx +++ b/apps/frontend/src/containers/volunteerDashboard.tsx @@ -158,6 +158,7 @@ const VolunteerDashboard: React.FC = () => { id: order.assignee.id, firstName: order.assignee.firstName, lastName: order.assignee.lastName, + active: order.assignee.active, }} onLinkClick={() => navigate( diff --git a/apps/frontend/src/containers/volunteerOrderManagement.tsx b/apps/frontend/src/containers/volunteerOrderManagement.tsx index 2f0cac565..ff193db2a 100644 --- a/apps/frontend/src/containers/volunteerOrderManagement.tsx +++ b/apps/frontend/src/containers/volunteerOrderManagement.tsx @@ -856,7 +856,12 @@ const OrderStatusSection: React.FC = ({ >