From 265bad670d3217c95ed2439d0c395762c67e3c1e Mon Sep 17 00:00:00 2001 From: skewalia <145241312+swarkewalia@users.noreply.github.com> Date: Sat, 17 Jan 2026 23:38:32 -0500 Subject: [PATCH] merge fix --- .gitignore | 1 - apps/backend/src/auth/auth.controller.ts | 2 +- apps/backend/src/auth/auth.service.ts | 2 +- apps/backend/src/config/typeorm.ts | 2 - .../foodRequests/request.controller.spec.ts | 322 - .../src/foodRequests/request.controller.ts | 10 +- .../src/foodRequests/request.module.ts | 7 +- .../src/foodRequests/request.service.spec.ts | 357 - .../src/foodRequests/request.service.ts | 9 - ...4811878152-RemoveMultipleVolunteerTypes.ts | 74 - .../1764816885341-RemoveUnusedStatuses.ts | 27 +- .../pantries/dtos/pantry-application.dto.ts | 2 +- .../src/pantries/pantries.controller.ts | 7 + apps/backend/src/pantries/pantries.service.ts | 39 + apps/backend/src/pantries/types.ts | 31 + apps/backend/src/users/types.ts | 10 +- apps/backend/src/users/user.entity.ts | 2 +- .../src/users/users.controller.spec.ts | 55 +- apps/backend/src/users/users.service.spec.ts | 8 +- apps/backend/src/users/users.service.ts | 6 +- .../src/utils/validation.utils.spec.ts | 14 - apps/frontend/src/api/apiClient.ts | 11 +- .../components/forms/addNewVolunteerModal.tsx | 159 - .../forms/pantryApplicationForm.tsx | 680 +- .../src/components/forms/usPhoneInput.tsx | 5 +- .../src/containers/volunteerManagement.tsx | 301 +- apps/frontend/src/main.tsx | 2 +- apps/frontend/src/theme.ts | 19 +- apps/frontend/src/types/types.ts | 16 +- package.json | 37 +- yarn.lock | 5926 +++++++++-------- 31 files changed, 3632 insertions(+), 4511 deletions(-) delete mode 100644 apps/backend/src/foodRequests/request.controller.spec.ts delete mode 100644 apps/backend/src/foodRequests/request.service.spec.ts delete mode 100644 apps/backend/src/migrations/1764811878152-RemoveMultipleVolunteerTypes.ts delete mode 100644 apps/backend/src/utils/validation.utils.spec.ts delete mode 100644 apps/frontend/src/components/forms/addNewVolunteerModal.tsx diff --git a/.gitignore b/.gitignore index 0d46e7cf..0e2cd373 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ npm-debug.log yarn-error.log testem.log /typings -.nx # System Files .DS_Store diff --git a/apps/backend/src/auth/auth.controller.ts b/apps/backend/src/auth/auth.controller.ts index ec741008..7f603f00 100644 --- a/apps/backend/src/auth/auth.controller.ts +++ b/apps/backend/src/auth/auth.controller.ts @@ -34,7 +34,7 @@ export class AuthController { signUpDto.firstName, signUpDto.lastName, signUpDto.phone, - Role.VOLUNTEER, + Role.STANDARD_VOLUNTEER, ); return user; diff --git a/apps/backend/src/auth/auth.service.ts b/apps/backend/src/auth/auth.service.ts index a0bae3ad..2d56324c 100644 --- a/apps/backend/src/auth/auth.service.ts +++ b/apps/backend/src/auth/auth.service.ts @@ -60,7 +60,7 @@ export class AuthService { async signup( { firstName, lastName, email, password }: SignUpDto, - role: Role = Role.VOLUNTEER, + role: Role = Role.STANDARD_VOLUNTEER, ): Promise { // Needs error handling const signUpCommand = new SignUpCommand({ diff --git a/apps/backend/src/config/typeorm.ts b/apps/backend/src/config/typeorm.ts index e1a76d30..80f6bf29 100644 --- a/apps/backend/src/config/typeorm.ts +++ b/apps/backend/src/config/typeorm.ts @@ -23,7 +23,6 @@ import { RemoveOrdersDonationId1761500262238 } from '../migrations/1761500262238 import { AddVolunteerPantryUniqueConstraint1760033134668 } from '../migrations/1760033134668-AddVolunteerPantryUniqueConstraint'; import { AllergyFriendlyToBoolType1763963056712 } from '../migrations/1763963056712-AllergyFriendlyToBoolType'; import { UpdatePantryUserFieldsFixed1764350314832 } from '../migrations/1764350314832-UpdatePantryUserFieldsFixed'; -import { RemoveMultipleVolunteerTypes1764811878152 } from '../migrations/1764811878152-RemoveMultipleVolunteerTypes'; import { RemoveUnusedStatuses1764816885341 } from '../migrations/1764816885341-RemoveUnusedStatuses'; import { UpdatePantryFields1763762628431 } from '../migrations/1763762628431-UpdatePantryFields'; @@ -63,7 +62,6 @@ const config = { AddVolunteerPantryUniqueConstraint1760033134668, AllergyFriendlyToBoolType1763963056712, UpdatePantryUserFieldsFixed1764350314832, - RemoveMultipleVolunteerTypes1764811878152, RemoveUnusedStatuses1764816885341, ], }; diff --git a/apps/backend/src/foodRequests/request.controller.spec.ts b/apps/backend/src/foodRequests/request.controller.spec.ts deleted file mode 100644 index 71c35ca4..00000000 --- a/apps/backend/src/foodRequests/request.controller.spec.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { RequestsService } from './request.service'; -import { RequestsController } from './request.controller'; -import { Test, TestingModule } from '@nestjs/testing'; -import { mock } from 'jest-mock-extended'; -import { AWSS3Service } from '../aws/aws-s3.service'; -import { OrdersService } from '../orders/order.service'; -import { Readable } from 'stream'; -import { FoodRequest } from './request.entity'; -import { RequestSize } from './types'; -import { OrderStatus } from '../orders/types'; - -const mockRequestsService = mock(); -const mockOrdersService = mock(); -const mockAWSS3Service = mock(); - -const foodRequest: Partial = { - requestId: 1, - pantryId: 1, -}; - -describe('RequestsController', () => { - let controller: RequestsController; - - beforeEach(async () => { - mockRequestsService.findOne.mockReset(); - mockRequestsService.find.mockReset(); - mockRequestsService.create.mockReset(); - mockRequestsService.updateDeliveryDetails?.mockReset(); - mockAWSS3Service.upload.mockReset(); - mockOrdersService.updateStatus.mockReset(); - - const module: TestingModule = await Test.createTestingModule({ - controllers: [RequestsController], - providers: [ - { - provide: RequestsService, - useValue: mockRequestsService, - }, - { - provide: OrdersService, - useValue: mockOrdersService, - }, - { - provide: AWSS3Service, - useValue: mockAWSS3Service, - }, - ], - }).compile(); - - controller = module.get(RequestsController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); - - describe('GET /:requestId', () => { - it('should call requestsService.findOne and return a specific food request', async () => { - const requestId = 1; - - mockRequestsService.findOne.mockResolvedValueOnce( - foodRequest as FoodRequest, - ); - - const result = await controller.getRequest(requestId); - - expect(result).toEqual(foodRequest); - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); - }); - }); - - describe('GET /get-all-requests/:pantryId', () => { - it('should call requestsService.find and return all food requests for a specific pantry', async () => { - const foodRequests: Partial[] = [ - foodRequest, - { - requestId: 2, - pantryId: 1, - }, - ]; - const pantryId = 1; - - mockRequestsService.find.mockResolvedValueOnce( - foodRequests as FoodRequest[], - ); - - const result = await controller.getAllPantryRequests(pantryId); - - expect(result).toEqual(foodRequests); - expect(mockRequestsService.find).toHaveBeenCalledWith(pantryId); - }); - }); - - describe('POST /create', () => { - it('should call requestsService.create and return the created food request', async () => { - const createBody: Partial = { - pantryId: 1, - requestedSize: RequestSize.MEDIUM, - requestedItems: ['Test item 1', 'Test item 2'], - additionalInformation: 'Test information.', - dateReceived: null, - feedback: null, - photos: null, - }; - - const createdRequest: Partial = { - requestId: 1, - ...createBody, - requestedAt: new Date(), - order: null, - }; - - mockRequestsService.create.mockResolvedValueOnce( - createdRequest as FoodRequest, - ); - - const result = await controller.createRequest(createBody as FoodRequest); - - expect(result).toEqual(createdRequest); - expect(mockRequestsService.create).toHaveBeenCalledWith( - createBody.pantryId, - createBody.requestedSize, - createBody.requestedItems, - createBody.additionalInformation, - createBody.dateReceived, - createBody.feedback, - createBody.photos, - ); - }); - }); - - describe('POST /:requestId/confirm-delivery', () => { - it('should upload photos, update the order, then update the request', async () => { - const requestId = 1; - - const body = { - dateReceived: new Date().toISOString(), - feedback: 'Nice delivery!', - }; - - // Mock Photos - const mockStream = new Readable(); - mockStream._read = () => {}; - - const photos: Express.Multer.File[] = [ - { - fieldname: 'photos', - originalname: 'photo1.jpg', - encoding: '7bit', - mimetype: 'image/jpeg', - buffer: Buffer.from('image1'), - size: 1000, - destination: '', - filename: '', - path: '', - stream: mockStream, - }, - { - fieldname: 'photos', - originalname: 'photo2.jpg', - encoding: '7bit', - mimetype: 'image/jpeg', - buffer: Buffer.from('image2'), - size: 2000, - destination: '', - filename: '', - path: '', - stream: mockStream, - }, - ]; - - const uploadedUrls = [ - 'https://fake-s3/photo1.jpg', - 'https://fake-s3/photo2.jpg', - ]; - - // Mock AWS upload - mockAWSS3Service.upload.mockResolvedValue(uploadedUrls); - - // Mock RequestsService.findOne - mockRequestsService.findOne.mockResolvedValue({ - requestId, - pantryId: 1, - order: { orderId: 99 }, - } as FoodRequest); - - mockOrdersService.updateStatus.mockResolvedValue(); - - const updatedRequest: Partial = { - requestId, - pantryId: 1, - dateReceived: new Date(body.dateReceived), - feedback: body.feedback, - photos: uploadedUrls, - }; - - mockRequestsService.updateDeliveryDetails.mockResolvedValue( - updatedRequest as FoodRequest, - ); - - const result = await controller.confirmDelivery(requestId, body, photos); - - expect(mockAWSS3Service.upload).toHaveBeenCalledWith(photos); - - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); - - expect(mockOrdersService.updateStatus).toHaveBeenCalledWith( - 99, - OrderStatus.DELIVERED, - ); - - expect(mockRequestsService.updateDeliveryDetails).toHaveBeenCalledWith( - requestId, - new Date(body.dateReceived), - body.feedback, - uploadedUrls, - ); - - expect(result).toEqual(updatedRequest); - }); - - it('should handle no photos being uploaded', async () => { - const requestId = 1; - - const body = { - dateReceived: new Date().toISOString(), - feedback: 'No photos delivery!', - }; - - mockRequestsService.findOne.mockResolvedValue({ - requestId, - pantryId: 1, - order: { orderId: 100 }, - } as FoodRequest); - - mockOrdersService.updateStatus.mockResolvedValue(); - - const updatedRequest: Partial = { - requestId, - pantryId: 1, - dateReceived: new Date(body.dateReceived), - feedback: body.feedback, - photos: [], - }; - - mockRequestsService.updateDeliveryDetails.mockResolvedValue( - updatedRequest as FoodRequest, - ); - - const result = await controller.confirmDelivery(requestId, body); - - expect(mockAWSS3Service.upload).not.toHaveBeenCalled(); - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); - expect(mockOrdersService.updateStatus).toHaveBeenCalledWith( - 100, - OrderStatus.DELIVERED, - ); - expect(mockRequestsService.updateDeliveryDetails).toHaveBeenCalledWith( - requestId, - new Date(body.dateReceived), - body.feedback, - [], - ); - expect(result).toEqual(updatedRequest); - }); - - it('should handle empty photos array', async () => { - const requestId = 1; - - const body = { - dateReceived: new Date().toISOString(), - feedback: 'Empty photos array delivery!', - }; - - mockRequestsService.findOne.mockResolvedValue({ - requestId, - pantryId: 1, - order: { orderId: 101 }, - } as FoodRequest); - - mockOrdersService.updateStatus.mockResolvedValue(); - - const updatedRequest: Partial = { - requestId, - pantryId: 1, - dateReceived: new Date(body.dateReceived), - feedback: body.feedback, - photos: [], - }; - - mockRequestsService.updateDeliveryDetails.mockResolvedValue( - updatedRequest as FoodRequest, - ); - - const result = await controller.confirmDelivery(requestId, body, []); - - expect(mockAWSS3Service.upload).not.toHaveBeenCalled(); - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); - expect(mockOrdersService.updateStatus).toHaveBeenCalledWith( - 101, - OrderStatus.DELIVERED, - ); - expect(mockRequestsService.updateDeliveryDetails).toHaveBeenCalledWith( - requestId, - new Date(body.dateReceived), - body.feedback, - [], - ); - expect(result).toEqual(updatedRequest); - }); - - it('should throw an error for invalid date', async () => { - await expect( - controller.confirmDelivery( - 1, - { dateReceived: 'bad-date', feedback: '' }, - [], - ), - ).rejects.toThrow('Invalid date format for deliveryDate'); - }); - }); -}); diff --git a/apps/backend/src/foodRequests/request.controller.ts b/apps/backend/src/foodRequests/request.controller.ts index 1f449491..e3a93727 100644 --- a/apps/backend/src/foodRequests/request.controller.ts +++ b/apps/backend/src/foodRequests/request.controller.ts @@ -22,7 +22,7 @@ import { OrderStatus } from '../orders/types'; @Controller('requests') // @UseInterceptors() -export class RequestsController { +export class FoodRequestsController { constructor( private requestsService: RequestsService, private awsS3Service: AWSS3Service, @@ -43,6 +43,14 @@ export class RequestsController { return this.requestsService.find(pantryId); } + @Get('get-order/:requestId') + async getOrderByRequestId( + @Param('requestId', ParseIntPipe) requestId: number, + ): Promise { + const request = await this.requestsService.findOne(requestId); + return request.order; + } + @Post('/create') @ApiBody({ description: 'Details for creating a food request', diff --git a/apps/backend/src/foodRequests/request.module.ts b/apps/backend/src/foodRequests/request.module.ts index 14a605d8..baf98bcd 100644 --- a/apps/backend/src/foodRequests/request.module.ts +++ b/apps/backend/src/foodRequests/request.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { RequestsController } from './request.controller'; +import { FoodRequestsController } from './request.controller'; import { FoodRequest } from './request.entity'; import { RequestsService } from './request.service'; import { JwtStrategy } from '../auth/jwt.strategy'; @@ -9,15 +9,14 @@ import { AWSS3Module } from '../aws/aws-s3.module'; import { MulterModule } from '@nestjs/platform-express'; import { OrdersService } from '../orders/order.service'; import { Order } from '../orders/order.entity'; -import { Pantry } from '../pantries/pantries.entity'; @Module({ imports: [ AWSS3Module, MulterModule.register({ dest: './uploads' }), - TypeOrmModule.forFeature([FoodRequest, Order, Pantry]), + TypeOrmModule.forFeature([FoodRequest, Order]), ], - controllers: [RequestsController], + controllers: [FoodRequestsController], providers: [RequestsService, OrdersService, AuthService, JwtStrategy], }) export class RequestsModule {} diff --git a/apps/backend/src/foodRequests/request.service.spec.ts b/apps/backend/src/foodRequests/request.service.spec.ts deleted file mode 100644 index cc69a5a3..00000000 --- a/apps/backend/src/foodRequests/request.service.spec.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { Test } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { FoodRequest } from './request.entity'; -import { RequestsService } from './request.service'; -import { mock } from 'jest-mock-extended'; -import { Pantry } from '../pantries/pantries.entity'; -import { RequestSize } from './types'; -import { Order } from '../orders/order.entity'; -import { OrderStatus } from '../orders/types'; - -const mockRequestsRepository = mock>(); -const mockPantryRepository = mock>(); - -const mockRequest: Partial = { - requestId: 1, - pantryId: 1, - requestedItems: ['Canned Goods', 'Vegetables'], - additionalInformation: 'No onions, please.', - requestedAt: null, - dateReceived: null, - feedback: null, - photos: null, - order: null, -}; - -describe('RequestsService', () => { - let service: RequestsService; - - beforeAll(async () => { - // Reset the mock repository before compiling module - mockRequestsRepository.findOne.mockReset(); - mockRequestsRepository.create.mockReset(); - mockRequestsRepository.save.mockReset(); - mockRequestsRepository.find.mockReset(); - mockPantryRepository.findOneBy.mockReset(); - - const module = await Test.createTestingModule({ - providers: [ - RequestsService, - { - provide: getRepositoryToken(FoodRequest), - useValue: mockRequestsRepository, - }, - { - provide: getRepositoryToken(Pantry), - useValue: mockPantryRepository, - }, - ], - }).compile(); - - service = module.get(RequestsService); - }); - - beforeEach(() => { - mockRequestsRepository.findOne.mockReset(); - mockRequestsRepository.create.mockReset(); - mockRequestsRepository.save.mockReset(); - mockRequestsRepository.find.mockReset(); - mockPantryRepository.findOneBy.mockReset(); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('findOne', () => { - it('should return a food request with the corresponding id', async () => { - const requestId = 1; - mockRequestsRepository.findOne.mockResolvedValueOnce( - mockRequest as FoodRequest, - ); - const result = await service.findOne(requestId); - expect(result).toEqual(mockRequest); - expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ - where: { requestId }, - relations: ['order'], - }); - }); - - it('should throw an error if the request id is not found', async () => { - const requestId = 999; - - mockRequestsRepository.findOne.mockResolvedValueOnce(null); - - await expect(service.findOne(requestId)).rejects.toThrow( - `Request ${requestId} not found`, - ); - - expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ - where: { requestId }, - relations: ['order'], - }); - }); - }); - - describe('create', () => { - it('should successfully create and return a new food request', async () => { - mockPantryRepository.findOneBy.mockResolvedValueOnce({ - pantryId: 1, - } as unknown as Pantry); - mockRequestsRepository.create.mockReturnValueOnce( - mockRequest as FoodRequest, - ); - mockRequestsRepository.save.mockResolvedValueOnce( - mockRequest as FoodRequest, - ); - mockRequestsRepository.find.mockResolvedValueOnce([ - mockRequest as FoodRequest, - ]); - - const result = await service.create( - mockRequest.pantryId, - mockRequest.requestedSize, - mockRequest.requestedItems, - mockRequest.additionalInformation, - mockRequest.dateReceived, - mockRequest.feedback, - mockRequest.photos, - ); - - expect(result).toEqual(mockRequest); - expect(mockRequestsRepository.create).toHaveBeenCalledWith({ - pantryId: mockRequest.pantryId, - requestedSize: mockRequest.requestedSize, - requestedItems: mockRequest.requestedItems, - additionalInformation: mockRequest.additionalInformation, - dateReceived: mockRequest.dateReceived, - feedback: mockRequest.feedback, - photos: mockRequest.photos, - }); - expect(mockRequestsRepository.save).toHaveBeenCalledWith(mockRequest); - }); - - it('should throw an error if the pantry ID does not exist', async () => { - const invalidPantryId = 999; - - await expect( - service.create( - invalidPantryId, - RequestSize.MEDIUM, - ['Canned Goods', 'Vegetables'], - 'Additional info', - null, - null, - null, - ), - ).rejects.toThrow(`Pantry ${invalidPantryId} not found`); - - expect(mockRequestsRepository.create).not.toHaveBeenCalled(); - expect(mockRequestsRepository.save).not.toHaveBeenCalled(); - }); - }); - - describe('find', () => { - it('should return all food requests for a specific pantry', async () => { - const mockRequests: Partial[] = [ - mockRequest, - { - requestId: 2, - pantryId: 1, - requestedSize: RequestSize.LARGE, - requestedItems: ['Rice', 'Beans'], - additionalInformation: 'Gluten-free items only.', - requestedAt: null, - dateReceived: null, - feedback: null, - photos: null, - order: null, - }, - { - requestId: 3, - pantryId: 2, - requestedSize: RequestSize.SMALL, - requestedItems: ['Fruits', 'Snacks'], - additionalInformation: 'No nuts, please.', - requestedAt: null, - dateReceived: null, - feedback: null, - photos: null, - order: null, - }, - ]; - const pantryId = 1; - mockRequestsRepository.find.mockResolvedValueOnce( - mockRequests.slice(0, 2) as FoodRequest[], - ); - - const result = await service.find(pantryId); - - expect(result).toEqual(mockRequests.slice(0, 2)); - expect(mockRequestsRepository.find).toHaveBeenCalledWith({ - where: { pantryId }, - relations: ['order'], - }); - }); - }); - - describe('updateDeliveryDetails', () => { - it('should update and return the food request with new delivery details', async () => { - const mockOrder: Partial = { - orderId: 1, - pantry: null, - request: null, - requestId: 1, - foodManufacturer: null, - shippedBy: 1, - status: OrderStatus.SHIPPED, - createdAt: new Date(), - shippedAt: new Date(), - deliveredAt: null, - }; - - const mockRequest2: Partial = { - ...mockRequest, - order: mockOrder as Order, - }; - - const requestId = 1; - const deliveryDate = new Date(); - const feedback = 'Good delivery!'; - const photos = ['photo1.jpg', 'photo2.jpg']; - - mockRequestsRepository.findOne.mockResolvedValueOnce( - mockRequest2 as FoodRequest, - ); - mockRequestsRepository.save.mockResolvedValueOnce({ - ...mockRequest, - dateReceived: deliveryDate, - feedback, - photos, - order: { - ...(mockOrder as Order), - status: OrderStatus.DELIVERED, - } as Order, - } as FoodRequest); - - const result = await service.updateDeliveryDetails( - requestId, - deliveryDate, - feedback, - photos, - ); - - expect(result).toEqual({ - ...mockRequest, - dateReceived: deliveryDate, - feedback, - photos, - order: { ...mockOrder, status: 'delivered' }, - }); - - expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ - where: { requestId }, - relations: ['order'], - }); - - expect(mockRequestsRepository.save).toHaveBeenCalledWith({ - ...mockRequest, - dateReceived: deliveryDate, - feedback, - photos, - order: { ...mockOrder, status: 'delivered' }, - }); - }); - - it('should throw an error if the request ID is invalid', async () => { - const requestId = 999; - const deliveryDate = new Date(); - const feedback = 'Good delivery!'; - const photos = ['photo1.jpg', 'photo2.jpg']; - - mockRequestsRepository.findOne.mockResolvedValueOnce(null); - - await expect( - service.updateDeliveryDetails( - requestId, - deliveryDate, - feedback, - photos, - ), - ).rejects.toThrow('Invalid request ID'); - - expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ - where: { requestId }, - relations: ['order'], - }); - }); - - it('should throw an error if there is no associated order', async () => { - const requestId = 1; - const deliveryDate = new Date(); - const feedback = 'Good delivery!'; - const photos = ['photo1.jpg', 'photo2.jpg']; - - mockRequestsRepository.findOne.mockResolvedValueOnce( - mockRequest as FoodRequest, - ); - - await expect( - service.updateDeliveryDetails( - requestId, - deliveryDate, - feedback, - photos, - ), - ).rejects.toThrow('No associated order found for this request'); - - expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ - where: { requestId }, - relations: ['order'], - }); - }); - - it('should throw an error if the order does not have a food manufacturer', async () => { - const mockOrder: Partial = { - orderId: 1, - pantry: null, - request: null, - requestId: 1, - foodManufacturer: null, - shippedBy: null, - status: OrderStatus.SHIPPED, - createdAt: new Date(), - shippedAt: new Date(), - deliveredAt: null, - }; - const mockRequest2: Partial = { - ...mockRequest, - order: mockOrder as Order, - }; - - const requestId = 1; - const deliveryDate = new Date(); - const feedback = 'Good delivery!'; - const photos = ['photo1.jpg', 'photo2.jpg']; - - mockRequestsRepository.findOne.mockResolvedValueOnce( - mockRequest2 as FoodRequest, - ); - - await expect( - service.updateDeliveryDetails( - requestId, - deliveryDate, - feedback, - photos, - ), - ).rejects.toThrow('No associated food manufacturer found for this order'); - - expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ - where: { requestId }, - relations: ['order'], - }); - }); - }); -}); diff --git a/apps/backend/src/foodRequests/request.service.ts b/apps/backend/src/foodRequests/request.service.ts index 32e600ba..d20d296e 100644 --- a/apps/backend/src/foodRequests/request.service.ts +++ b/apps/backend/src/foodRequests/request.service.ts @@ -9,13 +9,11 @@ import { FoodRequest } from './request.entity'; import { validateId } from '../utils/validation.utils'; import { RequestSize } from './types'; import { OrderStatus } from '../orders/types'; -import { Pantry } from '../pantries/pantries.entity'; @Injectable() export class RequestsService { constructor( @InjectRepository(FoodRequest) private repo: Repository, - @InjectRepository(Pantry) private pantryRepo: Repository, ) {} async findOne(requestId: number): Promise { @@ -41,13 +39,6 @@ export class RequestsService { feedback: string | undefined, photos: string[] | undefined, ): Promise { - validateId(pantryId, 'Pantry'); - - const pantry = await this.pantryRepo.findOneBy({ pantryId }); - if (!pantry) { - throw new NotFoundException(`Pantry ${pantryId} not found`); - } - const foodRequest = this.repo.create({ pantryId, requestedSize, diff --git a/apps/backend/src/migrations/1764811878152-RemoveMultipleVolunteerTypes.ts b/apps/backend/src/migrations/1764811878152-RemoveMultipleVolunteerTypes.ts deleted file mode 100644 index ae661a95..00000000 --- a/apps/backend/src/migrations/1764811878152-RemoveMultipleVolunteerTypes.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveMultipleVolunteerTypes1764811878152 - implements MigrationInterface -{ - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - ALTER COLUMN role DROP DEFAULT; - - CREATE TYPE users_role_enum_new AS ENUM ( - 'admin', - 'volunteer', - 'pantry', - 'food_manufacturer' - ); - - ALTER TABLE users - ALTER COLUMN role - TYPE users_role_enum_new - USING ( - CASE - WHEN role IN ('standard_volunteer', 'lead_volunteer') - THEN 'volunteer' - ELSE role::text - END - )::users_role_enum_new; - - DROP TYPE users_role_enum; - - ALTER TYPE users_role_enum_new - RENAME TO users_role_enum; - - ALTER TABLE users - ALTER COLUMN role - SET DEFAULT 'volunteer'; - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE users - ALTER COLUMN role DROP DEFAULT; - - CREATE TYPE users_role_enum_old AS ENUM ( - 'admin', - 'lead_volunteer', - 'standard_volunteer', - 'pantry', - 'food_manufacturer' - ); - - ALTER TABLE users - ALTER COLUMN role - TYPE users_role_enum_old - USING ( - CASE - WHEN role = 'volunteer' - THEN 'standard_volunteer' - ELSE role::text - END - )::users_role_enum_old; - - DROP TYPE users_role_enum; - - ALTER TYPE users_role_enum_old - RENAME TO users_role_enum; - - ALTER TABLE users - ALTER COLUMN role - SET DEFAULT 'standard_volunteer'; - `); - } -} diff --git a/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts b/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts index 4c35fd0f..6419e233 100644 --- a/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts +++ b/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts @@ -1,24 +1,25 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; +import { MigrationInterface, QueryRunner } from "typeorm"; export class RemoveUnusedStatuses1764816885341 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE allocations DROP COLUMN IF EXISTS status;`, - ); - await queryRunner.query( - `ALTER TABLE donation_items DROP COLUMN IF EXISTS status;`, - ); - } - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE allocations DROP COLUMN IF EXISTS status;` + ); + await queryRunner.query( + `ALTER TABLE donation_items DROP COLUMN IF EXISTS status;` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` ALTER TABLE allocations ADD COLUMN status VARCHAR(25) NOT NULL DEFAULT 'pending'; `); - await queryRunner.query(` + await queryRunner.query(` ALTER TABLE donation_items ADD COLUMN status VARCHAR(25) NOT NULL DEFAULT 'available'; `); - } + } } diff --git a/apps/backend/src/pantries/dtos/pantry-application.dto.ts b/apps/backend/src/pantries/dtos/pantry-application.dto.ts index c7473b0f..42510915 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/backend/src/pantries/pantries.controller.ts b/apps/backend/src/pantries/pantries.controller.ts index ee8287ce..f1f7941e 100644 --- a/apps/backend/src/pantries/pantries.controller.ts +++ b/apps/backend/src/pantries/pantries.controller.ts @@ -5,12 +5,14 @@ import { Param, ParseIntPipe, Post, + Put, ValidationPipe, } from '@nestjs/common'; import { Pantry } from './pantries.entity'; import { PantriesService } from './pantries.service'; import { PantryApplicationDto } from './dtos/pantry-application.dto'; import { ApiBody } from '@nestjs/swagger'; +import { ApprovedPantryResponse } from './types'; import { Activity, AllergensConfidence, @@ -34,6 +36,11 @@ export class PantriesController { return this.pantriesService.getPendingPantries(); } + @Get('/approved') + async getApprovedPantries(): Promise { + return this.pantriesService.getApprovedPantriesWithVolunteers(); + } + @Get('/:pantryId') async getPantry( @Param('pantryId', ParseIntPipe) pantryId: number, diff --git a/apps/backend/src/pantries/pantries.service.ts b/apps/backend/src/pantries/pantries.service.ts index a2f254ec..0d692595 100644 --- a/apps/backend/src/pantries/pantries.service.ts +++ b/apps/backend/src/pantries/pantries.service.ts @@ -7,6 +7,7 @@ import { validateId } from '../utils/validation.utils'; import { PantryStatus } from './types'; import { PantryApplicationDto } from './dtos/pantry-application.dto'; import { Role } from '../users/types'; +import { ApprovedPantryResponse } from './types'; @Injectable() export class PantriesService { @@ -110,6 +111,44 @@ export class PantriesService { await this.repo.update(id, { status: PantryStatus.DENIED }); } + + async getApprovedPantriesWithVolunteers(): Promise { + const pantries = await this.repo.find({ + where: { status: PantryStatus.APPROVED }, + relations: ['pantryUser', 'volunteers'], + }); + + return pantries.map((pantry) => ({ + pantryId: pantry.pantryId, + pantryName: pantry.pantryName, + address: { + line1: pantry.shipmentAddressLine1, + line2: pantry.shipmentAddressLine2, + city: pantry.shipmentAddressCity, + state: pantry.shipmentAddressState, + zip: pantry.shipmentAddressZip, + country: pantry.shipmentAddressCountry, + }, + contactInfo: { + firstName: pantry.pantryUser.firstName, + lastName: pantry.pantryUser.lastName, + email: pantry.pantryUser.email, + phone: pantry.pantryUser.phone, + }, + refrigeratedDonation: pantry.refrigeratedDonation, + allergenClients: pantry.allergenClients, + status: pantry.status, + dateApplied: pantry.dateApplied, + assignedVolunteers: (pantry.volunteers || []).map((volunteer) => ({ + userId: volunteer.id, + name: `${volunteer.firstName} ${volunteer.lastName}`, + email: volunteer.email, + phone: volunteer.phone, + role: volunteer.role, + })), + })); + } + async findByIds(pantryIds: number[]): Promise { pantryIds.forEach((id) => validateId(id, 'Pantry')); diff --git a/apps/backend/src/pantries/types.ts b/apps/backend/src/pantries/types.ts index cdf8b671..16c41f4f 100644 --- a/apps/backend/src/pantries/types.ts +++ b/apps/backend/src/pantries/types.ts @@ -1,3 +1,34 @@ +export interface ApprovedPantryResponse { + pantryId: number; + pantryName: string; + address: { + line1: string; + line2: string | null; + city: string; + state: string; + zip: string; + country: string | null; + }; + contactInfo: { + firstName: string; + lastName: string; + email: string; + phone: string; + }; + refrigeratedDonation: string; + allergenClients: string; + status: string; + dateApplied: Date; + assignedVolunteers: AssignedVolunteer[]; +} + +export interface AssignedVolunteer { + userId: number; + name: string; + email: string; + phone: string; + role: string; +} export enum RefrigeratedDonation { YES = 'Yes, always', NO = 'No', diff --git a/apps/backend/src/users/types.ts b/apps/backend/src/users/types.ts index 695cbc44..4eb7f295 100644 --- a/apps/backend/src/users/types.ts +++ b/apps/backend/src/users/types.ts @@ -1,6 +1,14 @@ export enum Role { ADMIN = 'admin', - VOLUNTEER = 'volunteer', + LEAD_VOLUNTEER = 'lead_volunteer', + STANDARD_VOLUNTEER = 'standard_volunteer', PANTRY = 'pantry', FOODMANUFACTURER = 'food_manufacturer', } + +export const VOLUNTEER_ROLES: Role[] = [ + Role.LEAD_VOLUNTEER, + Role.STANDARD_VOLUNTEER, +]; + +export type VolunteerType = (typeof VOLUNTEER_ROLES)[number]; diff --git a/apps/backend/src/users/user.entity.ts b/apps/backend/src/users/user.entity.ts index 746484ce..e2b9a958 100644 --- a/apps/backend/src/users/user.entity.ts +++ b/apps/backend/src/users/user.entity.ts @@ -19,7 +19,7 @@ export class User { name: 'role', enum: Role, enumName: 'users_role_enum', - default: Role.VOLUNTEER, + default: Role.STANDARD_VOLUNTEER, }) role: Role; diff --git a/apps/backend/src/users/users.controller.spec.ts b/apps/backend/src/users/users.controller.spec.ts index 97811a0e..8a22cf3a 100644 --- a/apps/backend/src/users/users.controller.spec.ts +++ b/apps/backend/src/users/users.controller.spec.ts @@ -13,25 +13,17 @@ const mockUserService = mock(); const mockUser1: Partial = { id: 1, - email: 'john@example.com', - firstName: 'John', - lastName: 'Doe', - phone: '1234567890', - role: Role.VOLUNTEER, + role: Role.STANDARD_VOLUNTEER, }; const mockUser2: Partial = { id: 2543210, - email: 'bobsmith@example.com', - firstName: 'Bob', - lastName: 'Smith', - phone: '9876', - role: Role.VOLUNTEER, + role: Role.LEAD_VOLUNTEER, }; const mockUser3: Partial = { id: 3, - role: Role.VOLUNTEER, + role: Role.STANDARD_VOLUNTEER, }; const mockPantries: Partial[] = [ @@ -76,45 +68,6 @@ describe('UsersController', () => { expect(controller).toBeDefined(); }); - describe('GET /volunteers', () => { - it('should return all volunteers', async () => { - const users: (Omit, 'pantries'> & { - pantryIds: number[]; - })[] = [ - { - id: 1, - role: Role.VOLUNTEER, - pantryIds: [1], - }, - { - id: 2, - role: Role.VOLUNTEER, - pantryIds: [2], - }, - { - id: 3, - role: Role.ADMIN, - pantryIds: [3], - }, - ]; - - const volunteers = users.slice(0, 2); - - mockUserService.getVolunteersAndPantryAssignments.mockResolvedValue( - volunteers as (Omit & { pantryIds: number[] })[], - ); - - const result = await controller.getAllVolunteers(); - - expect(result).toEqual(volunteers); - expect(result.length).toBe(2); - expect(result.every((u) => u.role === Role.VOLUNTEER)).toBe(true); - expect( - mockUserService.getVolunteersAndPantryAssignments, - ).toHaveBeenCalled(); - }); - }); - describe('GET /:id', () => { it('should return a user by id', async () => { mockUserService.findOne.mockResolvedValue(mockUser1 as User); @@ -189,7 +142,7 @@ describe('UsersController', () => { firstName: 'Jane', lastName: 'Smith', phone: '9876543210', - role: Role.VOLUNTEER, + role: Role.STANDARD_VOLUNTEER, }; const error = new Error('Database error'); diff --git a/apps/backend/src/users/users.service.spec.ts b/apps/backend/src/users/users.service.spec.ts index 3cb9b77a..db28dc0e 100644 --- a/apps/backend/src/users/users.service.spec.ts +++ b/apps/backend/src/users/users.service.spec.ts @@ -13,14 +13,14 @@ import { PantriesService } from '../pantries/pantries.service'; const mockUserRepository = mock>(); const mockPantriesService = mock(); -const mockUser: User = { +const mockUser = { id: 1, email: 'test@example.com', firstName: 'John', lastName: 'Doe', phone: '1234567890', - role: Role.VOLUNTEER, -}; + role: Role.STANDARD_VOLUNTEER, +} as User; describe('UsersService', () => { let service: UsersService; @@ -203,7 +203,7 @@ describe('UsersService', () => { describe('findUsersByRoles', () => { it('should return users by roles', async () => { - const roles = [Role.ADMIN, Role.VOLUNTEER]; + const roles = [Role.ADMIN, Role.LEAD_VOLUNTEER]; const users = [mockUser]; mockUserRepository.find.mockResolvedValue(users); diff --git a/apps/backend/src/users/users.service.ts b/apps/backend/src/users/users.service.ts index 65f90ae1..1f0984e3 100644 --- a/apps/backend/src/users/users.service.ts +++ b/apps/backend/src/users/users.service.ts @@ -7,7 +7,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { In, Repository } from 'typeorm'; import { User } from './user.entity'; -import { Role } from './types'; +import { Role, VOLUNTEER_ROLES } from './types'; import { validateId } from '../utils/validation.utils'; import { Pantry } from '../pantries/pantries.entity'; import { PantriesService } from '../pantries/pantries.service'; @@ -60,7 +60,7 @@ export class UsersService { if (!volunteer) throw new NotFoundException(`User ${volunteerId} not found`); - if (volunteer.role !== Role.VOLUNTEER) { + if (!VOLUNTEER_ROLES.includes(volunteer.role)) { throw new BadRequestException(`User ${volunteerId} is not a volunteer`); } return volunteer; @@ -106,7 +106,7 @@ export class UsersService { async getVolunteersAndPantryAssignments(): Promise< (Omit & { pantryIds: number[] })[] > { - const volunteers = await this.findUsersByRoles([Role.VOLUNTEER]); + const volunteers = await this.findUsersByRoles(VOLUNTEER_ROLES); return volunteers.map((v) => { const { pantries, ...volunteerWithoutPantries } = v; diff --git a/apps/backend/src/utils/validation.utils.spec.ts b/apps/backend/src/utils/validation.utils.spec.ts deleted file mode 100644 index 6b390840..00000000 --- a/apps/backend/src/utils/validation.utils.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BadRequestException } from '@nestjs/common'; -import { validateId } from './validation.utils'; - -describe('validateId', () => { - it('should not throw an error for a valid ID', () => { - expect(() => validateId(5, 'User')).not.toThrow(); - }); - - it('should throw BadRequestException for ID < 1', () => { - expect(() => validateId(0, 'User')).toThrow( - new BadRequestException('Invalid User ID'), - ); - }); -}); diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index f62efd8f..a548bfa5 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -10,7 +10,6 @@ import { CreateFoodRequestBody, Pantry, PantryApplicationDto, - UserDto, } from 'types/types'; const defaultBaseUrl = @@ -96,10 +95,6 @@ export class ApiClient { .then((response) => response.data); } - public async postUser(data: UserDto): Promise { - return this.axiosInstance.post(`/api/users`, data); - } - public async getPantrySSFRep(pantryId: number): Promise { return this.get(`/api/pantries/${pantryId}/ssf-contact`) as Promise; } @@ -193,6 +188,12 @@ export class ApiClient { return this.axiosInstance.get(`api/orders/${orderId}`) as Promise; } + public async getOrderByRequestId(requestId: number): Promise { + return this.axiosInstance.get( + `api/requests/get-order/${requestId}`, + ) as Promise; + } + async getAllAllocationsByOrder(orderId: number): Promise { return this.axiosInstance .get(`api/orders/${orderId}/allocations`) diff --git a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx b/apps/frontend/src/components/forms/addNewVolunteerModal.tsx deleted file mode 100644 index 7c2fa99c..00000000 --- a/apps/frontend/src/components/forms/addNewVolunteerModal.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { - Dialog, - Button, - Text, - Flex, - Field, - Input, - CloseButton, - Box -} from '@chakra-ui/react'; -import { useState } from 'react'; -import { Role, UserDto } from "../../types/types"; -import ApiClient from '@api/apiClient'; -import { USPhoneInput } from './usPhoneInput'; -import { PlusIcon } from 'lucide-react'; - -interface NewVolunteerModalProps { - onSubmitSuccess?: () => void; - onSubmitFail?: () => void; -} - -const NewVolunteerModal: React.FC = ({ onSubmitSuccess, onSubmitFail }) => { - const [firstName, setFirstName] = useState(""); - const [lastName, setLastName] = useState(""); - const [email, setEmail] = useState(""); - const [phone, setPhone] = useState(""); - - const [isOpen, setIsOpen] = useState(false); - - const [error, setError] = useState(""); - - const handleSubmit = async () => { - console.log("RAW phone value:", phone); - if (!firstName || !lastName || !email || !phone || phone === "+1") { - setError("Please fill in all fields. *"); - return; - } - - setError(""); - - const newVolunteer: UserDto = { - firstName, - lastName, - email, - phone, - role: Role.VOLUNTEER - }; - - try { - await ApiClient.postUser(newVolunteer); - if (onSubmitSuccess) onSubmitSuccess(); - handleClear(); - } catch (error: unknown) { - - let hasEmailError = false - let hasPhoneError = false - - if (typeof error === "object" && error !== null) { - const e = error as { response?: { data?: { message?: string | string[] } } }; - const message = e.response?.data?.message; - - hasEmailError = - Array.isArray(message) && - message.some((msg) => typeof msg === "string" && msg.toLowerCase().includes("email")); - - hasPhoneError = - Array.isArray(message) && - message.some((msg) => typeof msg === "string" && msg.toLowerCase().includes("phone")); - } - - if (hasEmailError) { - setError("Please specify a valid email. *") - } else if (hasPhoneError) { - setError("Please specify a valid phone number. *") - } else { - if (onSubmitFail) onSubmitFail(); - handleClear(); - } - } - } - - const handleClear = () => { - setFirstName(""); - setLastName(""); - setEmail(""); - setPhone(""); - setError(""); - setIsOpen(false); - }; - - return ( - - - - - - - - - - Add New Volunteer - - setIsOpen(false)} size="md" position="absolute" top={3} right={3}/> - - - - Complete all information in the form to register a new volunteer. - - - - First Name - setFirstName(e.target.value)}/> - - - Last Name - setLastName(e.target.value)}/> - - - - Email - setEmail(e.target.value)}/> - - - Phone Number - - - {error && ( - - {error} - - )} - - - - - - - - - ); -}; - - - - - - -export default NewVolunteerModal; \ No newline at end of file diff --git a/apps/frontend/src/components/forms/pantryApplicationForm.tsx b/apps/frontend/src/components/forms/pantryApplicationForm.tsx index df185caa..94abfe3a 100644 --- a/apps/frontend/src/components/forms/pantryApplicationForm.tsx +++ b/apps/frontend/src/components/forms/pantryApplicationForm.tsx @@ -67,22 +67,18 @@ 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< - boolean | null - >(); + const [reserveFoodForAllergic, setReserveFoodForAllergic] = useState(); + const [differentMailingAddress, setDifferentMailingAddress] = useState(); const [otherEmailContact, setOtherEmailContact] = useState(false); const sectionTitleStyles = { - fontFamily: 'inter', + fontFamily: "inter", fontWeight: '600', fontSize: 'md', color: 'gray.dark', @@ -90,16 +86,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', }; @@ -107,121 +103,96 @@ 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} @@ -230,151 +201,127 @@ 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} @@ -384,22 +331,28 @@ 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} @@ -411,13 +364,10 @@ const PantryApplicationForm: React.FC = () => { Please note any delivery window restrictions. -