Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/frontend/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import DonationManagement from '@containers/donationManagement';
import AdminDonation from '@containers/adminDonation';
import { pantryIdLoader } from '@loaders/pantryIdLoader';
import Homepage from '@containers/homepage';
import AssignedPantries from '@containers/volunteerAssignedPantries'

const router = createBrowserRouter([
{
Expand Down Expand Up @@ -96,6 +97,10 @@ const router = createBrowserRouter([
path: '/volunteer-management',
element: <VolunteerManagement />,
},
{
path: '/volunteer-assigned-pantries',
element: <AssignedPantries />,
},
],
},
]);
Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/src/containers/homepage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ const Homepage: React.FC = () => {
<RouterLink to="/pantry-overview">Pantry Overview</RouterLink>
</Link>
</ListItem>
<ListItem textAlign="center">
<Link asChild color="teal.500">
<RouterLink to="/volunteer-assigned-pantries">Volunteer Assigned Pantries</RouterLink>
</Link>
</ListItem>
</List.Root>
</Box>

Expand Down
289 changes: 289 additions & 0 deletions apps/frontend/src/containers/volunteerAssignedPantries.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
import React, { useState, useEffect } from 'react';
import { Funnel } from 'lucide-react';
import {
Box,
Button,
Table,
Heading,
VStack,
Checkbox,
Text,
} from '@chakra-ui/react';
import ApiClient from '@api/apiClient';
import { Pantry } from 'types/types';
import { RefrigeratedDonation } from '../types/pantryEnums';
import { Assignments } from 'types/volunteerAssignments';

const AssignedPantries: React.FC = () => {
const [assignments, setAssignments] = useState<Assignments[]>([]);
const [filteredAssignments, setFilteredAssignments] = useState<Assignments[]>([]);
const [pantryDetails, setPantryDetails] = useState<Map<number, Pantry>>(new Map());
const [isFilterOpen, setIsFilterOpen] = useState(false);
const [filterRefrigeratorFriendly, setFilterRefrigeratorFriendly] = useState<boolean | null>(null);

useEffect(() => {
const fetchAssignments = async () => {
try {

const data = await ApiClient.getAllAssignments() as Assignments[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this endpoint was deleted, the new equivalent is getAllVolunteers in the users controller. can you add a method to the apiclient and update this accordingly?

setAssignments(data);
setFilteredAssignments(data);

const detailsMap = new Map<number, Pantry>();
await Promise.all(
data
.filter(assignment => assignment.pantry)
.map(async (assignment) => {
try {
const pantry = await ApiClient.getPantry(assignment.pantry!.pantryId);
detailsMap.set(assignment.pantry!.pantryId, pantry);
} catch (error) {
console.error(`Error fetching pantry ${assignment.pantry!.pantryId}:`, error);
}
})
);
setPantryDetails(detailsMap);
} catch (error) {
console.error('Error fetching assignments:', error);
alert('Error fetching assigned pantries: ' + error);
}
};

fetchAssignments();
}, []);

useEffect(() => {

let filtered = [...assignments];

if (filterRefrigeratorFriendly !== null) {
filtered = filtered.filter(assignment => {
if (!assignment.pantry) return false;
const pantry = pantryDetails.get(assignment.pantry.pantryId);
if (!pantry) return true;
const isRefrigeratorFriendlyValue =
pantry.refrigeratedDonation === RefrigeratedDonation.YES ||
pantry.refrigeratedDonation === RefrigeratedDonation.SOMETIMES;
return isRefrigeratorFriendlyValue === filterRefrigeratorFriendly;
});
}

setFilteredAssignments(filtered);
}, [filterRefrigeratorFriendly, assignments, pantryDetails]);


const handleViewOrders = (pantryId: number) => {
// TODO: Redirect to Order Management page when it's created
console.log('View orders for pantry:', pantryId);
};

const isRefrigeratorFriendly = (pantryId: number): boolean => {
const pantry = pantryDetails.get(pantryId);
if (!pantry) return false;
return pantry.refrigeratedDonation === RefrigeratedDonation.YES ||
pantry.refrigeratedDonation === RefrigeratedDonation.SOMETIMES;
};

const getRefrigeratorFriendlyText = (pantryId: number): string => {
const pantry = pantryDetails.get(pantryId);
if (!pantry) return 'Loading...';
if (pantry.refrigeratedDonation === RefrigeratedDonation.YES ||
pantry.refrigeratedDonation === RefrigeratedDonation.SOMETIMES) {
return 'Refrigerator-Friendly';
}
return 'Not Refrigerator-Friendly';
};

const tableHeaderStyles = {
borderBottom: '1px solid',
borderColor: 'neutral.100',
color: 'neutral.800',
fontFamily: 'inter',
fontWeight: '600',
fontSize: 'sm',
};

return (
<Box p={12}>
<Heading textStyle="h1" color="gray.600" mb={6}>
Assigned Pantries
</Heading>

{/* Filter Button */}
<Box display="flex" gap={2} mb={6} fontFamily="'Inter', sans-serif">
<Box position="relative">
<Button
onClick={() => setIsFilterOpen(!isFilterOpen)}
variant="outline"
color="neutral.600"
border="1px solid"
borderColor="neutral.200"
size="sm"
p={3}
fontFamily="ibm"
fontWeight="semibold"
>
<Funnel />
Filter
</Button>

{isFilterOpen && (
<>
<Box
position="fixed"
top={0}
left={0}
right={0}
bottom={0}
onClick={() => setIsFilterOpen(false)}
zIndex={10}
/>
<Box
position="absolute"
top="100%"
left={0}
mt={2}
bg="white"
border="1px solid"
borderColor="gray.200"
borderRadius="md"
boxShadow="lg"
p={4}
minW="275px"
zIndex={20}
>
<VStack align="stretch" gap={2}>
<Box display="flex" alignItems="center" gap={2}>
<Checkbox.Root
checked={filterRefrigeratorFriendly === true}
onCheckedChange={(e: { checked: boolean }) =>
setFilterRefrigeratorFriendly(e.checked ? true : null)
}
color="black"
size="sm"
>
<Checkbox.HiddenInput />
<Checkbox.Control />
</Checkbox.Root>
<Text fontSize="sm" cursor="pointer" onClick={() => setFilterRefrigeratorFriendly(filterRefrigeratorFriendly === true ? null : true)}>
Refrigerator-Friendly Only
</Text>
</Box>

<Box display="flex" alignItems="center" gap={2}>
<Checkbox.Root
checked={filterRefrigeratorFriendly === false}
onCheckedChange={(e: { checked: boolean }) =>
setFilterRefrigeratorFriendly(e.checked ? false : null)
}
color="black"
size="sm"
>
<Checkbox.HiddenInput />
<Checkbox.Control />
</Checkbox.Root>
<Text fontSize="sm" cursor="pointer" onClick={() => setFilterRefrigeratorFriendly(filterRefrigeratorFriendly === false ? null : false)}>
Not Refrigerator-Friendly Only
</Text>
</Box>
</VStack>
</Box>
</>
)}
</Box>
</Box>

{/* Pantries Table */}
<Table.Root>
<Table.Header>
<Table.Row>
<Table.ColumnHeader
{...tableHeaderStyles}
borderRight="1px solid"
borderRightColor="neutral.100"
width="40%"
>
Pantry
</Table.ColumnHeader>
<Table.ColumnHeader
{...tableHeaderStyles}
borderRight="1px solid"
borderRightColor="neutral.100"
width="35%"
>
Refrigerator-Friendly
</Table.ColumnHeader>
<Table.ColumnHeader
{...tableHeaderStyles}
textAlign="center"
width="25%"
>
Action
</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{filteredAssignments.map((assignment) => {

if (!assignment.pantry) return null;

return (
<Table.Row
key={assignment.assignmentId}
_hover={{ bg: 'gray.50' }}
>
<Table.Cell
textStyle="p2"
borderRight="1px solid"
borderRightColor="neutral.100"
py={0}
>
<Button
variant="plain"
textDecoration="underline"
onClick={() => assignment.pantry}
fontFamily="inter"
>
{assignment.pantry.pantryName}
</Button>
</Table.Cell>
<Table.Cell
textStyle="p2"
borderRight="1px solid"
borderRightColor="neutral.100"
color="neutral.700"
>
<Box
bg={assignment.pantry && isRefrigeratorFriendly(assignment.pantry.pantryId) ? 'gray.100' : 'orange.50'}
px={3}
py={1}
borderRadius="md"
display="inline-block"
fontSize="sm"
fontFamily="inter"
>
{assignment.pantry ? getRefrigeratorFriendlyText(assignment.pantry.pantryId) : 'N/A'}
</Box>
</Table.Cell>
<Table.Cell textStyle="p2" textAlign="center">
<Button
variant="plain"
textDecoration="underline"
color="blue.600"
onClick={() => assignment.pantry && handleViewOrders(assignment.pantry.pantryId)}
fontFamily="inter"
fontSize="sm"
>
View Orders
</Button>
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table.Root>
</Box>
);
};

export default AssignedPantries;
18 changes: 18 additions & 0 deletions apps/frontend/src/types/volunteerAssignments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface LimitedPantryInfo {
pantryId: number;
pantryName: string;
}

export interface Assignments {
assignmentId: number;
volunteer: {
id: number;
firstName: string;
lastName: string;
email: string;
phone: string;
role: string;
};
pantry: LimitedPantryInfo | null;
}

Loading