diff --git a/apps/backend/src/foodRequests/request.controller.spec.ts b/apps/backend/src/foodRequests/request.controller.spec.ts new file mode 100644 index 00000000..71c35ca4 --- /dev/null +++ b/apps/backend/src/foodRequests/request.controller.spec.ts @@ -0,0 +1,322 @@ +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 e3a93727..1f449491 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 FoodRequestsController { +export class RequestsController { constructor( private requestsService: RequestsService, private awsS3Service: AWSS3Service, @@ -43,14 +43,6 @@ export class FoodRequestsController { 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 baf98bcd..14a605d8 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 { FoodRequestsController } from './request.controller'; +import { RequestsController } from './request.controller'; import { FoodRequest } from './request.entity'; import { RequestsService } from './request.service'; import { JwtStrategy } from '../auth/jwt.strategy'; @@ -9,14 +9,15 @@ 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]), + TypeOrmModule.forFeature([FoodRequest, Order, Pantry]), ], - controllers: [FoodRequestsController], + controllers: [RequestsController], 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 new file mode 100644 index 00000000..cc69a5a3 --- /dev/null +++ b/apps/backend/src/foodRequests/request.service.spec.ts @@ -0,0 +1,357 @@ +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 d20d296e..32e600ba 100644 --- a/apps/backend/src/foodRequests/request.service.ts +++ b/apps/backend/src/foodRequests/request.service.ts @@ -9,11 +9,13 @@ 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 { @@ -39,6 +41,13 @@ 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/1764816885341-RemoveUnusedStatuses.ts b/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts index 6419e233..4c35fd0f 100644 --- a/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts +++ b/apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts @@ -1,25 +1,24 @@ -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 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 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 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/backend/src/utils/validation.utils.spec.ts b/apps/backend/src/utils/validation.utils.spec.ts new file mode 100644 index 00000000..6b390840 --- /dev/null +++ b/apps/backend/src/utils/validation.utils.spec.ts @@ -0,0 +1,14 @@ +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 a548bfa5..1cc69f84 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -188,12 +188,6 @@ 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/pantryApplicationForm.tsx b/apps/frontend/src/components/forms/pantryApplicationForm.tsx index 94abfe3a..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. -