From 6e4562998395ea41edad7f8891f26f905765a35d Mon Sep 17 00:00:00 2001 From: jxuistrying Date: Sun, 24 May 2026 02:02:14 -0400 Subject: [PATCH 1/2] Add empty state components for dashboard --- .../src/components/PageEmptyState.tsx | 67 ++++++ .../src/components/SectionEmptyState.tsx | 35 +++ .../src/containers/adminDashboard.tsx | 208 +++++++++++------- .../src/containers/pantryDashboard.tsx | 144 ++++++++---- .../src/containers/volunteerDashboard.tsx | 131 +++++++---- 5 files changed, 418 insertions(+), 167 deletions(-) create mode 100644 apps/frontend/src/components/PageEmptyState.tsx create mode 100644 apps/frontend/src/components/SectionEmptyState.tsx diff --git a/apps/frontend/src/components/PageEmptyState.tsx b/apps/frontend/src/components/PageEmptyState.tsx new file mode 100644 index 000000000..ade46fca6 --- /dev/null +++ b/apps/frontend/src/components/PageEmptyState.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { Box, Button } from '@chakra-ui/react'; +import { CircleCheck } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; + +interface PageEmptyStateProps { + subtitle: string; + primaryButtonText: string; + primaryButtonLink: string; + secondaryButtonText: string; + secondaryButtonLink: string; +} + +const PageEmptyState: React.FC = ({ + subtitle, + primaryButtonText, + primaryButtonLink, + secondaryButtonText, + secondaryButtonLink, +}) => { + const navigate = useNavigate(); + + return ( + + + + + + Nothing to see here. + + + {subtitle} + + + + + + + ); +}; + +export default PageEmptyState; diff --git a/apps/frontend/src/components/SectionEmptyState.tsx b/apps/frontend/src/components/SectionEmptyState.tsx new file mode 100644 index 000000000..0244f4582 --- /dev/null +++ b/apps/frontend/src/components/SectionEmptyState.tsx @@ -0,0 +1,35 @@ +import { Box } from '@chakra-ui/react'; +import { CircleCheck } from 'lucide-react'; + +interface EmptyStateProps { + subtitle: string; +} + +const SectionEmptyState: React.FC = ({ subtitle }) => { + return ( + + + + + + Nothing to see here. + + + {subtitle} + + + ); +}; + +export default SectionEmptyState; diff --git a/apps/frontend/src/containers/adminDashboard.tsx b/apps/frontend/src/containers/adminDashboard.tsx index 7ce01914e..5b0e00201 100644 --- a/apps/frontend/src/containers/adminDashboard.tsx +++ b/apps/frontend/src/containers/adminDashboard.tsx @@ -16,6 +16,8 @@ import { useAlert } from '../hooks/alert'; import { FloatingAlert } from '@components/floatingAlert'; import { useNavigate } from 'react-router-dom'; import { ROUTES } from '../routes'; +import SectionEmptyState from '@components/SectionEmptyState'; +import PageEmptyState from '@components/PageEmptyState'; const AdminDashboard: React.FC = () => { const navigate = useNavigate(); @@ -84,6 +86,11 @@ const AdminDashboard: React.FC = () => { fetchPendingApplications(); }, [setAlertMessage]); + const isPageEmpty = + pendingApplications.length === 0 && + recentOrders.length === 0 && + recentDonations.length === 0; + return ( {alertState && ( @@ -98,84 +105,133 @@ const AdminDashboard: React.FC = () => { Welcome, {currentUser?.firstName} {currentUser?.lastName} - - Pending Actions - - - {pendingApplications.map((application) => ( - { - navigate( - application.type === 'pantry' - ? ROUTES.PANTRY_MANAGEMENT_DETAILS.replace( - ':pantryId', - application.id.toString(), - ) - : ROUTES.FOOD_MANUFACTURER_APPLICATION_DETAILS.replace( - ':applicationId', - application.id.toString(), - ), - ); - }} - /> - ))} - + {isPageEmpty ? ( + + ) : ( + <> + + Pending Actions + + {pendingApplications.length === 0 ? ( + + + + ) : ( + + {pendingApplications.map((application) => ( + { + navigate( + application.type === 'pantry' + ? ROUTES.PANTRY_MANAGEMENT_DETAILS.replace( + ':pantryId', + application.id.toString(), + ) + : ROUTES.FOOD_MANUFACTURER_APPLICATION_DETAILS.replace( + ':applicationId', + application.id.toString(), + ), + ); + }} + /> + ))} + + )} - - Recent Orders - - - {recentOrders.map((order) => ( - - navigate(`/admin-order-management?orderId=${order.orderId}`) - } - /> - ))} - + + Recent Orders + + {recentOrders.length === 0 ? ( + + + + ) : ( + + {recentOrders.map((order) => ( + + navigate(`/admin-order-management?orderId=${order.orderId}`) + } + /> + ))} + + )} - - Recent Donations - - - {recentDonations.map((donation) => ( - - navigate(`/admin-donation?donationId=${donation.donationId}`) - } - /> - ))} - + + Recent Donations + + {recentDonations.length === 0 ? ( + + + + ) : ( + + {recentDonations.map((donation) => ( + + navigate( + `/admin-donation?donationId=${donation.donationId}`, + ) + } + /> + ))} + + )} + + )} ); }; diff --git a/apps/frontend/src/containers/pantryDashboard.tsx b/apps/frontend/src/containers/pantryDashboard.tsx index 182c9a9ef..907403705 100644 --- a/apps/frontend/src/containers/pantryDashboard.tsx +++ b/apps/frontend/src/containers/pantryDashboard.tsx @@ -12,6 +12,8 @@ import { useAlert } from '../hooks/alert'; import { FloatingAlert } from '@components/floatingAlert'; import { useNavigate } from 'react-router-dom'; import { ROUTES } from '../routes'; +import SectionEmptyState from '@components/SectionEmptyState'; +import PageEmptyState from '@components/PageEmptyState'; const PantryDashboard: React.FC = () => { const navigate = useNavigate(); @@ -61,7 +63,25 @@ const PantryDashboard: React.FC = () => { fetchDashboardData(); }, [setAlertMessage]); - if (!pantry) return null; + if (!pantry) { + return ( + + + Pantry Dashboard + + + + ); + } + + const isPageEmpty = + recentFoodRequests.length === 0 && recentOrders.length === 0; return ( @@ -77,51 +97,85 @@ const PantryDashboard: React.FC = () => { Welcome, {pantry.pantryName} - - Recent Food Requests - - - {recentFoodRequests.map((fr) => ( - - navigate(`${ROUTES.REQUEST_FORM}?requestId=${fr.requestId}`) - } - /> - ))} - + {isPageEmpty ? ( + + ) : ( + <> + + Recent Food Requests + + {recentFoodRequests.length === 0 ? ( + + + + ) : ( + + {recentFoodRequests.map((fr) => ( + + navigate(`${ROUTES.REQUEST_FORM}?requestId=${fr.requestId}`) + } + /> + ))} + + )} - - Recent Orders - - - {recentOrders.map((order) => ( - - navigate( - `${ROUTES.PANTRY_ORDER_MANAGEMENT}?orderId=${order.orderId}`, - ) - } - /> - ))} - + + Recent Orders + + {recentOrders.length === 0 ? ( + + + + ) : ( + + {recentOrders.map((order) => ( + + navigate( + `${ROUTES.PANTRY_ORDER_MANAGEMENT}?orderId=${order.orderId}`, + ) + } + /> + ))} + + )} + + )} ); }; diff --git a/apps/frontend/src/containers/volunteerDashboard.tsx b/apps/frontend/src/containers/volunteerDashboard.tsx index c33843670..5637af1a9 100644 --- a/apps/frontend/src/containers/volunteerDashboard.tsx +++ b/apps/frontend/src/containers/volunteerDashboard.tsx @@ -8,6 +8,8 @@ import { useAlert } from '../hooks/alert'; import { FloatingAlert } from '@components/floatingAlert'; import { useNavigate } from 'react-router-dom'; import { ROUTES } from '../routes'; +import SectionEmptyState from '@components/SectionEmptyState'; +import PageEmptyState from '@components/PageEmptyState'; const VolunteerDashboard: React.FC = () => { const navigate = useNavigate(); @@ -53,6 +55,9 @@ const VolunteerDashboard: React.FC = () => { if (!user) return null; + const isPageEmpty = + recentFoodRequests.length === 0 && recentOrders.length === 0; + return ( {alertState && ( @@ -67,53 +72,87 @@ const VolunteerDashboard: React.FC = () => { Welcome, {user.firstName} {user.lastName} - - Recent Food Requests - - - {recentFoodRequests.map((fr) => ( - - navigate( - `${ROUTES.VOLUNTEER_REQUEST_MANAGEMENT}?requestId=${fr.requestId}`, - ) - } - /> - ))} - + {isPageEmpty ? ( + + ) : ( + <> + + Recent Food Requests + + {recentFoodRequests.length === 0 ? ( + + + + ) : ( + + {recentFoodRequests.map((fr) => ( + + navigate( + `${ROUTES.VOLUNTEER_REQUEST_MANAGEMENT}?requestId=${fr.requestId}`, + ) + } + /> + ))} + + )} - - My Orders - - - {recentOrders.map((order) => ( - - navigate( - `${ROUTES.VOLUNTEER_ORDER_MANAGEMENT}?orderId=${order.orderId}`, - ) - } - /> - ))} - + + My Orders + + {recentOrders.length === 0 ? ( + + + + ) : ( + + {recentOrders.map((order) => ( + + navigate( + `${ROUTES.VOLUNTEER_ORDER_MANAGEMENT}?orderId=${order.orderId}`, + ) + } + /> + ))} + + )} + + )} ); }; From 575b115823de6ba7a1c69c6058fda4d50cd358a9 Mon Sep 17 00:00:00 2001 From: jxuistrying Date: Sat, 30 May 2026 20:56:02 -0400 Subject: [PATCH 2/2] refactor subtitle using entity for ui --- apps/frontend/src/components/PageEmptyState.tsx | 7 +++++-- apps/frontend/src/components/SectionEmptyState.tsx | 8 +++++--- apps/frontend/src/containers/adminDashboard.tsx | 8 ++++---- apps/frontend/src/containers/pantryDashboard.tsx | 6 +++--- apps/frontend/src/containers/volunteerDashboard.tsx | 6 +++--- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/frontend/src/components/PageEmptyState.tsx b/apps/frontend/src/components/PageEmptyState.tsx index ade46fca6..de166dad3 100644 --- a/apps/frontend/src/components/PageEmptyState.tsx +++ b/apps/frontend/src/components/PageEmptyState.tsx @@ -4,7 +4,8 @@ import { CircleCheck } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; interface PageEmptyStateProps { - subtitle: string; + entity?: string; + subtitle?: string; primaryButtonText: string; primaryButtonLink: string; secondaryButtonText: string; @@ -12,6 +13,7 @@ interface PageEmptyStateProps { } const PageEmptyState: React.FC = ({ + entity, subtitle, primaryButtonText, primaryButtonLink, @@ -19,6 +21,7 @@ const PageEmptyState: React.FC = ({ secondaryButtonLink, }) => { const navigate = useNavigate(); + const message = subtitle ?? `You have no ${entity} at this time`; return ( = ({ Nothing to see here. - {subtitle} + {message}