From 9e74adabb5b420d76781e584c103183b0e6e007e Mon Sep 17 00:00:00 2001 From: amywng <147568742+amywng@users.noreply.github.com> Date: Sun, 2 Nov 2025 12:20:42 -0500 Subject: [PATCH 01/19] rebase main --- apps/backend/src/orders/order.controller.ts | 1 + .../pantries/dtos/pantry-application.dto.ts | 5 +- apps/backend/src/pantries/pantries.service.ts | 3 +- apps/frontend/src/app.tsx | 5 + .../forms/pantryApplicationForm.tsx | 1081 ++++++++++------- .../containers/pantryApplicationSubmitted.tsx | 47 + apps/frontend/src/theme.ts | 16 + package.json | 1 + 8 files changed, 717 insertions(+), 442 deletions(-) create mode 100644 apps/frontend/src/containers/pantryApplicationSubmitted.tsx diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 31ea1eb7..b43b8501 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -13,6 +13,7 @@ import { Order } from './order.entity'; import { Pantry } from '../pantries/pantries.entity'; import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity'; import { FoodRequest } from '../foodRequests/request.entity'; +import { Donation } from '../donations/donations.entity'; import { AllocationsService } from '../allocations/allocations.service'; import { OrderStatus } from './types'; diff --git a/apps/backend/src/pantries/dtos/pantry-application.dto.ts b/apps/backend/src/pantries/dtos/pantry-application.dto.ts index fff83b4d..134477d2 100644 --- a/apps/backend/src/pantries/dtos/pantry-application.dto.ts +++ b/apps/backend/src/pantries/dtos/pantry-application.dto.ts @@ -3,7 +3,6 @@ import { IsBoolean, IsEmail, IsEnum, - IsIn, IsNotEmpty, IsOptional, IsPhoneNumber, @@ -122,6 +121,6 @@ export class PantryApplicationDto { needMoreOptions: string; @IsOptional() - @IsIn(['Yes', 'No']) - newsletterSubscription?: string; + @IsBoolean() + newsletterSubscription?: boolean; } diff --git a/apps/backend/src/pantries/pantries.service.ts b/apps/backend/src/pantries/pantries.service.ts index 2e10535b..101b3aa1 100644 --- a/apps/backend/src/pantries/pantries.service.ts +++ b/apps/backend/src/pantries/pantries.service.ts @@ -62,8 +62,7 @@ export class PantriesService { pantry.activitiesComments = pantryData.activitiesComments; pantry.itemsInStock = pantryData.itemsInStock; pantry.needMoreOptions = pantryData.needMoreOptions; - pantry.newsletterSubscription = - pantryData?.newsletterSubscription === 'Yes'; + pantry.newsletterSubscription = pantryData.newsletterSubscription; // pantry contact is automatically added to User table await this.repo.save(pantry); diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index 89e0fccf..90be09d1 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -14,6 +14,7 @@ import { submitFoodRequestFormModal } from '@components/forms/requestFormModal'; import { submitDeliveryConfirmationFormModal } from '@components/forms/deliveryConfirmationModal'; import FormRequests from '@containers/FormRequests'; import PantryApplication from '@containers/pantryApplication'; +import PantryApplicationSubmitted from '@containers/pantryApplicationSubmitted'; import { submitPantryApplicationForm } from '@components/forms/pantryApplicationForm'; import ApprovePantries from '@containers/approvePantries'; import VolunteerManagement from '@containers/volunteerManagement'; @@ -59,6 +60,10 @@ const router = createBrowserRouter([ element: , action: submitPantryApplicationForm, }, + { + path: '/pantry-application/submitted', + element: , + }, { path: '/food-manufacturer-order-dashboard', element: , diff --git a/apps/frontend/src/components/forms/pantryApplicationForm.tsx b/apps/frontend/src/components/forms/pantryApplicationForm.tsx index 4c7325bf..49a6088a 100644 --- a/apps/frontend/src/components/forms/pantryApplicationForm.tsx +++ b/apps/frontend/src/components/forms/pantryApplicationForm.tsx @@ -2,7 +2,6 @@ import { Box, Button, Checkbox, - CheckboxGroup, Heading, Input, RadioGroup, @@ -10,7 +9,14 @@ import { Text, Field, Textarea, - Fieldset, + SimpleGrid, + Portal, + NativeSelect, + NativeSelectIndicator, + Combobox, + Wrap, + createListCollection, + Tag, } from '@chakra-ui/react'; import { ActionFunction, @@ -18,491 +24,687 @@ import { Form, redirect, } from 'react-router-dom'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { USPhoneInput } from '@components/forms/usPhoneInput'; import { PantryApplicationDto } from '../../types/types'; import ApiClient from '@api/apiClient'; import { Activity } from '../../types/pantryEnums'; import axios from 'axios'; +const otherRestrictionsOptions: string[] = [ + 'Other allergy (e.g., yeast, sunflower, etc.)', + 'Other allergic illness (e.g., eosinophilic esophagitis, FPIES, oral allergy syndrome)', + 'Other dietary restriction', +]; + +const dietaryRestrictionOptions = [ + 'Egg allergy', + 'Fish allergy', + 'Milk allergy', + 'Lactose intolerance/dairy sensitivity', + 'Peanut allergy', + 'Shellfish allergy', + 'Soy allergy', + 'Sesame allergy', + 'Tree nut allergy', + 'Wheat allergy', + 'Celiac disease', + 'Gluten sensitivity (not celiac disease)', + "Gastrointestinal illness (IBS, Crohn's, gastroparesis, etc.)", + ...otherRestrictionsOptions, + 'Unsure', +]; + +const activityOptions = [ + 'Create a labeled, allergy-friendly shelf or shelves', + 'Provide clients and staff/volunteers with educational pamphlets', + "Use a spreadsheet to track clients' medical dietary needs and distribution of SSF items per month", + 'Post allergen-free resource flyers throughout pantry', + 'Survey your clients to determine their medical dietary needs', + 'Collect feedback from allergen-avoidant clients on SSF foods', + 'Something else', +]; + const PantryApplicationForm: React.FC = () => { const [contactPhone, setContactPhone] = useState(''); - - // We need to keep track of the activities selected so we can provide custom - // validation (at least one activity chosen). const [activities, setActivities] = useState([]); - const noActivitiesSelected: boolean = activities.length === 0; - - // Option values and state below are for options that, when selected - const allergenClientsExactOption: string = 'I have an exact number'; - const otherRestrictionsOptions: string[] = [ - 'Other allergy (e.g., yeast, sunflower, etc.)', - 'Other allergic illness (e.g., eosinophilic esophagitis, FPIES, oral allergy syndrome)', - 'Other dietary restriction', - ]; - const reserveFoodForAllergicYesOption: string = 'Yes'; - const reserveFoodForAllergicSomeOption: string = 'Some'; const [allergenClients, setAllergenClients] = useState(); const [restrictions, setRestrictions] = useState([]); - const [reserveFoodForAllergic, setReserveFoodForAllergic] = useState< + const [reserveFoodForAllergic, setReserveFoodForAllergic] = useState(); + const [dedicatedAllergyFriendly, setDedicatedAllergyFriendly] = useState< + string | undefined + >(); + const [clientVisitFrequency, setClientVisitFrequency] = useState< + string | undefined + >(); + const [identifyAllergensConfidence, setIdentifyAllergensConfidence] = useState< + string | undefined + >(); + const [serveAllergicChildren, setServeAllergicChildren] = useState< string | undefined >(); + const [refrigeratedDonation, setRefrigeratedDonation] = useState(); + const [newsletterSubscription, setNewsletterSubscription] = useState(); + const [searchRestriction, setSearchRestriction] = useState(''); + const [searchActivity, setSearchActivity] = useState(''); + + const sectionTitleStyles = { + fontFamily: "'Inter', sans-serif", + fontWeight: '600', + fontSize: 'md', + }; + + const sectionSubtitleStyles = { + fontFamily: "'Inter', sans-serif", + fontWeight: '400', + color: 'gray', + mb: '2em', + fontSize: 'sm', + } + + const fieldHeaderStyles = { + color: 'neutral.800', + fontFamily: "'Inter', sans-serif", + fontSize: 'sm', + fontWeight: '600', + }; + + const filteredRestrictions = useMemo( + () => + dietaryRestrictionOptions.filter((option) => + option.toLowerCase().includes(searchRestriction.toLowerCase()), + ), + [searchRestriction], + ); + + const restrictionsCollection = useMemo( + () => createListCollection({ items: filteredRestrictions}), + [filteredRestrictions], + ); + + const filteredActivities = useMemo( + () => + activityOptions.filter((option) => + option.toLowerCase().includes(searchActivity.toLowerCase()), + ), + [searchActivity], + ); + + const activitiesCollection = useMemo( + () => createListCollection({ items: filteredActivities}), + [filteredActivities], + ); return ( - -
- - SSF Pantry Sign-Up Form + + + + Pantry Sign Up Form - - - Welcome! We are excited to have you join us in our mission to secure - allergen-safe food and promote food equity. - - Please fill out the following information to get started. - - - - First Name - - - - Whom should we contact at your pantry? - - - - - - Last Name - - - - Whom should we contact at your pantry? - - - - - - Email Address - - - - Please provide the email address of the pantry contact listed above. - - - - - - Phone Number - - - - Please provide the phone number of the pantry contact listed above. - - - - - - - Food Pantry Name - - - - - -
- - Address * + + Welcome! We are so excited to have you join us in our mission to secure allergen-safe food and promote food equity. + + + + + + Pantry Application - + + Please fill out the folllowing information to get started. + + + Point of Contact Information + + + Please provide information about whom we should contact at your pantry. + + + + + First Name + + + + + + + Last Name + + + + + + + Phone Number + + + + + + + Email Address + + + + + + + + Address + + Please list your address for food shipments. + + + + Address Line 1 + + + + + + + Address Line 2 + + + + + + City/Town + + + + + + + State/Region/Province + + + + + + + Zip/Post Code + + + + + + + Country + + + + + + + Pantry Details + + + + + Pantry Name + + + + + - - Address Line 1 - + + Approximately how many allergen-avoidant clients does your pantry + serve? + - + + setAllergenClients(e.target.value)} + placeholder="Select an option" + borderColor="neutral.100" + name="allergenClients" + > + {[ + '< 10', + '10 to 20', + '20 to 50', + '50 to 100', + '> 100', + "I'm not sure", + allergenClientsExactOption, + ].map((value) => ( + + ))} + + + + {allergenClients === allergenClientsExactOption && ( + + + Please provide the exact number, if known + + + + )} - - Address Line 2 + + Which food allergies or other medical dietary restrictions do + clients at your pantry report? - + setRestrictions(e.value)} + onInputValueChange={(e: {inputValue: string}) => setSearchRestriction(e.inputValue)} + > + + + + + + + + + + + + {filteredRestrictions.map((value) => ( + ) => (e.currentTarget.style.backgroundColor = '#f5f5f5')} + onMouseLeave={(e: React.MouseEvent) => (e.currentTarget.style.backgroundColor = '')} + > + {value} + + + ))} + No dietary restrictions found + + + + + + + {restrictions.map((value) => ( + <> + + + {value} + + + setRestrictions((prev) => + prev.filter((item) => item !== value) + ) + } + style={{ cursor: 'pointer' }} + /> + + + + ))} + + + + {restrictions.find((option) => + otherRestrictionsOptions.includes(option), + ) && ( + + + If you selected "Other," please specify: + + + + )} - - City/Town - + + Would you be able to accept refrigerated/frozen donations from us? + - + + setRefrigeratedDonation(e.target.value)} + placeholder="Select an option" + borderColor="neutral.100" + name="refrigeratedDonation" + > + {['Yes', 'Small quantities only', 'No'].map((value) => ( + + ))} + + + + - - State/Region/Province - - - + setReserveFoodForAllergic(e.checked)} + variant="outline" + name="reserveFoodForAllergic" + > + + + + Are you willing to reserve our food shipments for allergen-avoidant + individuals?{' '} + + + + {reserveFoodForAllergic && ( + + + Please explain how you would do this. + + +