From c1c04858c41ae0ceecc7dc26f07af9d88b85460f Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sat, 25 Oct 2025 19:37:58 -0400 Subject: [PATCH 01/19] Created initial Api and frontend integration, awaiting some validation/error handling --- .../donationItems/donationItems.controller.ts | 52 +++++++++++++++++++ apps/frontend/src/api/apiClient.ts | 7 +++ .../components/forms/newDonationFormModal.tsx | 31 ++++++----- 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/apps/backend/src/donationItems/donationItems.controller.ts b/apps/backend/src/donationItems/donationItems.controller.ts index 96381fc0..89133263 100644 --- a/apps/backend/src/donationItems/donationItems.controller.ts +++ b/apps/backend/src/donationItems/donationItems.controller.ts @@ -74,6 +74,58 @@ export class DonationItemsController { ); } + @Post('/create-multiple') + @ApiBody({ + description: 'List of donation items to create', + schema: { + type: 'array', + items: { + type: 'object', + properties: { + donationId: { type: 'integer', example: 1 }, + itemName: { type: 'string', example: 'Rice Noodles' }, + quantity: { type: 'integer', example: 100 }, + reservedQuantity: { type: 'integer', example: 0 }, + status: { type: 'string', example: 'available' }, + ozPerItem: { type: 'integer', example: 5 }, + estimatedValue: { type: 'integer', example: 100 }, + foodType: { type: 'string', example: 'grain' }, + }, + }, + }, + }) + async createMultipleDonationItems( + @Body() + body: { + donationId: number; + itemName: string; + quantity: number; + reservedQuantity: number; + status: string; + ozPerItem: number; + estimatedValue: number; + foodType: string; + }[], + ): Promise { + let createdDonationItems: DonationItem[] = []; + + for (const donationItem of body) { + const createdDonationItem = await this.donationItemsService.create( + donationItem.donationId, + donationItem.itemName, + donationItem.quantity, + donationItem.reservedQuantity, + donationItem.status, + donationItem.ozPerItem, + donationItem.estimatedValue, + donationItem.foodType, + ); + createdDonationItems.push(createdDonationItem); + } + + return createdDonationItems; + } + @Patch('/update-quantity/:itemId') async updateDonationItemQuantity( @Param('itemId', ParseIntPipe) itemId: number, diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 74d2c812..8515a723 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -54,6 +54,13 @@ export class ApiClient { return this.post('/api/requests/create', body) as Promise; } + public async postMultipleDonationItems(body: unknown): Promise { + return this.post( + '/api/donation-items/create-multiple', + body, + ) as Promise; + } + private async patch(path: string, body: unknown): Promise { return this.axiosInstance .patch(path, body) diff --git a/apps/frontend/src/components/forms/newDonationFormModal.tsx b/apps/frontend/src/components/forms/newDonationFormModal.tsx index d6697d08..fb2f703f 100644 --- a/apps/frontend/src/components/forms/newDonationFormModal.tsx +++ b/apps/frontend/src/components/forms/newDonationFormModal.tsx @@ -13,7 +13,7 @@ import { } from '@chakra-ui/react'; import { useState } from 'react'; import ApiClient from '@api/apiClient'; -import { FoodTypes } from '../../types/types'; +import { DonationItem, FoodTypes } from '../../types/types'; interface NewDonationFormModalProps { onDonationSuccess: () => void; @@ -127,8 +127,11 @@ const NewDonationFormModal: React.FC = ({ onDonationSuccess(); if (donationId) { + const multipleDonationItems_body: Partial[] = []; + + // Populate the body with all donation items for the donation rows.forEach(async (row) => { - const donationItem_body = { + const donationItem_body: Partial = { donationId: donationId, itemName: row.foodItem, quantity: parseInt(row.numItems), @@ -137,16 +140,18 @@ const NewDonationFormModal: React.FC = ({ foodType: row.foodType, }; - const donationItemResponse = await ApiClient.postDonationItem( - donationItem_body, - ); - if (donationItemResponse) { - console.log('Donation item submitted successfully'); - } else { - console.error('Failed to submit donation item'); - alert('Failed to submit donation item'); - } + multipleDonationItems_body.push(donationItem_body); }); + + const multipleDonationItemResponse = await ApiClient.postMultipleDonationItems( + multipleDonationItems_body, + ); + + if (multipleDonationItemResponse) { + console.log('Donation items submitted successfully'); + } else { + alert('Failed to submit donation items'); + } setRows([ { id: 1, @@ -161,12 +166,10 @@ const NewDonationFormModal: React.FC = ({ setTotalOz(0); setTotalValue(0); } else { - console.error('Failed to submit donation'); alert('Failed to submit donation'); } } catch (error) { - console.error('Error submitting new donation', error); - alert('Error submitting new donation'); + alert('Error submitting new donation: ' + error); } }; From 09d935729c83a41fafb476af2ec11e0f0ab4416b Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sat, 25 Oct 2025 19:42:32 -0400 Subject: [PATCH 02/19] Changes for setting upstream --- .../donationItems/donationItems.controller.ts | 2 +- apps/frontend/src/api/apiClient.ts | 11 +- .../forms/deliveryConfirmationModal.tsx | 11 +- .../components/forms/donationDetailsModal.tsx | 92 +++-- .../components/forms/newDonationFormModal.tsx | 320 +++++++++--------- apps/frontend/src/main.tsx | 2 +- apps/frontend/src/theme.ts | 4 +- 7 files changed, 218 insertions(+), 224 deletions(-) diff --git a/apps/backend/src/donationItems/donationItems.controller.ts b/apps/backend/src/donationItems/donationItems.controller.ts index 89133263..dcf88a76 100644 --- a/apps/backend/src/donationItems/donationItems.controller.ts +++ b/apps/backend/src/donationItems/donationItems.controller.ts @@ -107,7 +107,7 @@ export class DonationItemsController { foodType: string; }[], ): Promise { - let createdDonationItems: DonationItem[] = []; + const createdDonationItems: DonationItem[] = []; for (const donationItem of body) { const createdDonationItem = await this.donationItemsService.create( diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 8515a723..1d8447dc 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -54,11 +54,12 @@ export class ApiClient { return this.post('/api/requests/create', body) as Promise; } - public async postMultipleDonationItems(body: unknown): Promise { - return this.post( - '/api/donation-items/create-multiple', - body, - ) as Promise; + public async postMultipleDonationItems( + body: unknown, + ): Promise { + return this.post('/api/donation-items/create-multiple', body) as Promise< + DonationItem[] + >; } private async patch(path: string, body: unknown): Promise { diff --git a/apps/frontend/src/components/forms/deliveryConfirmationModal.tsx b/apps/frontend/src/components/forms/deliveryConfirmationModal.tsx index c85b09cf..3a71ccb1 100644 --- a/apps/frontend/src/components/forms/deliveryConfirmationModal.tsx +++ b/apps/frontend/src/components/forms/deliveryConfirmationModal.tsx @@ -59,7 +59,7 @@ const DeliveryConfirmationModal: React.FC = ({ return ( { + onOpenChange={(e) => { if (!e.open) onClose(); }} size="xl" @@ -91,6 +91,11 @@ const DeliveryConfirmationModal: React.FC = ({ fontSize={20} fontWeight={700} /> + = ({ Select up to 3 photos to upload. + + Select up to 3 photos to upload. + {renderPhotoNames()} @@ -151,6 +159,7 @@ export const submitDeliveryConfirmationFormModal: ActionFunction = async ({ const form = await request.formData(); const confirmDeliveryData = new FormData(); + const pantryId = form.get('pantryId'); const pantryId = form.get('pantryId'); const requestId = form.get('requestId') as string; confirmDeliveryData.append('requestId', requestId); diff --git a/apps/frontend/src/components/forms/donationDetailsModal.tsx b/apps/frontend/src/components/forms/donationDetailsModal.tsx index 093b06df..9837c424 100644 --- a/apps/frontend/src/components/forms/donationDetailsModal.tsx +++ b/apps/frontend/src/components/forms/donationDetailsModal.tsx @@ -8,12 +8,11 @@ import { CloseButton, } from '@chakra-ui/react'; import ApiClient from '@api/apiClient'; -import { Donation } from 'types/types'; -import { DonationItem } from 'types/types'; +import { Donation, DonationItem } from 'types/types'; import { formatDate } from '@utils/utils'; interface DonationDetailsModalProps { - donation: Donation; + donation: Donation | null; isOpen: boolean; onClose: () => void; } @@ -23,30 +22,34 @@ const DonationDetailsModal: React.FC = ({ isOpen, onClose, }) => { + const [loadedDonation, setLoadedDonation] = useState(null); const [items, setItems] = useState([]); + const donationId = donation?.donationId; // adjust if your ID field is different + useEffect(() => { - if (isOpen) { - const fetchData = async () => { - try { - const itemsData = await ApiClient.getDonationItemsByDonationId( - donation.donationId, - ); - - setItems(itemsData); - } catch (error) { - alert('Error fetching donation details:' + error); - } - }; - - fetchData(); - } - }, [isOpen, donation]); + if (!isOpen || !donationId) return; + + const fetchData = async () => { + try { + const donationData = await ApiClient.getOrderDonation(donationId); + const itemsData = await ApiClient.getDonationItemsByDonationId( + donationId + ); + + setLoadedDonation(donationData); + setItems(itemsData); + } catch (err) { + alert('Error fetching donation details: ' + err); + } + }; + + fetchData(); + }, [isOpen, donationId]); + // Group items by food type const groupedItems = items.reduce((acc, item) => { - if (!acc[item.foodType]) { - acc[item.foodType] = []; - } + if (!acc[item.foodType]) acc[item.foodType] = []; acc[item.foodType].push(item); return acc; }, {} as Record); @@ -54,14 +57,14 @@ const DonationDetailsModal: React.FC = ({ return ( { + onOpenChange={(e) => { if (!e.open) onClose(); }} - closeOnInteractOutside scrollBehavior="inside" > - + + @@ -70,21 +73,17 @@ const DonationDetailsModal: React.FC = ({ - - Donation #{donation.donationId} Details + + Donation #{donationId} Details - {donation && ( + + {loadedDonation && ( <> - - {donation.foodManufacturer?.foodManufacturerName} + + {loadedDonation.foodManufacturer?.foodManufacturerName} - - {formatDate(donation.dateDonated)} + + {formatDate(loadedDonation.dateDonated)} )} @@ -92,18 +91,14 @@ const DonationDetailsModal: React.FC = ({ - {donation && ( + {loadedDonation && ( {Object.entries(groupedItems).map(([foodType, typeItems]) => ( - + {foodType} + {typeItems.map((item, index) => ( = ({ overflow="hidden" > - - {item.itemName} - + {item.itemName} + = ({ justifyContent="center" bg="white" > - - {item.quantity} - + {item.quantity} ))} diff --git a/apps/frontend/src/components/forms/newDonationFormModal.tsx b/apps/frontend/src/components/forms/newDonationFormModal.tsx index fb2f703f..340ff33b 100644 --- a/apps/frontend/src/components/forms/newDonationFormModal.tsx +++ b/apps/frontend/src/components/forms/newDonationFormModal.tsx @@ -10,6 +10,7 @@ import { Dialog, NativeSelect, NativeSelectIndicator, + Portal, } from '@chakra-ui/react'; import { useState } from 'react'; import ApiClient from '@api/apiClient'; @@ -43,7 +44,7 @@ const NewDonationFormModal: React.FC = ({ const handleChange = (id: number, field: string, value: string) => { const updatedRows = rows.map((row) => - row.id === id ? { ...row, [field]: value } : row, + row.id === id ? { ...row, [field]: value } : row ); setRows(updatedRows); @@ -57,18 +58,16 @@ const NewDonationFormModal: React.FC = ({ updatedRows.forEach((row) => { if (row.numItems && row.ozPerItem && row.valuePerItem) { - totalItems += parseInt(row.numItems); - totalOz += parseFloat(row.ozPerItem) * parseInt(row.numItems); - totalValue += parseFloat(row.valuePerItem) * parseInt(row.numItems); + const qty = parseInt(row.numItems); + totalItems += qty; + totalOz += parseFloat(row.ozPerItem) * qty; + totalValue += parseFloat(row.valuePerItem) * qty; } }); - totalOz = parseFloat(totalOz.toFixed(2)); - totalValue = parseFloat(totalValue.toFixed(2)); - setTotalItems(totalItems); - setTotalOz(totalOz); - setTotalValue(totalValue); + setTotalOz(parseFloat(totalOz.toFixed(2))); + setTotalValue(parseFloat(totalValue.toFixed(2))); }; const addRow = () => { @@ -86,36 +85,34 @@ const NewDonationFormModal: React.FC = ({ }; const deleteRow = () => { - if (rows.length === 1) { - return; - } + if (rows.length === 1) return; + const newRows = rows.slice(0, -1); setRows(newRows); calculateTotals(newRows); }; const handleSubmit = async () => { - const hasEmptyFields = rows.some( + const hasEmpty = rows.some( (row) => - row.foodItem === '' || - row.foodType === '' || - row.numItems === '' || - row.ozPerItem === '' || - row.valuePerItem === '', + !row.foodItem || + !row.foodType || + !row.numItems || + !row.ozPerItem || + !row.valuePerItem ); - if (hasEmptyFields) { + if (hasEmpty) { alert('Please fill in all fields before submitting.'); return; } onClose(); - const foodManufacturerId = 1; const donation_body = { - foodManufacturerId: foodManufacturerId, - totalItems: totalItems, - totalOz: totalOz, + foodManufacturerId: 1, + totalItems, + totalOz, totalEstimatedValue: totalValue, }; @@ -123,35 +120,20 @@ const NewDonationFormModal: React.FC = ({ const donationResponse = await ApiClient.postDonation(donation_body); const donationId = donationResponse?.donationId; - // Automatically update the page after creating new donation onDonationSuccess(); if (donationId) { - const multipleDonationItems_body: Partial[] = []; - - // Populate the body with all donation items for the donation - rows.forEach(async (row) => { - const donationItem_body: Partial = { - donationId: donationId, - itemName: row.foodItem, - quantity: parseInt(row.numItems), - ozPerItem: parseFloat(row.ozPerItem), - estimatedValue: parseFloat(row.valuePerItem), - foodType: row.foodType, - }; - - multipleDonationItems_body.push(donationItem_body); - }); - - const multipleDonationItemResponse = await ApiClient.postMultipleDonationItems( - multipleDonationItems_body, - ); - - if (multipleDonationItemResponse) { - console.log('Donation items submitted successfully'); - } else { - alert('Failed to submit donation items'); - } + const multipleItems: Partial[] = rows.map((row) => ({ + donationId, + itemName: row.foodItem, + quantity: parseInt(row.numItems), + ozPerItem: parseFloat(row.ozPerItem), + estimatedValue: parseFloat(row.valuePerItem), + foodType: row.foodType, + })); + + await ApiClient.postMultipleDonationItems(multipleItems); + setRows([ { id: 1, @@ -176,127 +158,137 @@ const NewDonationFormModal: React.FC = ({ return ( { + size="xl" + onOpenChange={(e) => { if (!e.open) onClose(); }} closeOnInteractOutside > - - - - - - SSF Log New Donation Form SSF Donation Log Form - - - - - Log a new donation by filling out the form below. Use the add or - delete row buttons to add or remove food items from the donation. - Please make sure to fill out all fields before submitting. - - Log a new donation - - - - - - Total # of items: {totalItems}    Total oz of - items: {totalOz}    Total value of items:{' '} - {totalValue} - - - - - - - - Food Item - Food Type - # of Items - Oz per Item - Value per Item - - - - {rows.map((row) => ( - - - - handleChange(row.id, 'foodItem', e.target.value) - } - /> - - - - + + + + + + + + SSF Log New Donation Form + + + + + + Log a new donation by filling out the form below. + + + + + + + + Total Items: {totalItems}   Total oz: {totalOz}{' '} +   Total Value: {totalValue} + + + + + + + + + Food Item + Food Type + # of Items + Oz per Item + Value per Item + + + + + {rows.map((row) => ( + + + - handleChange(row.id, 'foodType', e.target.value) + handleChange(row.id, 'foodItem', e.target.value) } - > - {FoodTypes.map((type) => ( - - ))} - - - - - - - handleChange(row.id, 'numItems', e.target.value) - } - /> - - - - handleChange(row.id, 'ozPerItem', e.target.value) - } - /> - - - - handleChange(row.id, 'valuePerItem', e.target.value) - } - /> - - - ))} - - - - - - - - - - - + /> + + + + + + handleChange( + row.id, + 'foodType', + e.target.value + ) + } + > + + {FoodTypes.map((type) => ( + + ))} + + + + + + + + handleChange(row.id, 'numItems', e.target.value) + } + /> + + + + + handleChange(row.id, 'ozPerItem', e.target.value) + } + /> + + + + + handleChange( + row.id, + 'valuePerItem', + e.target.value + ) + } + /> + + + ))} + + + + + + + + + + + + ); }; diff --git a/apps/frontend/src/main.tsx b/apps/frontend/src/main.tsx index 10110223..7f571895 100644 --- a/apps/frontend/src/main.tsx +++ b/apps/frontend/src/main.tsx @@ -14,4 +14,4 @@ root.render( , -); \ No newline at end of file +); diff --git a/apps/frontend/src/theme.ts b/apps/frontend/src/theme.ts index c472e41f..5b3eab09 100644 --- a/apps/frontend/src/theme.ts +++ b/apps/frontend/src/theme.ts @@ -53,7 +53,7 @@ const customConfig = defineConfig({ colors: { white: { value: '#fff' }, black: { value: '#000' }, - blue: { + blue: { ssf: { value: '#2B5061' }, 100: { value: '#bee3f8' }, }, @@ -79,4 +79,4 @@ const customConfig = defineConfig({ }, }); -export const system = createSystem(defaultConfig, customConfig); \ No newline at end of file +export const system = createSystem(defaultConfig, customConfig); From 3d95bbd65d1b6d0d23100cdd54f04b3c2e0436a1 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 26 Oct 2025 16:03:41 -0400 Subject: [PATCH 03/19] Added tests for donation items controller only --- .../donationItems.controller.spec.ts | 102 ++++++++++++++++++ package.json | 1 + yarn.lock | 21 +--- 3 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 apps/backend/src/donationItems/donationItems.controller.spec.ts diff --git a/apps/backend/src/donationItems/donationItems.controller.spec.ts b/apps/backend/src/donationItems/donationItems.controller.spec.ts new file mode 100644 index 00000000..c2fbe4df --- /dev/null +++ b/apps/backend/src/donationItems/donationItems.controller.spec.ts @@ -0,0 +1,102 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { DonationItemsController } from './donationItems.controller'; +import { DonationItemsService } from './donationItems.service'; +import { DonationItem } from './donationItems.entity'; +import { mock } from 'jest-mock-extended'; + +const mockDonationItemsService = mock(); + +describe('DonationItemsController', () => { + let controller: DonationItemsController; + + const mockDonationItemsCreateData: Partial[] = [ + { + itemId: 1, + donationId: 1, + itemName: 'Canned Beans', + quantity: 100, + reservedQuantity: 0, + status: 'available', + ozPerItem: 15, + estimatedValue: 200, + foodType: 'legume', + }, + { + itemId: 2, + donationId: 1, + itemName: 'Rice', + quantity: 50, + reservedQuantity: 0, + status: 'available', + ozPerItem: 20, + estimatedValue: 150, + foodType: 'grain', + } + ]; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [DonationItemsController], + providers: [ + { provide: DonationItemsService, useValue: mockDonationItemsService }, + ], + }).compile(); + + controller = module.get(DonationItemsController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('create', () => { + it('should call donationItemsService.create and return a donationItem', async () => { + const donationItemData = mockDonationItemsCreateData[0]; + mockDonationItemsService.create.mockResolvedValue(donationItemData as DonationItem); + const result = await controller.createDonationItem(donationItemData as DonationItem); + expect(result).toEqual(donationItemData as DonationItem); + expect(mockDonationItemsService.create).toHaveBeenCalledWith( + donationItemData.donationId, + donationItemData.itemName, + donationItemData.quantity, + donationItemData.reservedQuantity, + donationItemData.status, + donationItemData.ozPerItem, + donationItemData.estimatedValue, + donationItemData.foodType, + ); + }); + }); + + describe('createMultipleDonationItems', () => { + it('should call donationItemsService.create for each item and return created donationItems', async () => { + mockDonationItemsService.create.mockImplementation(async (donationId: number, itemName: string, quantity: number, reservedQuantity: number, status: string, ozPerItem: number, estimatedValue: number, foodType: string) => { + return mockDonationItemsCreateData.find( + (item) => + item.donationId === donationId && + item.itemName === itemName && + item.quantity === quantity && + item.reservedQuantity === reservedQuantity && + item.status === status && + item.ozPerItem === ozPerItem && + item.estimatedValue === estimatedValue && + item.foodType === foodType, + ) as DonationItem; + }); + const result = await controller.createMultipleDonationItems(mockDonationItemsCreateData as DonationItem[]); + expect(result).toEqual(mockDonationItemsCreateData as DonationItem[]); + for (const donationItemData of mockDonationItemsCreateData) { + expect(mockDonationItemsService.create).toHaveBeenCalledWith( + donationItemData.donationId, + donationItemData.itemName, + donationItemData.quantity, + donationItemData.reservedQuantity, + donationItemData.status, + donationItemData.ozPerItem, + donationItemData.estimatedValue, + donationItemData.foodType, + ); + } + }); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index 6af7be3b..eadf2701 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "dotenv": "^16.4.5", "global": "^4.4.0", "google-libphonenumber": "^3.2.40", + "jest-mock-extended": "^4.0.0", "jwks-rsa": "^3.1.0", "lucide-react": "^0.544.0", "mongodb": "^6.1.0", diff --git a/yarn.lock b/yarn.lock index 1367081b..fa7b60cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9674,22 +9674,6 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock-extended@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-4.0.0.tgz#fe8cfa686c7ada4be2e7f7a3eced794b1338c18b" - integrity sha512-7BZpfuvLam+/HC+NxifIi9b+5VXj/utUDMPUqrDJehGWVuXPtLS9Jqlob2mJLrI/pg2k1S8DMfKDvEB88QNjaQ== - dependencies: - ts-essentials "^10.0.2" - -jest-mock@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" - integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== - dependencies: - "@jest/types" "30.2.0" - "@types/node" "*" - jest-util "30.2.0" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -13412,6 +13396,11 @@ ts-essentials@^10.0.2: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-10.1.1.tgz#4e1d29b7c9b33c1a2744482376634c4fafba5210" integrity sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw== +ts-essentials@^10.0.2: + version "10.1.1" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-10.1.1.tgz#4e1d29b7c9b33c1a2744482376634c4fafba5210" + integrity sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw== + ts-jest@^29.1.0: version "29.4.5" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.5.tgz#a6b0dc401e521515d5342234be87f1ca96390a6f" From 706b59f0f9affad4638ae9d6c70fea64a3ab673c Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 26 Oct 2025 16:20:05 -0400 Subject: [PATCH 04/19] Final commit --- .../donationItems.controller.spec.ts | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/apps/backend/src/donationItems/donationItems.controller.spec.ts b/apps/backend/src/donationItems/donationItems.controller.spec.ts index c2fbe4df..eecb9559 100644 --- a/apps/backend/src/donationItems/donationItems.controller.spec.ts +++ b/apps/backend/src/donationItems/donationItems.controller.spec.ts @@ -31,7 +31,7 @@ describe('DonationItemsController', () => { ozPerItem: 20, estimatedValue: 150, foodType: 'grain', - } + }, ]; beforeEach(async () => { @@ -52,8 +52,12 @@ describe('DonationItemsController', () => { describe('create', () => { it('should call donationItemsService.create and return a donationItem', async () => { const donationItemData = mockDonationItemsCreateData[0]; - mockDonationItemsService.create.mockResolvedValue(donationItemData as DonationItem); - const result = await controller.createDonationItem(donationItemData as DonationItem); + mockDonationItemsService.create.mockResolvedValue( + donationItemData as DonationItem, + ); + const result = await controller.createDonationItem( + donationItemData as DonationItem, + ); expect(result).toEqual(donationItemData as DonationItem); expect(mockDonationItemsService.create).toHaveBeenCalledWith( donationItemData.donationId, @@ -70,20 +74,33 @@ describe('DonationItemsController', () => { describe('createMultipleDonationItems', () => { it('should call donationItemsService.create for each item and return created donationItems', async () => { - mockDonationItemsService.create.mockImplementation(async (donationId: number, itemName: string, quantity: number, reservedQuantity: number, status: string, ozPerItem: number, estimatedValue: number, foodType: string) => { - return mockDonationItemsCreateData.find( - (item) => - item.donationId === donationId && - item.itemName === itemName && - item.quantity === quantity && - item.reservedQuantity === reservedQuantity && - item.status === status && - item.ozPerItem === ozPerItem && - item.estimatedValue === estimatedValue && - item.foodType === foodType, - ) as DonationItem; - }); - const result = await controller.createMultipleDonationItems(mockDonationItemsCreateData as DonationItem[]); + mockDonationItemsService.create.mockImplementation( + async ( + donationId: number, + itemName: string, + quantity: number, + reservedQuantity: number, + status: string, + ozPerItem: number, + estimatedValue: number, + foodType: string, + ) => { + return mockDonationItemsCreateData.find( + (item) => + item.donationId === donationId && + item.itemName === itemName && + item.quantity === quantity && + item.reservedQuantity === reservedQuantity && + item.status === status && + item.ozPerItem === ozPerItem && + item.estimatedValue === estimatedValue && + item.foodType === foodType, + ) as DonationItem; + }, + ); + const result = await controller.createMultipleDonationItems( + mockDonationItemsCreateData as DonationItem[], + ); expect(result).toEqual(mockDonationItemsCreateData as DonationItem[]); for (const donationItemData of mockDonationItemsCreateData) { expect(mockDonationItemsService.create).toHaveBeenCalledWith( @@ -99,4 +116,4 @@ describe('DonationItemsController', () => { } }); }); -}); \ No newline at end of file +}); From 9c478218853b0a083a7b2535892f6f2aff4e8f52 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sat, 22 Nov 2025 00:11:02 -0500 Subject: [PATCH 05/19] Final commit --- apps/frontend/src/theme.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/theme.ts b/apps/frontend/src/theme.ts index 5b3eab09..7945a3cd 100644 --- a/apps/frontend/src/theme.ts +++ b/apps/frontend/src/theme.ts @@ -1,4 +1,9 @@ -import { createSystem, defaultConfig, defineConfig, defineTextStyles } from '@chakra-ui/react'; +import { + createSystem, + defaultConfig, + defineConfig, + defineTextStyles, +} from '@chakra-ui/react'; const textStyles = defineTextStyles({ body: { @@ -10,7 +15,7 @@ const textStyles = defineTextStyles({ value: { fontFamily: 'instrument', fontSize: '32px', - fontWeight: '400' + fontWeight: '400', }, }, h2: { From ea1d063cca4120c70e672d72a4a6d72830059785 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Wed, 26 Nov 2025 20:46:03 -0500 Subject: [PATCH 06/19] Resolved merge conflicts --- apps/backend/src/donationItems/donationItems.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/donationItems/donationItems.controller.ts b/apps/backend/src/donationItems/donationItems.controller.ts index dcf88a76..2deada65 100644 --- a/apps/backend/src/donationItems/donationItems.controller.ts +++ b/apps/backend/src/donationItems/donationItems.controller.ts @@ -104,7 +104,7 @@ export class DonationItemsController { status: string; ozPerItem: number; estimatedValue: number; - foodType: string; + foodType: FoodType; }[], ): Promise { const createdDonationItems: DonationItem[] = []; From ec957b78398d98f8a631b74588866500e586d12a Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Wed, 26 Nov 2025 20:46:24 -0500 Subject: [PATCH 07/19] Final commit --- .../donationItems/donationItems.controller.ts | 9 ++++--- .../src/donations/donations.controller.ts | 4 +-- apps/backend/src/donations/types.ts | 2 +- .../src/foodRequests/request.controller.ts | 9 ++++--- ...1763963056712-AllergyFriendlyToBoolType.ts | 20 +++++++------- apps/backend/src/orders/order.service.spec.ts | 8 +++++- apps/backend/src/pantries/types.ts | 2 +- .../forms/pantryApplicationForm.tsx | 27 +++++++++++++------ .../src/components/forms/requestFormModal.tsx | 4 +-- apps/frontend/src/types/pantryEnums.ts | 4 +-- apps/frontend/src/types/types.ts | 11 ++++---- 11 files changed, 60 insertions(+), 40 deletions(-) diff --git a/apps/backend/src/donationItems/donationItems.controller.ts b/apps/backend/src/donationItems/donationItems.controller.ts index 2deada65..a710fa6c 100644 --- a/apps/backend/src/donationItems/donationItems.controller.ts +++ b/apps/backend/src/donationItems/donationItems.controller.ts @@ -38,8 +38,8 @@ export class DonationItemsController { status: { type: 'string', example: 'available' }, ozPerItem: { type: 'integer', example: 5 }, estimatedValue: { type: 'integer', example: 100 }, - foodType: { - type: 'string', + foodType: { + type: 'string', enum: Object.values(FoodType), example: FoodType.DAIRY_FREE_ALTERNATIVES, }, @@ -59,7 +59,10 @@ export class DonationItemsController { foodType: FoodType; }, ): Promise { - if (body.foodType && !Object.values(FoodType).includes(body.foodType as FoodType)) { + if ( + body.foodType && + !Object.values(FoodType).includes(body.foodType as FoodType) + ) { throw new BadRequestException('Invalid foodtype'); } return this.donationItemsService.create( diff --git a/apps/backend/src/donations/donations.controller.ts b/apps/backend/src/donations/donations.controller.ts index d748df66..6bcd2a7e 100644 --- a/apps/backend/src/donations/donations.controller.ts +++ b/apps/backend/src/donations/donations.controller.ts @@ -46,8 +46,8 @@ export class DonationsController { type: 'string', format: 'date-time', }, - status: { - type: 'string', + status: { + type: 'string', enum: Object.values(DonationStatus), example: DonationStatus.AVAILABLE, }, diff --git a/apps/backend/src/donations/types.ts b/apps/backend/src/donations/types.ts index 549ee6c6..16387987 100644 --- a/apps/backend/src/donations/types.ts +++ b/apps/backend/src/donations/types.ts @@ -2,4 +2,4 @@ export enum DonationStatus { AVAILABLE = 'available', FULFILLED = 'fulfilled', MATCHING = 'matching', -} \ No newline at end of file +} diff --git a/apps/backend/src/foodRequests/request.controller.ts b/apps/backend/src/foodRequests/request.controller.ts index c01eda5b..e3a93727 100644 --- a/apps/backend/src/foodRequests/request.controller.ts +++ b/apps/backend/src/foodRequests/request.controller.ts @@ -58,8 +58,8 @@ export class FoodRequestsController { type: 'object', properties: { pantryId: { type: 'integer', example: 1 }, - requestedSize: { - type: 'string', + requestedSize: { + type: 'string', enum: Object.values(RequestSize), example: RequestSize.LARGE, }, @@ -166,7 +166,10 @@ export class FoodRequestsController { ); const request = await this.requestsService.findOne(requestId); - await this.ordersService.updateStatus(request.order.orderId, OrderStatus.DELIVERED); + await this.ordersService.updateStatus( + request.order.orderId, + OrderStatus.DELIVERED, + ); return this.requestsService.updateDeliveryDetails( requestId, diff --git a/apps/backend/src/migrations/1763963056712-AllergyFriendlyToBoolType.ts b/apps/backend/src/migrations/1763963056712-AllergyFriendlyToBoolType.ts index 079362d6..3a14c4d4 100644 --- a/apps/backend/src/migrations/1763963056712-AllergyFriendlyToBoolType.ts +++ b/apps/backend/src/migrations/1763963056712-AllergyFriendlyToBoolType.ts @@ -1,21 +1,21 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; +import { MigrationInterface, QueryRunner } from 'typeorm'; -export class AllergyFriendlyToBoolType1763963056712 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` +export class AllergyFriendlyToBoolType1763963056712 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` ALTER TABLE pantries ALTER COLUMN dedicated_allergy_friendly TYPE BOOLEAN USING (FALSE), ALTER COLUMN dedicated_allergy_friendly SET NOT NULL; `); - } + } - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` ALTER TABLE pantries ALTER COLUMN dedicated_allergy_friendly TYPE VARCHAR(255), ALTER COLUMN dedicated_allergy_friendly DROP NOT NULL; `); - } - + } } diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index 4c64ac10..b65e335b 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -6,7 +6,13 @@ import { OrdersService } from './order.service'; import { mock } from 'jest-mock-extended'; import { Pantry } from '../pantries/pantries.entity'; import { User } from '../users/user.entity'; -import { AllergensConfidence, ClientVisitFrequency, PantryStatus, RefrigeratedDonation, ServeAllergicChildren } from '../pantries/types'; +import { + AllergensConfidence, + ClientVisitFrequency, + PantryStatus, + RefrigeratedDonation, + ServeAllergicChildren, +} from '../pantries/types'; import { OrderStatus } from './types'; const mockOrdersRepository = mock>(); diff --git a/apps/backend/src/pantries/types.ts b/apps/backend/src/pantries/types.ts index f776991b..cdf8b671 100644 --- a/apps/backend/src/pantries/types.ts +++ b/apps/backend/src/pantries/types.ts @@ -33,7 +33,7 @@ export enum PantryStatus { export enum Activity { CREATE_LABELED_SHELF = 'Create labeled shelf', PROVIDE_EDUCATIONAL_PAMPHLETS = 'Provide educational pamphlets', - TRACK_DIETARY_NEEDS ='Spreadsheet to track dietary needs', + TRACK_DIETARY_NEEDS = 'Spreadsheet to track dietary needs', POST_RESOURCE_FLYERS = 'Post allergen-free resource flyers', SURVEY_CLIENTS = 'Survey clients to determine medical dietary needs', COLLECT_FEEDBACK = 'Collect feedback from allergen-avoidant clients', diff --git a/apps/frontend/src/components/forms/pantryApplicationForm.tsx b/apps/frontend/src/components/forms/pantryApplicationForm.tsx index 4c7325bf..70f49e97 100644 --- a/apps/frontend/src/components/forms/pantryApplicationForm.tsx +++ b/apps/frontend/src/components/forms/pantryApplicationForm.tsx @@ -515,12 +515,18 @@ export const submitPantryApplicationForm: ActionFunction = async ({ const pantryApplicationData = new Map(); const ActivityStorageMap: Record = { - 'Create a labeled, allergy-friendly shelf or shelves': Activity.CREATE_LABELED_SHELF, - 'Provide clients and staff/volunteers with educational pamphlets': Activity.PROVIDE_EDUCATIONAL_PAMPHLETS, - "Use a spreadsheet to track clients' medical dietary needs and distribution of SSF items per month": Activity.TRACK_DIETARY_NEEDS, - 'Post allergen-free resource flyers throughout pantry': Activity.POST_RESOURCE_FLYERS, - 'Survey your clients to determine their medical dietary needs': Activity.SURVEY_CLIENTS, - 'Collect feedback from allergen-avoidant clients on SSF foods': Activity.COLLECT_FEEDBACK, + 'Create a labeled, allergy-friendly shelf or shelves': + Activity.CREATE_LABELED_SHELF, + 'Provide clients and staff/volunteers with educational pamphlets': + Activity.PROVIDE_EDUCATIONAL_PAMPHLETS, + "Use a spreadsheet to track clients' medical dietary needs and distribution of SSF items per month": + Activity.TRACK_DIETARY_NEEDS, + 'Post allergen-free resource flyers throughout pantry': + Activity.POST_RESOURCE_FLYERS, + 'Survey your clients to determine their medical dietary needs': + Activity.SURVEY_CLIENTS, + 'Collect feedback from allergen-avoidant clients on SSF foods': + Activity.COLLECT_FEEDBACK, 'Something else': Activity.SOMETHING_ELSE, }; @@ -538,11 +544,16 @@ export const submitPantryApplicationForm: ActionFunction = async ({ form.delete('restrictions'); const selectedActivities = form.getAll('activities') as string[]; - const convertedActivities = selectedActivities.map((activity) => ActivityStorageMap[activity]); + const convertedActivities = selectedActivities.map( + (activity) => ActivityStorageMap[activity], + ); pantryApplicationData.set('activities', convertedActivities); form.delete('activities'); - pantryApplicationData.set('dedicatedAllergyFriendly', form.get('dedicatedAllergyFriendly')); + pantryApplicationData.set( + 'dedicatedAllergyFriendly', + form.get('dedicatedAllergyFriendly'), + ); form.delete('dedicatedAllergyFriendly'); // Handle all other questions diff --git a/apps/frontend/src/components/forms/requestFormModal.tsx b/apps/frontend/src/components/forms/requestFormModal.tsx index cb12dc51..9861327a 100644 --- a/apps/frontend/src/components/forms/requestFormModal.tsx +++ b/apps/frontend/src/components/forms/requestFormModal.tsx @@ -132,9 +132,7 @@ const FoodRequestFormModal: React.FC = ({ - - {option} - + {option} ))} diff --git a/apps/frontend/src/types/pantryEnums.ts b/apps/frontend/src/types/pantryEnums.ts index e5f13a6f..cdf8b671 100644 --- a/apps/frontend/src/types/pantryEnums.ts +++ b/apps/frontend/src/types/pantryEnums.ts @@ -33,7 +33,7 @@ export enum PantryStatus { export enum Activity { CREATE_LABELED_SHELF = 'Create labeled shelf', PROVIDE_EDUCATIONAL_PAMPHLETS = 'Provide educational pamphlets', - TRACK_DIETARY_NEEDS ='Spreadsheet to track dietary needs', + TRACK_DIETARY_NEEDS = 'Spreadsheet to track dietary needs', POST_RESOURCE_FLYERS = 'Post allergen-free resource flyers', SURVEY_CLIENTS = 'Survey clients to determine medical dietary needs', COLLECT_FEEDBACK = 'Collect feedback from allergen-avoidant clients', @@ -44,4 +44,4 @@ export enum ReserveFoodForAllergic { YES = 'Yes', SOME = 'Some', NO = 'No', -} \ No newline at end of file +} diff --git a/apps/frontend/src/types/types.ts b/apps/frontend/src/types/types.ts index cb901272..619bdf4f 100644 --- a/apps/frontend/src/types/types.ts +++ b/apps/frontend/src/types/types.ts @@ -1,12 +1,12 @@ -import { - RefrigeratedDonation, - ReserveFoodForAllergic, - ClientVisitFrequency, +import { + RefrigeratedDonation, + ReserveFoodForAllergic, + ClientVisitFrequency, ServeAllergicChildren, AllergensConfidence, PantryStatus, Activity, -} from "./pantryEnums"; +} from './pantryEnums'; // Note: The API calls as currently written do not // return a pantry's SSF representative or pantry @@ -223,4 +223,3 @@ export enum DonationStatus { FULFILLED = 'fulfilled', MATCHING = 'matching', } - From 20b9bbeb30773753827ace9a79856aca9d7475b5 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sat, 29 Nov 2025 22:03:15 -0500 Subject: [PATCH 08/19] Prettier --- .../src/components/forms/donationDetailsModal.tsx | 2 +- .../src/components/forms/newDonationFormModal.tsx | 12 ++++-------- apps/frontend/src/theme.ts | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/frontend/src/components/forms/donationDetailsModal.tsx b/apps/frontend/src/components/forms/donationDetailsModal.tsx index 9837c424..a7533e18 100644 --- a/apps/frontend/src/components/forms/donationDetailsModal.tsx +++ b/apps/frontend/src/components/forms/donationDetailsModal.tsx @@ -34,7 +34,7 @@ const DonationDetailsModal: React.FC = ({ try { const donationData = await ApiClient.getOrderDonation(donationId); const itemsData = await ApiClient.getDonationItemsByDonationId( - donationId + donationId, ); setLoadedDonation(donationData); diff --git a/apps/frontend/src/components/forms/newDonationFormModal.tsx b/apps/frontend/src/components/forms/newDonationFormModal.tsx index 340ff33b..64ce10ca 100644 --- a/apps/frontend/src/components/forms/newDonationFormModal.tsx +++ b/apps/frontend/src/components/forms/newDonationFormModal.tsx @@ -44,7 +44,7 @@ const NewDonationFormModal: React.FC = ({ const handleChange = (id: number, field: string, value: string) => { const updatedRows = rows.map((row) => - row.id === id ? { ...row, [field]: value } : row + row.id === id ? { ...row, [field]: value } : row, ); setRows(updatedRows); @@ -99,7 +99,7 @@ const NewDonationFormModal: React.FC = ({ !row.foodType || !row.numItems || !row.ozPerItem || - !row.valuePerItem + !row.valuePerItem, ); if (hasEmpty) { @@ -221,11 +221,7 @@ const NewDonationFormModal: React.FC = ({ - handleChange( - row.id, - 'foodType', - e.target.value - ) + handleChange(row.id, 'foodType', e.target.value) } > @@ -270,7 +266,7 @@ const NewDonationFormModal: React.FC = ({ handleChange( row.id, 'valuePerItem', - e.target.value + e.target.value, ) } /> diff --git a/apps/frontend/src/theme.ts b/apps/frontend/src/theme.ts index 7945a3cd..c504bb84 100644 --- a/apps/frontend/src/theme.ts +++ b/apps/frontend/src/theme.ts @@ -84,4 +84,4 @@ const customConfig = defineConfig({ }, }); -export const system = createSystem(defaultConfig, customConfig); +export const system = createSystem(defaultConfig, customConfig); From 98b5e25cb671402c15bdf54eef503cbb62e33452 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sat, 6 Dec 2025 13:12:11 -0500 Subject: [PATCH 09/19] prettier --- .../pantries/dtos/pantry-application.dto.ts | 2 +- .../forms/pantryApplicationForm.tsx | 658 ++++++++++-------- .../src/components/forms/usPhoneInput.tsx | 5 +- apps/frontend/src/theme.ts | 6 +- 4 files changed, 394 insertions(+), 277 deletions(-) diff --git a/apps/backend/src/pantries/dtos/pantry-application.dto.ts b/apps/backend/src/pantries/dtos/pantry-application.dto.ts index 42510915..c7473b0f 100644 --- a/apps/backend/src/pantries/dtos/pantry-application.dto.ts +++ b/apps/backend/src/pantries/dtos/pantry-application.dto.ts @@ -52,7 +52,7 @@ export class PantryApplicationDto { @IsNotEmpty() @MaxLength(255) emailContactOther?: string; - + @IsOptional() @IsString() @IsNotEmpty() diff --git a/apps/frontend/src/components/forms/pantryApplicationForm.tsx b/apps/frontend/src/components/forms/pantryApplicationForm.tsx index 48b693e0..df185caa 100644 --- a/apps/frontend/src/components/forms/pantryApplicationForm.tsx +++ b/apps/frontend/src/components/forms/pantryApplicationForm.tsx @@ -67,18 +67,22 @@ const activityOptions = [ const PantryApplicationForm: React.FC = () => { const [contactPhone, setContactPhone] = useState(''); - const [secondaryContactPhone, setSecondaryContactPhone] = useState(''); + const [secondaryContactPhone, setSecondaryContactPhone] = + useState(''); const [activities, setActivities] = useState([]); const allergenClientsExactOption: string = 'I have an exact number'; const [allergenClients, setAllergenClients] = useState(); const [restrictions, setRestrictions] = useState([]); - const [reserveFoodForAllergic, setReserveFoodForAllergic] = useState(); - const [differentMailingAddress, setDifferentMailingAddress] = useState(); + const [reserveFoodForAllergic, setReserveFoodForAllergic] = + useState(); + const [differentMailingAddress, setDifferentMailingAddress] = useState< + boolean | null + >(); const [otherEmailContact, setOtherEmailContact] = useState(false); const sectionTitleStyles = { - fontFamily: "inter", + fontFamily: 'inter', fontWeight: '600', fontSize: 'md', color: 'gray.dark', @@ -86,16 +90,16 @@ const PantryApplicationForm: React.FC = () => { }; const sectionSubtitleStyles = { - fontFamily: "inter", + fontFamily: 'inter', fontWeight: '400', color: 'gray.light', mb: '2.25em', fontSize: 'sm', - } + }; const fieldHeaderStyles = { color: 'neutral.800', - fontFamily: "inter", + fontFamily: 'inter', fontSize: 'sm', fontWeight: '600', }; @@ -103,96 +107,121 @@ const PantryApplicationForm: React.FC = () => { return ( - + Partner Pantry Application - Thank you for your interest in partnering with Securing Safe Food (SSF) to help - serve clients with food allergies and other adverse reactions to foods. + Thank you for your interest in partnering with Securing Safe Food + (SSF) to help serve clients with food allergies and other adverse + reactions to foods. -
- + Pantry Application Form - + - This application helps us understand your pantry’s capacity and interest in - distributing allergen-friendly food. We’ll ask about your pantry’s current - practices, storage capabilities, and communication preferences. + This application helps us understand your pantry’s capacity and + interest in distributing allergen-friendly food. We’ll ask about + your pantry’s current practices, storage capabilities, and + communication preferences. - Please answer as accurately as possible. If you have any questions or need help, - don’t hesitate to contact the SSF team. + Please answer as accurately as possible. If you have any questions + or need help, don’t hesitate to contact the SSF team. - - - Primary Contact Information - + + Primary Contact Information First Name - + - + Last Name - + - + Phone Number - + Email Address - + - + - Is there someone at your pantry who can regularly check and respond to emails from SSF as needed?{' '} - + Is there someone at your pantry who can regularly check and + respond to emails from SSF as needed?{' '} + - setOtherEmailContact(e.value === 'Other')} + onValueChange={(e: { value: string }) => + setOtherEmailContact(e.value === 'Other') + } > {['Yes', 'No', 'Other'].map((value) => ( - - - + + - + {value} @@ -201,127 +230,151 @@ const PantryApplicationForm: React.FC = () => { - - + - - Secondary Contact Information - + Secondary Contact Information - - First Name - - + First Name + - - Last Name - - + Last Name + - - Phone Number - + Phone Number - - Email Address - - + Email Address + - - + + Food Shipment Address Please list your address for food shipments. - + Address Line 1 - + - + - - Address Line 2 - - + Address Line 2 + City/Town - + - + State/Region/Province - + - + Zip/Post Code - + - + - - Country - - + Country + - Does this address differ from your pantry's mailing address for documents?{' '} - + Does this address differ from your pantry's mailing address for + documents? - setDifferentMailingAddress(e.value === 'Yes')} + onValueChange={(e: { value: string }) => + setDifferentMailingAddress(e.value === 'Yes') + } name="differentMailingAddress" > {['Yes', 'No'].map((value) => ( - - - + + - + {value} @@ -331,28 +384,22 @@ const PantryApplicationForm: React.FC = () => { - Would your pantry be able to accept food deliveries - during standard business hours Mon-Fri?{' '} - + Would your pantry be able to accept food deliveries during + standard business hours Mon-Fri?{' '} + - + {['Yes', 'No'].map((value) => ( - - + - + {value} @@ -364,10 +411,13 @@ const PantryApplicationForm: React.FC = () => { Please note any delivery window restrictions. -