From 4bdd3dcb6581ba5415f802ef135bb509ff40e83d Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Mon, 11 May 2026 13:08:24 -0400 Subject: [PATCH 1/8] frontend --- apps/frontend/src/api/apiClient.ts | 9 ++ apps/frontend/src/app.tsx | 9 ++ apps/frontend/src/components/Navbar.tsx | 2 +- .../containers/foodManufacturerDashboard.tsx | 131 ++++++++++++++++++ .../foodManufacturerDonationManagement.tsx | 71 ++++++---- apps/frontend/src/containers/homepage.tsx | 7 + apps/frontend/src/routes.ts | 1 + apps/frontend/src/types/types.ts | 5 + 8 files changed, 210 insertions(+), 25 deletions(-) create mode 100644 apps/frontend/src/containers/foodManufacturerDashboard.tsx diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 9820681ec..9ea8ac135 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -41,6 +41,7 @@ import { VolunteerAction, FoodRequestWithoutRelations, PendingApplication, + DonationReminderDto, } from 'types/types'; const defaultBaseUrl = @@ -460,6 +461,14 @@ export class ApiClient { .then((response) => response.data); } + public async getNextTwoDonationReminders( + foodManufacturerId: number, + ): Promise { + return this.axiosInstance + .get(`/api/manufacturers/${foodManufacturerId}/next-two-reminders`) + .then((response) => response.data); + } + public async updateFoodManufacturerApplicationData( manufacturerId: number, data: UpdateFoodManufacturerApplicationDto, diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index be6b3c9aa..ba4eb055f 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -33,6 +33,7 @@ import ProfilePage from '@containers/profilePage'; import VolunteerOrderManagement from '@containers/volunteerOrderManagement'; import AdminRequestManagement from '@containers/adminRequestManagement'; import AdminDashboard from '@containers/adminDashboard'; +import FoodManufacturerDashboard from '@containers/foodManufacturerDashboard'; Amplify.configure(CognitoAuthConfig); @@ -93,6 +94,14 @@ const router = createBrowserRouter([ ), }, + { + path: ROUTES.FM_DASHBOARD, + element: ( + + + + ), + }, { path: ROUTES.APPROVE_PANTRIES, element: ( diff --git a/apps/frontend/src/components/Navbar.tsx b/apps/frontend/src/components/Navbar.tsx index ef77f564a..1a35e63ba 100644 --- a/apps/frontend/src/components/Navbar.tsx +++ b/apps/frontend/src/components/Navbar.tsx @@ -274,7 +274,7 @@ const Navbar: React.FC = () => { [Role.ADMIN]: ROUTES.ADMIN_DASHBOARD, [Role.VOLUNTEER]: ROUTES.HOME, [Role.PANTRY]: ROUTES.HOME, - [Role.FOODMANUFACTURER]: ROUTES.HOME, + [Role.FOODMANUFACTURER]: ROUTES.FM_DASHBOARD, }; return ( diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx new file mode 100644 index 000000000..d424963c7 --- /dev/null +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react'; +import { Box, Heading, Text } from '@chakra-ui/react'; +import DashboardCard, { + DONATION_STATUS_BADGE, + DashboardCardType, +} from '@components/dashboardCard'; +import { + Donation, + DonationDetails, + DonationReminderDto, + FoodManufacturer, + User, +} from '../types/types'; +import ApiClient from '@api/apiClient'; +import { useAlert } from '../hooks/alert'; +import { FloatingAlert } from '@components/floatingAlert'; +import { useNavigate } from 'react-router-dom'; + +const FoodManufacturerDashboard: React.FC = () => { + const navigate = useNavigate(); + + const [alertState, setAlertMessage] = useAlert(); + const [foodManufacturer, setFoodManufacturer] = + useState(null); + const [upcomingReminders, setUpcomingReminders] = useState< + DonationReminderDto[] + >([]); + const [recentDonations, setRecentDonations] = useState([]); + + useEffect(() => { + const fetchFmData = async () => { + let fmId: number; + try { + fmId = await ApiClient.getCurrentUserFoodManufacturerId(); + const fm = await ApiClient.getFoodManufacturer(fmId); + setFoodManufacturer(fm); + } catch { + setAlertMessage('Error fetching your manufacturer profile.'); + return; + } + + const [reminders, donations] = await Promise.allSettled([ + ApiClient.getNextTwoDonationReminders(fmId), + ApiClient.getAllDonationsByFoodManufacturer(fmId), + ]); + + if (reminders.status === 'fulfilled') { + setUpcomingReminders(reminders.value); + } else { + setAlertMessage('Error fetching upcoming donations.'); + } + + if (donations.status === 'fulfilled') { + const sorted = donations.value + .map((d: DonationDetails) => d.donation) + .sort( + (a: Donation, b: Donation) => + new Date(b.dateDonated).getTime() - + new Date(a.dateDonated).getTime(), + ) + .slice(0, 2); + setRecentDonations(sorted); + } else { + setAlertMessage('Error fetching recent donations.'); + } + }; + fetchFmData(); + }, [setAlertMessage]); + + return ( + + {alertState && ( + + )} + + Welcome, {foodManufacturer?.foodManufacturerName} + + + + Upcoming Donations + + + {upcomingReminders.map((reminder) => ( + + navigate( + `/fm-donation-management?donationId=${reminder.donation.donationId}`, + ) + } + /> + ))} + + + + Recent Donations + + + {recentDonations.map((donation) => ( + + navigate( + `/fm-donation-management?donationId=${donation.donationId}`, + ) + } + /> + ))} + + + ); +}; + +export default FoodManufacturerDashboard; diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index f60109c27..ae4d157e7 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -12,12 +12,20 @@ import { import { ChevronRight, ChevronLeft, Mail, CircleCheck } from 'lucide-react'; import { capitalize, formatDate, DONATION_STATUS_COLORS } from '@utils/utils'; import ApiClient from '@api/apiClient'; -import { DonationDetails, DonationStatus } from '../types/types'; +import { Donation, DonationDetails, DonationStatus } from '../types/types'; import DonationDetailsModal from '@components/forms/donationDetailsModal'; import NewDonationFormModal from '@components/forms/newDonationFormModal'; +import { useSearchParams } from 'react-router-dom'; +import { useAlert } from '../hooks/alert'; +import { FloatingAlert } from '@components/floatingAlert'; const FoodManufacturerDonationManagement: React.FC = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const [alertState, setAlertMessage] = useAlert(); const [isLogDonationOpen, setIsLogDonationOpen] = useState(false); + const [selectedDonation, setSelectedDonation] = useState( + null, + ); // State to hold donations grouped by status const [statusDonations, setStatusDonations] = useState<{ [key in DonationStatus]: DonationDetails[]; @@ -26,12 +34,6 @@ const FoodManufacturerDonationManagement: React.FC = () => { [DonationStatus.AVAILABLE]: [], [DonationStatus.FULFILLED]: [], }); - - // State to hold selected donation for details modal - const [selectedDonationId, setSelectedDonationId] = useState( - null, - ); - // State to hold current page per status const [currentPages, setCurrentPages] = useState< Record @@ -46,7 +48,8 @@ const FoodManufacturerDonationManagement: React.FC = () => { // Fetch all donations on component mount and sorts them into their appropriate status lists const fetchDonations = async () => { try { - const data = await ApiClient.getAllDonationsByFoodManufacturer(1); // Replace with actual food manufacturer ID + const fmId = await ApiClient.getCurrentUserFoodManufacturerId(); + const data = await ApiClient.getAllDonationsByFoodManufacturer(fmId); const grouped: Record = { [DonationStatus.AVAILABLE]: [], @@ -68,15 +71,14 @@ const FoodManufacturerDonationManagement: React.FC = () => { setStatusDonations(grouped); - // Initialize current page for each status const initialPages: Record = { [DonationStatus.AVAILABLE]: 1, [DonationStatus.FULFILLED]: 1, [DonationStatus.MATCHED]: 1, }; setCurrentPages(initialPages); - } catch (error) { - alert('Error fetching donations: ' + error); + } catch { + setAlertMessage('Error fetching donations'); } }; @@ -84,6 +86,16 @@ const FoodManufacturerDonationManagement: React.FC = () => { fetchDonations(); }, []); + useEffect(() => { + const donationIdParam = searchParams.get('donationId'); + if (!donationIdParam) return; + + const id = Number(donationIdParam); + ApiClient.getDonation(id) + .then(setSelectedDonation) + .catch(() => setAlertMessage('Error loading donation')); + }, [searchParams, setAlertMessage]); + const handlePageChange = (status: DonationStatus, page: number) => { setCurrentPages((prev) => ({ ...prev, @@ -91,8 +103,21 @@ const FoodManufacturerDonationManagement: React.FC = () => { })); }; + const handleCloseModal = () => { + setSelectedDonation(null); + setSearchParams({}); + }; + return ( + {alertState && ( + + )} Donation Management @@ -122,6 +147,14 @@ const FoodManufacturerDonationManagement: React.FC = () => { /> )} + {selectedDonation && ( + + )} + {Object.values(DonationStatus).map((status) => { const allDonationsByStatus = statusDonations[status] || []; @@ -137,8 +170,7 @@ const FoodManufacturerDonationManagement: React.FC = () => { donations={displayedDonations} status={status} colors={DONATION_STATUS_COLORS[status]} - selectedDonationId={selectedDonationId} - onDonationSelect={setSelectedDonationId} + onDonationSelect={setSelectedDonation} totalDonations={allDonationsByStatus.length} currentPage={currentPage} onPageChange={(page) => handlePageChange(status, page)} @@ -154,8 +186,7 @@ interface DonationStatusSectionProps { donations: DonationDetails[]; status: DonationStatus; colors: string[]; - onDonationSelect: (donationId: number | null) => void; - selectedDonationId: number | null; + onDonationSelect: (donation: Donation | null) => void; totalDonations: number; currentPage: number; onPageChange: (page: number) => void; @@ -166,7 +197,6 @@ const DonationStatusSection: React.FC = ({ status, colors, onDonationSelect, - selectedDonationId, totalDonations, currentPage, onPageChange, @@ -293,17 +323,10 @@ const DonationStatusSection: React.FC = ({ onDonationSelect(donation.donationId)} + onClick={() => onDonationSelect(donation)} > {donation.donationId} - {selectedDonationId === donation.donationId && ( - onDonationSelect(null)} - /> - )} { + + + + Food Manufacturer Dashboard + + + diff --git a/apps/frontend/src/routes.ts b/apps/frontend/src/routes.ts index 19da091e0..572106a7c 100644 --- a/apps/frontend/src/routes.ts +++ b/apps/frontend/src/routes.ts @@ -34,4 +34,5 @@ export const ROUTES = { REQUEST_FORM: '/request-form', FM_DONATION_MANAGEMENT: '/fm-donation-management', + FM_DASHBOARD: '/fm-dashboard', }; diff --git a/apps/frontend/src/types/types.ts b/apps/frontend/src/types/types.ts index 2a15a0584..cf49f1f3f 100644 --- a/apps/frontend/src/types/types.ts +++ b/apps/frontend/src/types/types.ts @@ -213,6 +213,11 @@ export interface DonationOrderDetails { pantryName: string; } +export interface DonationReminderDto { + donation: Donation; + reminderDate: string; +} + export interface DonationItem { itemId: number; donationId: number; From 940a901068aea45228c371eacc8adb9cefef4fc3 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sat, 16 May 2026 18:27:39 -0400 Subject: [PATCH 2/8] comments and conflicts --- apps/frontend/src/api/apiClient.ts | 3 +- .../containers/foodManufacturerDashboard.tsx | 8 ++-- .../foodManufacturerDonationManagement.tsx | 44 +++++++++++++------ 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 03c5ea37a..613efb893 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -40,7 +40,6 @@ import { VolunteerAction, ApprovedPantryResponse, UpdatePantryVolunteersDto, - FoodRequestWithoutRelations, BulkUpdateTrackingCostDto, UpdateDonationItemDetailsDto, PendingApplication, @@ -480,7 +479,7 @@ export class ApiClient { .get(`/api/manufacturers/${foodManufacturerId}/next-two-reminders`) .then((response) => response.data); } - + public async bulkUpdateTrackingCostInfo( data: BulkUpdateTrackingCostDto, ): Promise { diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index d424963c7..bde08e080 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -9,12 +9,12 @@ import { DonationDetails, DonationReminderDto, FoodManufacturer, - User, } from '../types/types'; import ApiClient from '@api/apiClient'; import { useAlert } from '../hooks/alert'; import { FloatingAlert } from '@components/floatingAlert'; import { useNavigate } from 'react-router-dom'; +import { ROUTES } from '../routes'; const FoodManufacturerDashboard: React.FC = () => { const navigate = useNavigate(); @@ -87,7 +87,7 @@ const FoodManufacturerDashboard: React.FC = () => { {upcomingReminders.map((reminder) => ( { linkText="View Donation Requirements" onLinkClick={() => navigate( - `/fm-donation-management?donationId=${reminder.donation.donationId}`, + `${ROUTES.FM_DONATION_MANAGEMENT}?donationId=${reminder.donation.donationId}`, ) } /> @@ -118,7 +118,7 @@ const FoodManufacturerDashboard: React.FC = () => { linkText="View Donation Details" onLinkClick={() => navigate( - `/fm-donation-management?donationId=${donation.donationId}`, + `${ROUTES.FM_DONATION_MANAGEMENT}?donationId=${donation.donationId}`, ) } /> diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index 9a5702aee..3c5464f5d 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -12,16 +12,18 @@ import { import { ChevronRight, ChevronLeft, Mail, CircleCheck } from 'lucide-react'; import { capitalize, formatDate, DONATION_STATUS_COLORS } from '@utils/utils'; import ApiClient from '@api/apiClient'; -import { Donation, DonationDetails, DonationStatus } from '../types/types'; -import DonationDetailsModal from '@components/forms/donationDetailsModal'; +import { DonationDetails, DonationStatus } from '../types/types'; import NewDonationFormModal from '@components/forms/newDonationFormModal'; import FmCompleteRequiredActionsModal from '@components/forms/fmCompleteRequiredActionsModal'; import { FloatingAlert } from '@components/floatingAlert'; import { useAlert } from '../hooks/alert'; +import { useSearchParams } from 'react-router-dom'; +import DonationDetailsModal from '@components/forms/donationDetailsModal'; const MAX_PER_STATUS = 5; const FoodManufacturerDonationManagement: React.FC = () => { + const [searchParams, setSearchParams] = useSearchParams(); const [errorAlertState, setErrorMessage] = useAlert(); const [successAlertState, setSuccessMessage] = useAlert(); const [isLogDonationOpen, setIsLogDonationOpen] = useState(false); @@ -45,6 +47,11 @@ const FoodManufacturerDonationManagement: React.FC = () => { [DonationStatus.FULFILLED]: 1, }); + // State to hold selected donation for details modal + const [selectedDonationId, setSelectedDonationId] = useState( + null, + ); + // Fetch all donations on component mount and sorts them into their appropriate status lists const fetchDonations = async (fmId: number) => { try { @@ -100,10 +107,8 @@ const FoodManufacturerDonationManagement: React.FC = () => { if (!donationIdParam) return; const id = Number(donationIdParam); - ApiClient.getDonation(id) - .then(setSelectedDonation) - .catch(() => setAlertMessage('Error loading donation')); - }, [searchParams, setAlertMessage]); + setSelectedDonationId(id); + }, [searchParams, setErrorMessage]); const handlePageChange = (status: DonationStatus, page: number) => { setCurrentPages((prev) => ({ @@ -112,11 +117,6 @@ const FoodManufacturerDonationManagement: React.FC = () => { })); }; - const handleCloseModal = () => { - setSelectedDonation(null); - setSearchParams({}); - }; - return ( {errorAlertState && ( @@ -195,11 +195,16 @@ const FoodManufacturerDonationManagement: React.FC = () => { donations={displayedDonations} status={status} colors={DONATION_STATUS_COLORS[status]} - onDonationSelect={setSelectedDonation} + selectedDonationId={selectedDonationId} + onDonationSelect={setSelectedDonationId} totalDonations={allDonationsByStatus.length} currentPage={currentPage} onPageChange={(page) => handlePageChange(status, page)} onActionSelect={setSelectedActionDonation} + onDonationClose={() => { + setSelectedDonationId(null); + setSearchParams({}); + }} /> ); @@ -212,11 +217,13 @@ interface DonationStatusSectionProps { donations: DonationDetails[]; status: DonationStatus; colors: string[]; - onDonationSelect: (donation: Donation | null) => void; + onDonationSelect: (donationId: number | null) => void; + selectedDonationId: number | null; totalDonations: number; currentPage: number; onPageChange: (page: number) => void; onActionSelect: (donation: DonationDetails | null) => void; + onDonationClose: () => void; } const DonationStatusSection: React.FC = ({ @@ -226,8 +233,10 @@ const DonationStatusSection: React.FC = ({ onDonationSelect, totalDonations, currentPage, + selectedDonationId, onPageChange, onActionSelect, + onDonationClose, }) => { const totalPages = Math.ceil(totalDonations / MAX_PER_STATUS); @@ -350,10 +359,17 @@ const DonationStatusSection: React.FC = ({ onDonationSelect(donation)} + onClick={() => onDonationSelect(donation.donationId)} > {donation.donationId} + {selectedDonationId === donation.donationId && ( + + )} Date: Sun, 17 May 2026 13:52:20 -0400 Subject: [PATCH 3/8] comments --- .../containers/approveFoodManufacturers.tsx | 9 +++++---- .../src/containers/approvePantries.tsx | 9 +++++---- .../containers/foodManufacturerDashboard.tsx | 20 +++++++++---------- .../foodManufacturerDonationManagement.tsx | 6 ++++-- apps/frontend/src/containers/homepage.tsx | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/frontend/src/containers/approveFoodManufacturers.tsx b/apps/frontend/src/containers/approveFoodManufacturers.tsx index 786e13137..14208d897 100644 --- a/apps/frontend/src/containers/approveFoodManufacturers.tsx +++ b/apps/frontend/src/containers/approveFoodManufacturers.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; -import { ROUTES } from '../routes'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { Table, Button, @@ -24,8 +23,10 @@ import { } from 'lucide-react'; import { useAlert } from '../hooks/alert'; import { FloatingAlert } from '@components/floatingAlert'; +import { ROUTES } from '../routes'; const ApproveFoodManufacturers: React.FC = () => { + const navigate = useNavigate(); const [foodManufacturers, setFoodManufacturers] = useState< FoodManufacturer[] >([]); @@ -117,9 +118,9 @@ const ApproveFoodManufacturers: React.FC = () => { : `${name} - Application Rejected`; setSuccessMessage(message); - setSearchParams({}); + navigate(ROUTES.APPROVE_FOOD_MANUFACTURERS, { replace: true }); } - }, [searchParams, setSearchParams, setErrorMessage, setSuccessMessage]); + }, [searchParams, setErrorMessage, setSuccessMessage, navigate]); return ( diff --git a/apps/frontend/src/containers/approvePantries.tsx b/apps/frontend/src/containers/approvePantries.tsx index 75dd9245a..90bce7abe 100644 --- a/apps/frontend/src/containers/approvePantries.tsx +++ b/apps/frontend/src/containers/approvePantries.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; -import { ROUTES } from '../routes'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import { Table, Button, @@ -24,8 +23,10 @@ import { } from 'lucide-react'; import { useAlert } from '../hooks/alert'; import { FloatingAlert } from '@components/floatingAlert'; +import { ROUTES } from '../routes'; const ApprovePantries: React.FC = () => { + const navigate = useNavigate(); const [pantries, setPantries] = useState([]); const [sortAsc, setSortAsc] = useState(false); const [selectedPantries, setSelectedPantries] = useState([]); @@ -108,9 +109,9 @@ const ApprovePantries: React.FC = () => { : `${name} - Application Rejected`; setSuccessMessage(message); - setSearchParams({}); + navigate(ROUTES.APPROVE_PANTRIES, { replace: true }); } - }, [searchParams, setSearchParams, setErrorMessage, setSuccessMessage]); + }, [searchParams, navigate, setErrorMessage, setSuccessMessage]); return ( diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index bde08e080..d2a7dc9d3 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -19,7 +19,7 @@ import { ROUTES } from '../routes'; const FoodManufacturerDashboard: React.FC = () => { const navigate = useNavigate(); - const [alertState, setAlertMessage] = useAlert(); + const [errorAlertState, setErrorMessage] = useAlert(); const [foodManufacturer, setFoodManufacturer] = useState(null); const [upcomingReminders, setUpcomingReminders] = useState< @@ -35,7 +35,7 @@ const FoodManufacturerDashboard: React.FC = () => { const fm = await ApiClient.getFoodManufacturer(fmId); setFoodManufacturer(fm); } catch { - setAlertMessage('Error fetching your manufacturer profile.'); + setErrorMessage('Error fetching your manufacturer profile.'); return; } @@ -47,7 +47,7 @@ const FoodManufacturerDashboard: React.FC = () => { if (reminders.status === 'fulfilled') { setUpcomingReminders(reminders.value); } else { - setAlertMessage('Error fetching upcoming donations.'); + setErrorMessage('Error fetching upcoming donations.'); } if (donations.status === 'fulfilled') { @@ -61,19 +61,19 @@ const FoodManufacturerDashboard: React.FC = () => { .slice(0, 2); setRecentDonations(sorted); } else { - setAlertMessage('Error fetching recent donations.'); + setErrorMessage('Error fetching recent donations.'); } }; fetchFmData(); - }, [setAlertMessage]); + }, [setErrorMessage]); return ( - {alertState && ( + {errorAlertState && ( )} @@ -92,7 +92,6 @@ const FoodManufacturerDashboard: React.FC = () => { title={`Donation #${reminder.donation.donationId}`} date={reminder.reminderDate} subtitle={reminder.donation.foodManufacturer?.foodManufacturerName} - badge={DONATION_STATUS_BADGE[reminder.donation.status]} linkText="View Donation Requirements" onLinkClick={() => navigate( @@ -114,7 +113,6 @@ const FoodManufacturerDashboard: React.FC = () => { title={`Donation #${donation.donationId}`} date={donation.dateDonated} subtitle={donation.foodManufacturer?.foodManufacturerName} - badge={DONATION_STATUS_BADGE[donation.status]} linkText="View Donation Details" onLinkClick={() => navigate( diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index 3c5464f5d..32e49f0b0 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -17,12 +17,14 @@ import NewDonationFormModal from '@components/forms/newDonationFormModal'; import FmCompleteRequiredActionsModal from '@components/forms/fmCompleteRequiredActionsModal'; import { FloatingAlert } from '@components/floatingAlert'; import { useAlert } from '../hooks/alert'; -import { useSearchParams } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import DonationDetailsModal from '@components/forms/donationDetailsModal'; +import { ROUTES } from '../routes'; const MAX_PER_STATUS = 5; const FoodManufacturerDonationManagement: React.FC = () => { + const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); const [errorAlertState, setErrorMessage] = useAlert(); const [successAlertState, setSuccessMessage] = useAlert(); @@ -203,7 +205,7 @@ const FoodManufacturerDonationManagement: React.FC = () => { onActionSelect={setSelectedActionDonation} onDonationClose={() => { setSelectedDonationId(null); - setSearchParams({}); + navigate(ROUTES.FM_DONATION_MANAGEMENT, { replace: true }); }} /> diff --git a/apps/frontend/src/containers/homepage.tsx b/apps/frontend/src/containers/homepage.tsx index f7109e74f..fca3ab8e0 100644 --- a/apps/frontend/src/containers/homepage.tsx +++ b/apps/frontend/src/containers/homepage.tsx @@ -82,7 +82,7 @@ const Homepage: React.FC = () => { - + Food Manufacturer Dashboard From 409e3a7f72f21679c9715d332c266aba01d76876 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Sun, 17 May 2026 15:37:15 -0400 Subject: [PATCH 4/8] pagination functionality for deeplinking --- .../src/components/foodRequestManagement.tsx | 19 ++++++ .../frontend/src/containers/adminDonation.tsx | 19 +++++- .../src/containers/adminOrderManagement.tsx | 33 +++++++++-- .../containers/foodManufacturerDashboard.tsx | 5 +- .../foodManufacturerDonationManagement.tsx | 27 +++++++++ apps/frontend/src/containers/formRequests.tsx | 17 +++++- .../src/containers/pantryOrderManagement.tsx | 29 +++++++++- .../containers/volunteerOrderManagement.tsx | 58 ++++++++++++++----- 8 files changed, 178 insertions(+), 29 deletions(-) diff --git a/apps/frontend/src/components/foodRequestManagement.tsx b/apps/frontend/src/components/foodRequestManagement.tsx index 1db69e7df..96103c288 100644 --- a/apps/frontend/src/components/foodRequestManagement.tsx +++ b/apps/frontend/src/components/foodRequestManagement.tsx @@ -51,6 +51,9 @@ const RequestManagement: React.FC = ({ const [selectedCreateOrderRequest, setSelectedCreateOrderRequest] = useState(null); + // Tracks whether the current page was advanced by a deeplink so the close handler knows to revert it back to page 1 + const [wasDeeplinked, setWasDeeplinked] = useState(false); + const [errorAlertState, setErrorMessage] = useAlert(); const [successAlertState, setSuccessMessage] = useAlert(); @@ -80,6 +83,18 @@ const RequestManagement: React.FC = ({ if (match) { setSelectedViewDetailsRequest(match); + + // Paginate to the page that contains the deeplinked request + const sortedAtLoad = [...requests].sort((a, b) => + b.requestedAt.localeCompare(a.requestedAt), + ); + const idx = sortedAtLoad.findIndex( + (r) => r.requestId === initialRequestId, + ); + if (idx >= 0) { + setCurrentPage(Math.floor(idx / itemsPerPage) + 1); + setWasDeeplinked(true); + } } else { navigate(ROUTES.REQUEST_FORM, { replace: true }); } @@ -391,6 +406,10 @@ const RequestManagement: React.FC = ({ if (initialRequestId) { navigate(location.pathname, { replace: true }); } + if (wasDeeplinked) { + setCurrentPage(1); + setWasDeeplinked(false); + } }} /> )} diff --git a/apps/frontend/src/containers/adminDonation.tsx b/apps/frontend/src/containers/adminDonation.tsx index 62d547bf2..f2986ca67 100644 --- a/apps/frontend/src/containers/adminDonation.tsx +++ b/apps/frontend/src/containers/adminDonation.tsx @@ -36,6 +36,9 @@ const AdminDonation: React.FC = () => { null, ); + // Tracks whether the current page was advanced by a deeplink so the close handler knows to revert it back to page 1. + const [wasDeeplinked, setWasDeeplinked] = useState(false); + const [alertState, setAlertMessage] = useAlert(); useEffect(() => { @@ -60,12 +63,22 @@ const AdminDonation: React.FC = () => { if (!donationIdFromUrl) return; if (donations.length === 0) return; + const id = Number(donationIdFromUrl); const matchedDonation = donations.find( - (donation) => donation.donationId === Number(donationIdFromUrl), + (donation) => donation.donationId === id, ); if (matchedDonation) { setSelectedDonation(matchedDonation); + // Paginate to the page that contains the deeplinked donation + const sortedAtLoad = [...donations].sort((a, b) => + b.dateDonated.localeCompare(a.dateDonated), + ); + const idx = sortedAtLoad.findIndex((d) => d.donationId === id); + if (idx >= 0) { + setCurrentPage(Math.floor(idx / itemsPerPage) + 1); + setWasDeeplinked(true); + } } else { navigate(ROUTES.ADMIN_DONATION, { replace: true }); } @@ -283,6 +296,10 @@ const AdminDonation: React.FC = () => { onClose={() => { setSelectedDonation(null); navigate(ROUTES.ADMIN_DONATION, { replace: true }); + if (wasDeeplinked) { + setCurrentPage(1); + setWasDeeplinked(false); + } }} /> )} diff --git a/apps/frontend/src/containers/adminOrderManagement.tsx b/apps/frontend/src/containers/adminOrderManagement.tsx index 84e09c3f8..4f3436975 100644 --- a/apps/frontend/src/containers/adminOrderManagement.tsx +++ b/apps/frontend/src/containers/adminOrderManagement.tsx @@ -51,6 +51,10 @@ const AdminOrderManagement: React.FC = () => { // State to hold selected order for details modal const [selectedOrderId, setSelectedOrderId] = useState(null); + // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. + const [deeplinkedStatus, setDeeplinkedStatus] = useState( + null, + ); // State to hold current page per status const [currentPages, setCurrentPages] = useState>( @@ -159,12 +163,26 @@ const AdminOrderManagement: React.FC = () => { const allOrders = Object.values(statusOrders).flat(); if (allOrders.length === 0) return; - const matchedOrder = allOrders.find( - (order) => order.orderId === Number(orderIdFromUrl), - ); + const id = Number(orderIdFromUrl); + const matchedOrder = allOrders.find((order) => order.orderId === id); if (matchedOrder) { - setSelectedOrderId(Number(orderIdFromUrl)); + setSelectedOrderId(id); + // Paginate the containing status to the page that holds this order. + for (const status of Object.values(OrderStatus)) { + const sorted = [...statusOrders[status]].sort((a, b) => + b.createdAt.localeCompare(a.createdAt), + ); + const idx = sorted.findIndex((o) => o.orderId === id); + if (idx >= 0) { + setCurrentPages((prev) => ({ + ...prev, + [status]: Math.floor(idx / MAX_PER_STATUS) + 1, + })); + setDeeplinkedStatus(status); + break; + } + } } else { navigate(ROUTES.ADMIN_ORDER_MANAGEMENT, { replace: true }); } @@ -255,6 +273,13 @@ const AdminOrderManagement: React.FC = () => { onClose={() => { setSelectedOrderId(null); navigate(ROUTES.ADMIN_ORDER_MANAGEMENT, { replace: true }); + if (deeplinkedStatus) { + setCurrentPages((prev) => ({ + ...prev, + [deeplinkedStatus]: 1, + })); + setDeeplinkedStatus(null); + } }} /> )} diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index d2a7dc9d3..afe44b557 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -1,9 +1,6 @@ import React, { useEffect, useState } from 'react'; import { Box, Heading, Text } from '@chakra-ui/react'; -import DashboardCard, { - DONATION_STATUS_BADGE, - DashboardCardType, -} from '@components/dashboardCard'; +import DashboardCard, { DashboardCardType } from '@components/dashboardCard'; import { Donation, DonationDetails, diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index 32e49f0b0..f366bc70c 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -53,6 +53,9 @@ const FoodManufacturerDonationManagement: React.FC = () => { const [selectedDonationId, setSelectedDonationId] = useState( null, ); + // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. + const [deeplinkedStatus, setDeeplinkedStatus] = + useState(null); // Fetch all donations on component mount and sorts them into their appropriate status lists const fetchDonations = async (fmId: number) => { @@ -84,6 +87,23 @@ const FoodManufacturerDonationManagement: React.FC = () => { [DonationStatus.FULFILLED]: 1, [DonationStatus.MATCHED]: 1, }; + + // Paginate the containing status to the page that holds this donation. + const donationIdParam = searchParams.get('donationId'); + if (donationIdParam) { + const id = Number(donationIdParam); + for (const status of Object.values(DonationStatus)) { + const idx = grouped[status].findIndex( + (d) => d.donation.donationId === id, + ); + if (idx >= 0) { + initialPages[status] = Math.floor(idx / MAX_PER_STATUS) + 1; + setDeeplinkedStatus(status); + break; + } + } + } + setCurrentPages(initialPages); } catch { setErrorMessage('Error fetching donations'); @@ -206,6 +226,13 @@ const FoodManufacturerDonationManagement: React.FC = () => { onDonationClose={() => { setSelectedDonationId(null); navigate(ROUTES.FM_DONATION_MANAGEMENT, { replace: true }); + if (deeplinkedStatus) { + setCurrentPages((prev) => ({ + ...prev, + [deeplinkedStatus]: 1, + })); + setDeeplinkedStatus(null); + } }} /> diff --git a/apps/frontend/src/containers/formRequests.tsx b/apps/frontend/src/containers/formRequests.tsx index 5dc980bc7..3d96d55c3 100644 --- a/apps/frontend/src/containers/formRequests.tsx +++ b/apps/frontend/src/containers/formRequests.tsx @@ -37,6 +37,8 @@ const FormRequests: React.FC = () => { const [openReadOnlyRequest, setOpenReadOnlyRequest] = useState(null); + // Tracks whether the current page was advanced by a deeplink so the close handler knows to revert it back to page 1. + const [wasDeeplinked, setWasDeeplinked] = useState(false); const [searchParams] = useSearchParams(); const navigate = useNavigate(); @@ -73,11 +75,16 @@ const FormRequests: React.FC = () => { const requestIdFromUrl = searchParams.get('requestId'); if (!requestIdFromUrl || requests.length === 0) return; - const match = requests.find( - (r) => r.requestId === Number(requestIdFromUrl), - ); + const id = Number(requestIdFromUrl); + const match = requests.find((r) => r.requestId === id); if (match) { setOpenReadOnlyRequest(match); + // Paginate to page that holds the deeplinked request + const idx = requests.findIndex((r) => r.requestId === id); + if (idx >= 0) { + setCurrentPage(Math.floor(idx / pageSize) + 1); + setWasDeeplinked(true); + } } else { navigate(ROUTES.REQUEST_FORM, { replace: true }); } @@ -230,6 +237,10 @@ const FormRequests: React.FC = () => { if (searchParams.get('requestId')) { navigate(ROUTES.REQUEST_FORM, { replace: true }); } + if (wasDeeplinked) { + setCurrentPage(1); + setWasDeeplinked(false); + } }} /> )} diff --git a/apps/frontend/src/containers/pantryOrderManagement.tsx b/apps/frontend/src/containers/pantryOrderManagement.tsx index c231b87a4..13af8b5ac 100644 --- a/apps/frontend/src/containers/pantryOrderManagement.tsx +++ b/apps/frontend/src/containers/pantryOrderManagement.tsx @@ -41,6 +41,10 @@ const PantryOrderManagement: React.FC = () => { // State to hold selected order for details modal const [selectedOrderId, setSelectedOrderId] = useState(null); + // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. + const [deeplinkedStatus, setDeeplinkedStatus] = useState( + null, + ); const [selectedActionOrder, setSelectedActionOrder] = useState(null); @@ -123,9 +127,25 @@ const PantryOrderManagement: React.FC = () => { const allOrders = Object.values(statusOrders).flat(); if (!orderIdFromUrl || allOrders.length === 0) return; - const match = allOrders.find((o) => o.orderId === Number(orderIdFromUrl)); + const id = Number(orderIdFromUrl); + const match = allOrders.find((o) => o.orderId === id); if (match) { setSelectedOrderId(match.orderId); + // Paginate the containing status to the page that holds this order. + for (const status of Object.values(OrderStatus)) { + const sorted = [...statusOrders[status]].sort((a, b) => + b.createdAt.localeCompare(a.createdAt), + ); + const idx = sorted.findIndex((o) => o.orderId === id); + if (idx >= 0) { + setCurrentPages((prev) => ({ + ...prev, + [status]: Math.floor(idx / MAX_PER_STATUS) + 1, + })); + setDeeplinkedStatus(status); + break; + } + } } else { navigate(ROUTES.PANTRY_ORDER_MANAGEMENT, { replace: true }); } @@ -216,6 +236,13 @@ const PantryOrderManagement: React.FC = () => { onClose={() => { setSelectedOrderId(null); navigate(ROUTES.PANTRY_ORDER_MANAGEMENT, { replace: true }); + if (deeplinkedStatus) { + setCurrentPages((prev) => ({ + ...prev, + [deeplinkedStatus]: 1, + })); + setDeeplinkedStatus(null); + } }} /> )} diff --git a/apps/frontend/src/containers/volunteerOrderManagement.tsx b/apps/frontend/src/containers/volunteerOrderManagement.tsx index 591981416..20ea565f7 100644 --- a/apps/frontend/src/containers/volunteerOrderManagement.tsx +++ b/apps/frontend/src/containers/volunteerOrderManagement.tsx @@ -75,6 +75,11 @@ const VolunteerOrderManagement: React.FC = () => { const [actionModalOrder, setActionModalOrder] = useState(null); + // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. + const [deeplinkedStatus, setDeeplinkedStatus] = useState( + null, + ); + const [currentPages, setCurrentPages] = useState>( { [OrderStatus.SHIPPED]: 1, @@ -177,9 +182,27 @@ const VolunteerOrderManagement: React.FC = () => { const allOrders = Object.values(statusOrders).flat(); if (!orderIdFromUrl || allOrders.length === 0) return; - const match = allOrders.find((o) => o.orderId === Number(orderIdFromUrl)); + const id = Number(orderIdFromUrl); + const match = allOrders.find((o) => o.orderId === id); if (match) { setSelectedOrderId(match.orderId); + + // Paginate the containing status to the page that holds this order. + for (const status of Object.values(OrderStatus)) { + const sorted = [...statusOrders[status]].sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ); + const idx = sorted.findIndex((o) => o.orderId === id); + if (idx >= 0) { + setCurrentPages((prev) => ({ + ...prev, + [status]: Math.floor(idx / MAX_PER_STATUS) + 1, + })); + setDeeplinkedStatus(status); + break; + } + } } else { navigate(ROUTES.VOLUNTEER_ORDER_MANAGEMENT, { replace: true }); } @@ -282,7 +305,6 @@ const VolunteerOrderManagement: React.FC = () => { orders={displayedOrders} status={status} colors={ORDER_STATUS_COLORS[status]} - selectedOrderId={selectedOrderId} onOrderSelect={setSelectedOrderId} totalOrders={totalFiltered} currentPage={currentPage} @@ -317,6 +339,24 @@ const VolunteerOrderManagement: React.FC = () => { onActionCompleted={handleActionCompleted} /> )} + + {selectedOrderId && ( + { + setSelectedOrderId(null); + navigate(ROUTES.VOLUNTEER_ORDER_MANAGEMENT, { replace: true }); + if (deeplinkedStatus) { + setCurrentPages((prev) => ({ + ...prev, + [deeplinkedStatus]: 1, + })); + setDeeplinkedStatus(null); + } + }} + /> + )} ); }; @@ -326,7 +366,6 @@ interface OrderStatusSectionProps { status: OrderStatus; colors: string[]; onOrderSelect: (orderId: number | null) => void; - selectedOrderId: number | null; totalOrders: number; currentPage: number; onPageChange: (page: number) => void; @@ -350,7 +389,6 @@ const OrderStatusSection: React.FC = ({ status, colors, onOrderSelect, - selectedOrderId, totalOrders, currentPage, onPageChange, @@ -363,8 +401,6 @@ const OrderStatusSection: React.FC = ({ const [isFilterOpen, setIsFilterOpen] = useState(false); const [isSortOpen, setIsSortOpen] = useState(false); - const navigate = useNavigate(); - const MAX_PER_STATUS = 5; const totalPages = Math.ceil(totalOrders / MAX_PER_STATUS); @@ -808,16 +844,6 @@ const OrderStatusSection: React.FC = ({ })} - {selectedOrderId && ( - { - onOrderSelect(null); - navigate(ROUTES.VOLUNTEER_ORDER_MANAGEMENT, { replace: true }); - }} - /> - )} {totalPages > 1 && ( From 4d255fc03fa1dd047a6201b10c386a4e9a52669e Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 27 May 2026 11:31:11 -0400 Subject: [PATCH 5/8] comments --- apps/frontend/src/components/foodRequestManagement.tsx | 6 ++---- apps/frontend/src/containers/adminDonation.tsx | 1 - apps/frontend/src/containers/foodManufacturerDashboard.tsx | 1 + .../src/containers/foodManufacturerDonationManagement.tsx | 4 +--- apps/frontend/src/containers/formRequests.tsx | 1 - 5 files changed, 4 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/foodRequestManagement.tsx b/apps/frontend/src/components/foodRequestManagement.tsx index 96103c288..c54741c14 100644 --- a/apps/frontend/src/components/foodRequestManagement.tsx +++ b/apps/frontend/src/components/foodRequestManagement.tsx @@ -21,7 +21,6 @@ import VolunteerRequestActionRequiredModal from '@components/forms/volunteerRequ import CreateNewOrderModal from '@components/forms/createNewOrderModal'; import { useAlert } from '../hooks/alert'; import { useNavigate, useLocation } from 'react-router-dom'; -import { ROUTES } from '../routes'; interface RequestManagementProps { fetchRequests: () => Promise; @@ -96,9 +95,9 @@ const RequestManagement: React.FC = ({ setWasDeeplinked(true); } } else { - navigate(ROUTES.REQUEST_FORM, { replace: true }); + navigate(location.pathname, { replace: true }); } - }, [initialRequestId, requests, navigate]); + }, [initialRequestId, requests, navigate, location]); const pantryOptions = [ ...new Set( @@ -407,7 +406,6 @@ const RequestManagement: React.FC = ({ navigate(location.pathname, { replace: true }); } if (wasDeeplinked) { - setCurrentPage(1); setWasDeeplinked(false); } }} diff --git a/apps/frontend/src/containers/adminDonation.tsx b/apps/frontend/src/containers/adminDonation.tsx index f2986ca67..a7eb683ef 100644 --- a/apps/frontend/src/containers/adminDonation.tsx +++ b/apps/frontend/src/containers/adminDonation.tsx @@ -297,7 +297,6 @@ const AdminDonation: React.FC = () => { setSelectedDonation(null); navigate(ROUTES.ADMIN_DONATION, { replace: true }); if (wasDeeplinked) { - setCurrentPage(1); setWasDeeplinked(false); } }} diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index afe44b557..29ae91d09 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -41,6 +41,7 @@ const FoodManufacturerDashboard: React.FC = () => { ApiClient.getAllDonationsByFoodManufacturer(fmId), ]); + // If reminders is successfully retrieved from API with the Promise.allSettled if (reminders.status === 'fulfilled') { setUpcomingReminders(reminders.value); } else { diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index 0a2946a46..50888b059 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -20,9 +20,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom'; import { ROUTES } from '../routes'; import { FloatingAlert } from '@components/floatingAlert'; import { useAlert } from '../hooks/alert'; -import { useNavigate, useSearchParams } from 'react-router-dom'; import DonationDetailsModal from '@components/forms/donationDetailsModal'; -import { ROUTES } from '../routes'; import FmCompleteRequiredActionsModal from '@components/forms/fmCompleteRequiredActionsModal'; const MAX_PER_STATUS = 5; @@ -157,7 +155,7 @@ const FoodManufacturerDonationManagement: React.FC = () => { const id = Number(donationIdParam); setSelectedDonationId(id); }, [searchParams, setErrorMessage]); - + const handleResubmitClose = () => { setIsResubmitOpen(false); if (resubmitDonationId) { diff --git a/apps/frontend/src/containers/formRequests.tsx b/apps/frontend/src/containers/formRequests.tsx index 8e50ed442..6d4d8046c 100644 --- a/apps/frontend/src/containers/formRequests.tsx +++ b/apps/frontend/src/containers/formRequests.tsx @@ -238,7 +238,6 @@ const FormRequests: React.FC = () => { navigate(ROUTES.REQUEST_FORM, { replace: true }); } if (wasDeeplinked) { - setCurrentPage(1); setWasDeeplinked(false); } }} From 9eef5e5d82e2f5b7f63e13d88aa0464ec154c985 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Thu, 28 May 2026 09:48:09 -0400 Subject: [PATCH 6/8] comments --- .../src/containers/foodManufacturerDashboard.tsx | 1 + .../foodManufacturerDonationManagement.tsx | 13 +------------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index 29ae91d09..93dd02e0d 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -48,6 +48,7 @@ const FoodManufacturerDashboard: React.FC = () => { setErrorMessage('Error fetching upcoming donations.'); } + // If donations is successfully retrieved from API with the Promise.allSettled if (donations.status === 'fulfilled') { const sorted = donations.value .map((d: DonationDetails) => d.donation) diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index 50888b059..c684921d6 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -23,7 +23,7 @@ import { useAlert } from '../hooks/alert'; import DonationDetailsModal from '@components/forms/donationDetailsModal'; import FmCompleteRequiredActionsModal from '@components/forms/fmCompleteRequiredActionsModal'; -const MAX_PER_STATUS = 5; +const MAX_PER_STATUS = 1; const FoodManufacturerDonationManagement: React.FC = () => { const navigate = useNavigate(); @@ -58,9 +58,6 @@ const FoodManufacturerDonationManagement: React.FC = () => { const [selectedDonationId, setSelectedDonationId] = useState( null, ); - // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. - const [deeplinkedStatus, setDeeplinkedStatus] = - useState(null); // Fetch all donations on component mount and sorts them into their appropriate status lists const fetchDonations = async (fmId: number) => { @@ -103,7 +100,6 @@ const FoodManufacturerDonationManagement: React.FC = () => { ); if (idx >= 0) { initialPages[status] = Math.floor(idx / MAX_PER_STATUS) + 1; - setDeeplinkedStatus(status); break; } } @@ -289,13 +285,6 @@ const FoodManufacturerDonationManagement: React.FC = () => { onDonationClose={() => { setSelectedDonationId(null); navigate(ROUTES.FM_DONATION_MANAGEMENT, { replace: true }); - if (deeplinkedStatus) { - setCurrentPages((prev) => ({ - ...prev, - [deeplinkedStatus]: 1, - })); - setDeeplinkedStatus(null); - } }} /> From fe19495ee5c6552de6009a4e2991ea1b939e2462 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Thu, 28 May 2026 09:49:14 -0400 Subject: [PATCH 7/8] fix unintended change --- .../src/containers/foodManufacturerDonationManagement.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx index c684921d6..8005e28eb 100644 --- a/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx +++ b/apps/frontend/src/containers/foodManufacturerDonationManagement.tsx @@ -23,7 +23,7 @@ import { useAlert } from '../hooks/alert'; import DonationDetailsModal from '@components/forms/donationDetailsModal'; import FmCompleteRequiredActionsModal from '@components/forms/fmCompleteRequiredActionsModal'; -const MAX_PER_STATUS = 1; +const MAX_PER_STATUS = 5; const FoodManufacturerDonationManagement: React.FC = () => { const navigate = useNavigate(); From f207f6856963403b627d3b4204e2d750141abcb2 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Fri, 29 May 2026 10:06:26 -0400 Subject: [PATCH 8/8] remove deprecated deeplink code --- .../src/components/foodRequestManagement.tsx | 7 ------- apps/frontend/src/containers/adminDonation.tsx | 7 ------- .../src/containers/adminOrderManagement.tsx | 12 ------------ apps/frontend/src/containers/formRequests.tsx | 6 ------ .../src/containers/pantryOrderManagement.tsx | 12 ------------ .../src/containers/volunteerOrderManagement.tsx | 13 ------------- 6 files changed, 57 deletions(-) diff --git a/apps/frontend/src/components/foodRequestManagement.tsx b/apps/frontend/src/components/foodRequestManagement.tsx index c54741c14..7d0bce590 100644 --- a/apps/frontend/src/components/foodRequestManagement.tsx +++ b/apps/frontend/src/components/foodRequestManagement.tsx @@ -50,9 +50,6 @@ const RequestManagement: React.FC = ({ const [selectedCreateOrderRequest, setSelectedCreateOrderRequest] = useState(null); - // Tracks whether the current page was advanced by a deeplink so the close handler knows to revert it back to page 1 - const [wasDeeplinked, setWasDeeplinked] = useState(false); - const [errorAlertState, setErrorMessage] = useAlert(); const [successAlertState, setSuccessMessage] = useAlert(); @@ -92,7 +89,6 @@ const RequestManagement: React.FC = ({ ); if (idx >= 0) { setCurrentPage(Math.floor(idx / itemsPerPage) + 1); - setWasDeeplinked(true); } } else { navigate(location.pathname, { replace: true }); @@ -405,9 +401,6 @@ const RequestManagement: React.FC = ({ if (initialRequestId) { navigate(location.pathname, { replace: true }); } - if (wasDeeplinked) { - setWasDeeplinked(false); - } }} /> )} diff --git a/apps/frontend/src/containers/adminDonation.tsx b/apps/frontend/src/containers/adminDonation.tsx index a7eb683ef..c0fa1afe5 100644 --- a/apps/frontend/src/containers/adminDonation.tsx +++ b/apps/frontend/src/containers/adminDonation.tsx @@ -36,9 +36,6 @@ const AdminDonation: React.FC = () => { null, ); - // Tracks whether the current page was advanced by a deeplink so the close handler knows to revert it back to page 1. - const [wasDeeplinked, setWasDeeplinked] = useState(false); - const [alertState, setAlertMessage] = useAlert(); useEffect(() => { @@ -77,7 +74,6 @@ const AdminDonation: React.FC = () => { const idx = sortedAtLoad.findIndex((d) => d.donationId === id); if (idx >= 0) { setCurrentPage(Math.floor(idx / itemsPerPage) + 1); - setWasDeeplinked(true); } } else { navigate(ROUTES.ADMIN_DONATION, { replace: true }); @@ -296,9 +292,6 @@ const AdminDonation: React.FC = () => { onClose={() => { setSelectedDonation(null); navigate(ROUTES.ADMIN_DONATION, { replace: true }); - if (wasDeeplinked) { - setWasDeeplinked(false); - } }} /> )} diff --git a/apps/frontend/src/containers/adminOrderManagement.tsx b/apps/frontend/src/containers/adminOrderManagement.tsx index b55ef6236..26f56e931 100644 --- a/apps/frontend/src/containers/adminOrderManagement.tsx +++ b/apps/frontend/src/containers/adminOrderManagement.tsx @@ -51,10 +51,6 @@ const AdminOrderManagement: React.FC = () => { // State to hold selected order for details modal const [selectedOrderId, setSelectedOrderId] = useState(null); - // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. - const [deeplinkedStatus, setDeeplinkedStatus] = useState( - null, - ); // State to hold current page per status const [currentPages, setCurrentPages] = useState>( @@ -179,7 +175,6 @@ const AdminOrderManagement: React.FC = () => { ...prev, [status]: Math.floor(idx / MAX_PER_STATUS) + 1, })); - setDeeplinkedStatus(status); break; } } @@ -273,13 +268,6 @@ const AdminOrderManagement: React.FC = () => { onClose={() => { setSelectedOrderId(null); navigate(ROUTES.ADMIN_ORDER_MANAGEMENT, { replace: true }); - if (deeplinkedStatus) { - setCurrentPages((prev) => ({ - ...prev, - [deeplinkedStatus]: 1, - })); - setDeeplinkedStatus(null); - } }} /> )} diff --git a/apps/frontend/src/containers/formRequests.tsx b/apps/frontend/src/containers/formRequests.tsx index 6d4d8046c..40a7cfb48 100644 --- a/apps/frontend/src/containers/formRequests.tsx +++ b/apps/frontend/src/containers/formRequests.tsx @@ -37,8 +37,6 @@ const FormRequests: React.FC = () => { const [openReadOnlyRequest, setOpenReadOnlyRequest] = useState(null); - // Tracks whether the current page was advanced by a deeplink so the close handler knows to revert it back to page 1. - const [wasDeeplinked, setWasDeeplinked] = useState(false); const [searchParams] = useSearchParams(); const navigate = useNavigate(); @@ -83,7 +81,6 @@ const FormRequests: React.FC = () => { const idx = requests.findIndex((r) => r.requestId === id); if (idx >= 0) { setCurrentPage(Math.floor(idx / pageSize) + 1); - setWasDeeplinked(true); } } else { navigate(ROUTES.REQUEST_FORM, { replace: true }); @@ -237,9 +234,6 @@ const FormRequests: React.FC = () => { if (searchParams.get('requestId')) { navigate(ROUTES.REQUEST_FORM, { replace: true }); } - if (wasDeeplinked) { - setWasDeeplinked(false); - } }} /> )} diff --git a/apps/frontend/src/containers/pantryOrderManagement.tsx b/apps/frontend/src/containers/pantryOrderManagement.tsx index 476e269b2..a02810e7c 100644 --- a/apps/frontend/src/containers/pantryOrderManagement.tsx +++ b/apps/frontend/src/containers/pantryOrderManagement.tsx @@ -45,10 +45,6 @@ const PantryOrderManagement: React.FC = () => { // State to hold selected order for details modal const [selectedOrderId, setSelectedOrderId] = useState(null); - // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. - const [deeplinkedStatus, setDeeplinkedStatus] = useState( - null, - ); const [selectedActionOrder, setSelectedActionOrder] = useState(null); @@ -146,7 +142,6 @@ const PantryOrderManagement: React.FC = () => { ...prev, [status]: Math.floor(idx / MAX_PER_STATUS) + 1, })); - setDeeplinkedStatus(status); break; } } @@ -240,13 +235,6 @@ const PantryOrderManagement: React.FC = () => { onClose={() => { setSelectedOrderId(null); navigate(ROUTES.PANTRY_ORDER_MANAGEMENT, { replace: true }); - if (deeplinkedStatus) { - setCurrentPages((prev) => ({ - ...prev, - [deeplinkedStatus]: 1, - })); - setDeeplinkedStatus(null); - } }} /> )} diff --git a/apps/frontend/src/containers/volunteerOrderManagement.tsx b/apps/frontend/src/containers/volunteerOrderManagement.tsx index 744aa145d..d23677005 100644 --- a/apps/frontend/src/containers/volunteerOrderManagement.tsx +++ b/apps/frontend/src/containers/volunteerOrderManagement.tsx @@ -69,11 +69,6 @@ const VolunteerOrderManagement: React.FC = () => { const [actionModalOrder, setActionModalOrder] = useState(null); - // Tracks which status had its page advanced by a deeplink so we can revert that single page back to 1 when the modal closes. - const [deeplinkedStatus, setDeeplinkedStatus] = useState( - null, - ); - const [currentPages, setCurrentPages] = useState>( { [OrderStatus.SHIPPED]: 1, @@ -193,7 +188,6 @@ const VolunteerOrderManagement: React.FC = () => { ...prev, [status]: Math.floor(idx / MAX_PER_STATUS) + 1, })); - setDeeplinkedStatus(status); break; } } @@ -341,13 +335,6 @@ const VolunteerOrderManagement: React.FC = () => { onClose={() => { setSelectedOrderId(null); navigate(ROUTES.VOLUNTEER_ORDER_MANAGEMENT, { replace: true }); - if (deeplinkedStatus) { - setCurrentPages((prev) => ({ - ...prev, - [deeplinkedStatus]: 1, - })); - setDeeplinkedStatus(null); - } }} /> )}