diff --git a/apps/backend/src/allocations/allocations.entity.ts b/apps/backend/src/allocations/allocations.entity.ts index a5a3c734..26985283 100644 --- a/apps/backend/src/allocations/allocations.entity.ts +++ b/apps/backend/src/allocations/allocations.entity.ts @@ -6,15 +6,20 @@ import { JoinColumn, } from 'typeorm'; import { DonationItem } from '../donationItems/donationItems.entity'; +import { Order } from '../orders/order.entity'; @Entity('allocations') export class Allocation { @PrimaryGeneratedColumn({ name: 'allocation_id' }) allocationId: number; - @Column({ name: 'order_id', type: 'int' }) + @Column({ name: 'order_id', type: 'int', nullable: false }) orderId: number; + @ManyToOne(() => Order, (order) => order.allocations) + @JoinColumn({ name: 'order_id' }) + order: Order; + @Column({ name: 'item_id', type: 'int', nullable: false }) itemId: number; diff --git a/apps/backend/src/foodRequests/dtos/order-details.dto.ts b/apps/backend/src/foodRequests/dtos/order-details.dto.ts new file mode 100644 index 00000000..21d360ec --- /dev/null +++ b/apps/backend/src/foodRequests/dtos/order-details.dto.ts @@ -0,0 +1,15 @@ +import { FoodType } from '../../donationItems/types'; +import { OrderStatus } from '../../orders/types'; + +export class OrderItemDetailsDto { + name: string; + quantity: number; + foodType: FoodType; +} + +export class OrderDetailsDto { + orderId: number; + status: OrderStatus; + foodManufacturerName: string; + items: OrderItemDetailsDto[]; +} diff --git a/apps/backend/src/foodRequests/request.controller.spec.ts b/apps/backend/src/foodRequests/request.controller.spec.ts index 71c35ca4..45b0d2d2 100644 --- a/apps/backend/src/foodRequests/request.controller.spec.ts +++ b/apps/backend/src/foodRequests/request.controller.spec.ts @@ -8,6 +8,9 @@ import { Readable } from 'stream'; import { FoodRequest } from './request.entity'; import { RequestSize } from './types'; import { OrderStatus } from '../orders/types'; +import { FoodType } from '../donationItems/types'; +import { OrderDetailsDto } from './dtos/order-details.dto'; +import { Order } from '../orders/order.entity'; const mockRequestsService = mock(); const mockOrdersService = mock(); @@ -26,6 +29,7 @@ describe('RequestsController', () => { mockRequestsService.find.mockReset(); mockRequestsService.create.mockReset(); mockRequestsService.updateDeliveryDetails?.mockReset(); + mockRequestsService.getOrderDetails.mockReset(); mockAWSS3Service.upload.mockReset(); mockOrdersService.updateStatus.mockReset(); @@ -91,6 +95,55 @@ describe('RequestsController', () => { }); }); + describe('GET /all-order-details/:requestId', () => { + it('should call requestsService.getOrderDetails and return all associated orders and their details', async () => { + const mockOrderDetails: OrderDetailsDto[] = [ + { + orderId: 10, + status: OrderStatus.DELIVERED, + foodManufacturerName: 'Test Manufacturer', + items: [ + { + name: 'Rice', + quantity: 5, + foodType: FoodType.GRANOLA, + }, + { + name: 'Beans', + quantity: 3, + foodType: FoodType.DRIED_BEANS, + }, + ], + }, + { + orderId: 11, + status: OrderStatus.PENDING, + foodManufacturerName: 'Another Manufacturer', + items: [ + { + name: 'Milk', + quantity: 2, + foodType: FoodType.DAIRY_FREE_ALTERNATIVES, + }, + ], + }, + ]; + + const requestId = 1; + + mockRequestsService.getOrderDetails.mockResolvedValueOnce( + mockOrderDetails as OrderDetailsDto[], + ); + + const result = await controller.getAllOrderDetailsFromRequest(requestId); + + expect(result).toEqual(mockOrderDetails); + expect(mockRequestsService.getOrderDetails).toHaveBeenCalledWith( + requestId, + ); + }); + }); + describe('POST /create', () => { it('should call requestsService.create and return the created food request', async () => { const createBody: Partial = { @@ -107,7 +160,7 @@ describe('RequestsController', () => { requestId: 1, ...createBody, requestedAt: new Date(), - order: null, + orders: null, }; mockRequestsService.create.mockResolvedValueOnce( @@ -181,17 +234,21 @@ describe('RequestsController', () => { mockRequestsService.findOne.mockResolvedValue({ requestId, pantryId: 1, - order: { orderId: 99 }, + orders: [{ orderId: 99 }], } as FoodRequest); mockOrdersService.updateStatus.mockResolvedValue(); + const order = new Order(); + order.orderId = 99; + const updatedRequest: Partial = { requestId, pantryId: 1, dateReceived: new Date(body.dateReceived), feedback: body.feedback, photos: uploadedUrls, + orders: [order], }; mockRequestsService.updateDeliveryDetails.mockResolvedValue( @@ -202,8 +259,6 @@ describe('RequestsController', () => { expect(mockAWSS3Service.upload).toHaveBeenCalledWith(photos); - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); - expect(mockOrdersService.updateStatus).toHaveBeenCalledWith( 99, OrderStatus.DELIVERED, @@ -230,17 +285,21 @@ describe('RequestsController', () => { mockRequestsService.findOne.mockResolvedValue({ requestId, pantryId: 1, - order: { orderId: 100 }, + orders: [{ orderId: 100 }], } as FoodRequest); mockOrdersService.updateStatus.mockResolvedValue(); + const order = new Order(); + order.orderId = 100; + const updatedRequest: Partial = { requestId, pantryId: 1, dateReceived: new Date(body.dateReceived), feedback: body.feedback, photos: [], + orders: [order], }; mockRequestsService.updateDeliveryDetails.mockResolvedValue( @@ -250,7 +309,6 @@ describe('RequestsController', () => { const result = await controller.confirmDelivery(requestId, body); expect(mockAWSS3Service.upload).not.toHaveBeenCalled(); - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); expect(mockOrdersService.updateStatus).toHaveBeenCalledWith( 100, OrderStatus.DELIVERED, @@ -275,17 +333,21 @@ describe('RequestsController', () => { mockRequestsService.findOne.mockResolvedValue({ requestId, pantryId: 1, - order: { orderId: 101 }, + orders: [{ orderId: 101 }], } as FoodRequest); mockOrdersService.updateStatus.mockResolvedValue(); + const order = new Order(); + order.orderId = 101; + const updatedRequest: Partial = { requestId, pantryId: 1, dateReceived: new Date(body.dateReceived), feedback: body.feedback, photos: [], + orders: [order], }; mockRequestsService.updateDeliveryDetails.mockResolvedValue( @@ -295,7 +357,6 @@ describe('RequestsController', () => { const result = await controller.confirmDelivery(requestId, body, []); expect(mockAWSS3Service.upload).not.toHaveBeenCalled(); - expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId); expect(mockOrdersService.updateStatus).toHaveBeenCalledWith( 101, OrderStatus.DELIVERED, diff --git a/apps/backend/src/foodRequests/request.controller.ts b/apps/backend/src/foodRequests/request.controller.ts index 1f449491..ec6dc0f0 100644 --- a/apps/backend/src/foodRequests/request.controller.ts +++ b/apps/backend/src/foodRequests/request.controller.ts @@ -8,6 +8,7 @@ import { UploadedFiles, UseInterceptors, BadRequestException, + NotFoundException, } from '@nestjs/common'; import { ApiBody } from '@nestjs/swagger'; import { RequestsService } from './request.service'; @@ -16,9 +17,9 @@ import { AWSS3Service } from '../aws/aws-s3.service'; import { FilesInterceptor } from '@nestjs/platform-express'; import * as multer from 'multer'; import { OrdersService } from '../orders/order.service'; -import { Order } from '../orders/order.entity'; import { RequestSize } from './types'; import { OrderStatus } from '../orders/types'; +import { OrderDetailsDto } from './dtos/order-details.dto'; @Controller('requests') // @UseInterceptors() @@ -43,6 +44,13 @@ export class RequestsController { return this.requestsService.find(pantryId); } + @Get('/all-order-details/:requestId') + async getAllOrderDetailsFromRequest( + @Param('requestId', ParseIntPipe) requestId: number, + ): Promise { + return this.requestsService.getOrderDetails(requestId); + } + @Post('/create') @ApiBody({ description: 'Details for creating a food request', @@ -109,6 +117,7 @@ export class RequestsController { ); } + //TODO: delete endpoint, here temporarily as a logic reference for order status impl. @Post('/:requestId/confirm-delivery') @ApiBody({ description: 'Details for a confirmation form', @@ -157,17 +166,29 @@ export class RequestsController { photos?.length, ); - const request = await this.requestsService.findOne(requestId); - await this.ordersService.updateStatus( - request.order.orderId, - OrderStatus.DELIVERED, - ); - - return this.requestsService.updateDeliveryDetails( + const updatedRequest = await this.requestsService.updateDeliveryDetails( requestId, formattedDate, body.feedback, uploadedPhotoUrls, ); + + if (!updatedRequest) { + throw new NotFoundException('Invalid request ID'); + } + + if (!updatedRequest.orders || updatedRequest.orders.length == 0) { + throw new NotFoundException( + 'No associated orders found for this request', + ); + } + + await Promise.all( + updatedRequest.orders.map((order) => + this.ordersService.updateStatus(order.orderId, OrderStatus.DELIVERED), + ), + ); + + return updatedRequest; } } diff --git a/apps/backend/src/foodRequests/request.entity.ts b/apps/backend/src/foodRequests/request.entity.ts index 06c2ce79..9864b4cc 100644 --- a/apps/backend/src/foodRequests/request.entity.ts +++ b/apps/backend/src/foodRequests/request.entity.ts @@ -47,5 +47,5 @@ export class FoodRequest { photos: string[]; @OneToMany(() => Order, (order) => order.request, { nullable: true }) - order: Order; + orders: Order[]; } diff --git a/apps/backend/src/foodRequests/request.service.spec.ts b/apps/backend/src/foodRequests/request.service.spec.ts index cc69a5a3..89d43414 100644 --- a/apps/backend/src/foodRequests/request.service.spec.ts +++ b/apps/backend/src/foodRequests/request.service.spec.ts @@ -8,9 +8,14 @@ import { Pantry } from '../pantries/pantries.entity'; import { RequestSize } from './types'; import { Order } from '../orders/order.entity'; import { OrderStatus } from '../orders/types'; +import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity'; +import { FoodType } from '../donationItems/types'; +import { DonationItem } from '../donationItems/donationItems.entity'; +import { Allocation } from '../allocations/allocations.entity'; const mockRequestsRepository = mock>(); const mockPantryRepository = mock>(); +const mockOrdersRepository = mock>(); const mockRequest: Partial = { requestId: 1, @@ -21,7 +26,7 @@ const mockRequest: Partial = { dateReceived: null, feedback: null, photos: null, - order: null, + orders: null, }; describe('RequestsService', () => { @@ -46,6 +51,10 @@ describe('RequestsService', () => { provide: getRepositoryToken(Pantry), useValue: mockPantryRepository, }, + { + provide: getRepositoryToken(Order), + useValue: mockOrdersRepository, + }, ], }).compile(); @@ -74,7 +83,7 @@ describe('RequestsService', () => { expect(result).toEqual(mockRequest); expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); }); @@ -89,7 +98,134 @@ describe('RequestsService', () => { expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ where: { requestId }, - relations: ['order'], + relations: ['orders'], + }); + }); + }); + + describe('getOrderDetails', () => { + it('should return mapped order details for a valid requestId', async () => { + const requestId = 1; + + const mockOrders: Partial[] = [ + { + orderId: 10, + status: OrderStatus.DELIVERED, + foodManufacturer: { + foodManufacturerName: 'Test Manufacturer', + } as FoodManufacturer, + allocations: [ + { + allocatedQuantity: 5, + item: { + itemName: 'Rice', + foodType: FoodType.GRANOLA, + } as DonationItem, + } as Allocation, + { + allocatedQuantity: 3, + item: { + itemName: 'Beans', + foodType: FoodType.DRIED_BEANS, + } as DonationItem, + } as Allocation, + ], + }, + { + orderId: 11, + status: OrderStatus.SHIPPED, + foodManufacturer: { + foodManufacturerName: 'Another Manufacturer', + } as FoodManufacturer, + allocations: [ + { + allocatedQuantity: 2, + item: { + itemName: 'Milk', + foodType: FoodType.DAIRY_FREE_ALTERNATIVES, + } as DonationItem, + } as Allocation, + ], + }, + ]; + + mockOrdersRepository.find.mockResolvedValueOnce(mockOrders as Order[]); + + mockRequestsRepository.findOne.mockResolvedValueOnce( + mockRequest as FoodRequest, + ); + + const result = await service.getOrderDetails(requestId); + + expect(result).toEqual([ + { + orderId: 10, + status: OrderStatus.DELIVERED, + foodManufacturerName: 'Test Manufacturer', + items: [ + { + name: 'Rice', + quantity: 5, + foodType: FoodType.GRANOLA, + }, + { + name: 'Beans', + quantity: 3, + foodType: FoodType.DRIED_BEANS, + }, + ], + }, + { + orderId: 11, + status: OrderStatus.SHIPPED, + foodManufacturerName: 'Another Manufacturer', + items: [ + { + name: 'Milk', + quantity: 2, + foodType: FoodType.DAIRY_FREE_ALTERNATIVES, + }, + ], + }, + ]); + + expect(mockOrdersRepository.find).toHaveBeenCalledWith({ + where: { requestId }, + relations: { + foodManufacturer: true, + allocations: { + item: true, + }, + }, + }); + }); + + it('should throw an error if the request id is not found', async () => { + const requestId = 999; + + await expect(service.getOrderDetails(requestId)).rejects.toThrow( + `Request ${requestId} not found`, + ); + }); + + it('should return empty list if no associated orders', async () => { + const requestId = 1; + + mockRequestsRepository.findOne.mockResolvedValueOnce( + mockRequest as FoodRequest, + ); + mockOrdersRepository.find.mockResolvedValueOnce([]); + + const result = await service.getOrderDetails(requestId); + expect(result).toEqual([]); + expect(mockOrdersRepository.find).toHaveBeenCalledWith({ + where: { requestId }, + relations: { + foodManufacturer: true, + allocations: { + item: true, + }, + }, }); }); }); @@ -166,7 +302,7 @@ describe('RequestsService', () => { dateReceived: null, feedback: null, photos: null, - order: null, + orders: null, }, { requestId: 3, @@ -178,7 +314,7 @@ describe('RequestsService', () => { dateReceived: null, feedback: null, photos: null, - order: null, + orders: null, }, ]; const pantryId = 1; @@ -191,7 +327,7 @@ describe('RequestsService', () => { expect(result).toEqual(mockRequests.slice(0, 2)); expect(mockRequestsRepository.find).toHaveBeenCalledWith({ where: { pantryId }, - relations: ['order'], + relations: ['orders'], }); }); }); @@ -213,7 +349,7 @@ describe('RequestsService', () => { const mockRequest2: Partial = { ...mockRequest, - order: mockOrder as Order, + orders: [mockOrder] as Order[], }; const requestId = 1; @@ -224,15 +360,15 @@ describe('RequestsService', () => { mockRequestsRepository.findOne.mockResolvedValueOnce( mockRequest2 as FoodRequest, ); + + const updatedOrder = { ...mockOrder, status: OrderStatus.DELIVERED }; + mockRequestsRepository.save.mockResolvedValueOnce({ ...mockRequest, dateReceived: deliveryDate, feedback, photos, - order: { - ...(mockOrder as Order), - status: OrderStatus.DELIVERED, - } as Order, + orders: [updatedOrder], } as FoodRequest); const result = await service.updateDeliveryDetails( @@ -247,12 +383,12 @@ describe('RequestsService', () => { dateReceived: deliveryDate, feedback, photos, - order: { ...mockOrder, status: 'delivered' }, + orders: [updatedOrder], }); expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); expect(mockRequestsRepository.save).toHaveBeenCalledWith({ @@ -260,7 +396,7 @@ describe('RequestsService', () => { dateReceived: deliveryDate, feedback, photos, - order: { ...mockOrder, status: 'delivered' }, + orders: [mockOrder], }); }); @@ -283,7 +419,7 @@ describe('RequestsService', () => { expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); }); @@ -304,11 +440,11 @@ describe('RequestsService', () => { feedback, photos, ), - ).rejects.toThrow('No associated order found for this request'); + ).rejects.toThrow('No associated orders found for this request'); expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); }); @@ -327,7 +463,7 @@ describe('RequestsService', () => { }; const mockRequest2: Partial = { ...mockRequest, - order: mockOrder as Order, + orders: [mockOrder] as Order[], }; const requestId = 1; @@ -346,11 +482,13 @@ describe('RequestsService', () => { feedback, photos, ), - ).rejects.toThrow('No associated food manufacturer found for this order'); + ).rejects.toThrow( + 'No associated food manufacturer found for an associated order', + ); expect(mockRequestsRepository.findOne).toHaveBeenCalledWith({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); }); }); diff --git a/apps/backend/src/foodRequests/request.service.ts b/apps/backend/src/foodRequests/request.service.ts index 32e600ba..80093c58 100644 --- a/apps/backend/src/foodRequests/request.service.ts +++ b/apps/backend/src/foodRequests/request.service.ts @@ -1,21 +1,19 @@ -import { - ConflictException, - Injectable, - NotFoundException, -} from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; 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'; +import { Order } from '../orders/order.entity'; +import { OrderDetailsDto } from './dtos/order-details.dto'; @Injectable() export class RequestsService { constructor( @InjectRepository(FoodRequest) private repo: Repository, @InjectRepository(Pantry) private pantryRepo: Repository, + @InjectRepository(Order) private orderRepo: Repository, ) {} async findOne(requestId: number): Promise { @@ -23,7 +21,7 @@ export class RequestsService { const request = await this.repo.findOne({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); if (!request) { @@ -32,6 +30,49 @@ export class RequestsService { return request; } + async getOrderDetails(requestId: number): Promise { + validateId(requestId, 'Request'); + + const requestExists = await this.repo.findOne({ + where: { requestId }, + }); + + if (!requestExists) { + throw new NotFoundException(`Request ${requestId} not found`); + } + + const orders = await this.orderRepo.find({ + where: { requestId }, + relations: { + foodManufacturer: true, + allocations: { + item: true, + }, + }, + }); + + if (!orders) { + throw new NotFoundException( + 'No associated orders found for this request', + ); + } + + if (!orders.length) { + return []; + } + + return orders.map((order) => ({ + orderId: order.orderId, + status: order.status, + foodManufacturerName: order.foodManufacturer.foodManufacturerName, + items: order.allocations.map((allocation) => ({ + name: allocation.item.itemName, + quantity: allocation.allocatedQuantity, + foodType: allocation.item.foodType, + })), + })); + } + async create( pantryId: number, requestedSize: RequestSize, @@ -66,7 +107,7 @@ export class RequestsService { return await this.repo.find({ where: { pantryId }, - relations: ['order'], + relations: ['orders'], }); } @@ -80,29 +121,32 @@ export class RequestsService { const request = await this.repo.findOne({ where: { requestId }, - relations: ['order'], + relations: ['orders'], }); if (!request) { throw new NotFoundException('Invalid request ID'); } - if (!request.order) { - throw new ConflictException('No associated order found for this request'); + if (!request.orders || request.orders.length == 0) { + throw new NotFoundException( + 'No associated orders found for this request', + ); } - const order = request.order; + const orders = request.orders; - if (!order.shippedBy) { - throw new ConflictException( - 'No associated food manufacturer found for this order', - ); + for (const order of orders) { + if (!order.shippedBy) { + throw new NotFoundException( + 'No associated food manufacturer found for an associated order', + ); + } } request.feedback = feedback; request.dateReceived = deliveryDate; request.photos = photos; - request.order.status = OrderStatus.DELIVERED; return await this.repo.save(request); } diff --git a/apps/backend/src/orders/order.entity.ts b/apps/backend/src/orders/order.entity.ts index 4c38457b..7c40fdb4 100644 --- a/apps/backend/src/orders/order.entity.ts +++ b/apps/backend/src/orders/order.entity.ts @@ -5,11 +5,13 @@ import { CreateDateColumn, ManyToOne, JoinColumn, + OneToMany, } from 'typeorm'; import { FoodRequest } from '../foodRequests/request.entity'; import { Pantry } from '../pantries/pantries.entity'; import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity'; import { OrderStatus } from './types'; +import { Allocation } from '../allocations/allocations.entity'; @Entity('orders') export class Order { @@ -72,4 +74,7 @@ export class Order { nullable: true, }) deliveredAt: Date | null; + + @OneToMany(() => Allocation, (allocation) => allocation.order) + allocations: Allocation[]; } diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index f62efd8f..98f41493 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -11,6 +11,7 @@ import { Pantry, PantryApplicationDto, UserDto, + OrderDetails, } from 'types/types'; const defaultBaseUrl = @@ -190,12 +191,20 @@ export class ApiClient { } public async getOrder(orderId: number): Promise { - return this.axiosInstance.get(`api/orders/${orderId}`) as Promise; + return this.axiosInstance.get(`/api/orders/${orderId}`) as Promise; + } + + public async getOrderDetailsListFromRequest( + requestId: number, + ): Promise { + return this.axiosInstance + .get(`/api/requests/all-order-details/${requestId}`) + .then((response) => response.data) as Promise; } async getAllAllocationsByOrder(orderId: number): Promise { return this.axiosInstance - .get(`api/orders/${orderId}/allocations`) + .get(`/api/orders/${orderId}/allocations`) .then((response) => response.data); } diff --git a/apps/frontend/src/components/forms/requestFormModal.tsx b/apps/frontend/src/components/forms/requestFormModal.tsx index 348cb247..f54e625e 100644 --- a/apps/frontend/src/components/forms/requestFormModal.tsx +++ b/apps/frontend/src/components/forms/requestFormModal.tsx @@ -14,11 +14,9 @@ import { import { Form, ActionFunction, ActionFunctionArgs } from 'react-router-dom'; import { FoodRequest, FoodTypes, RequestSize } from '../../types/types'; import { ChevronDownIcon } from 'lucide-react'; -import apiClient from '@api/apiClient'; interface FoodRequestFormModalProps { previousRequest?: FoodRequest; - readOnly?: boolean; isOpen: boolean; onClose: () => void; pantryId: number; @@ -26,7 +24,6 @@ interface FoodRequestFormModalProps { const FoodRequestFormModal: React.FC = ({ previousRequest, - readOnly = false, isOpen, onClose, pantryId, @@ -34,7 +31,6 @@ const FoodRequestFormModal: React.FC = ({ const [selectedItems, setSelectedItems] = useState([]); const [requestedSize, setRequestedSize] = useState(''); const [additionalNotes, setAdditionalNotes] = useState(''); - const [pantryName, setPantryName] = useState(''); const isFormValid = requestedSize !== '' && selectedItems.length > 0; @@ -49,18 +45,6 @@ const FoodRequestFormModal: React.FC = ({ } }, [isOpen, previousRequest]); - useEffect(() => { - const fetchData = async () => { - try { - const pantry = await apiClient.getPantry(pantryId); - setPantryName(pantry.pantryName); - } catch (error) { - console.error('Error fetching pantry data', error); - } - }; - fetchData(); - }); - return ( = ({ - {readOnly - ? `Order ${previousRequest?.requestId}` - : previousRequest - ? 'Resubmit Latest Order' - : 'New Food Request'} + {previousRequest ? 'Resubmit Latest Request' : 'New Food Request'} - {readOnly && ( - - {pantryName} - - )} = ({ pt={0} mt={0} > - {readOnly && previousRequest - ? `Requested ${new Date( - previousRequest.requestedAt, - ).toLocaleDateString()}` - : previousRequest - ? 'Confirm order details.' + {previousRequest + ? 'Confirm request details.' : `Please keep in mind that we may not be able to accommodate specific food requests at all times, but we will do our best to match your preferences.`} @@ -130,7 +101,6 @@ const FoodRequestFormModal: React.FC = ({ @@ -186,73 +156,69 @@ const FoodRequestFormModal: React.FC = ({ /> ))} - {!readOnly && ( - - - - + + + + - - - {FoodTypes.map((allergen) => { - const isChecked = selectedItems.includes(allergen); - return ( - { - setSelectedItems((prev) => - checked - ? [...prev, allergen] - : prev.filter((i) => i !== allergen), - ); - }} - disabled={readOnly} - display="flex" - alignItems="center" + + + {FoodTypes.map((allergen) => { + const isChecked = selectedItems.includes(allergen); + return ( + { + setSelectedItems((prev) => + checked + ? [...prev, allergen] + : prev.filter((i) => i !== allergen), + ); + }} + display="flex" + alignItems="center" + > + + + - - - - {allergen} - - - ); - })} - - - - )} + {allergen} + + + ); + })} + + + {selectedItems.length > 0 && ( @@ -261,27 +227,26 @@ const FoodRequestFormModal: React.FC = ({ key={item} size="xl" variant="solid" - bg={!readOnly ? '#E9F4F6' : 'neutral.100'} + bg={'neutral.100'} color="neutral.800" borderRadius="4px" - borderColor={!readOnly ? 'teal.400' : 'neutral.300'} + borderColor={'neutral.300'} borderWidth="1px" fontFamily="Inter" fontWeight={500} > {item} - {!readOnly && ( - - - setSelectedItems((prev) => - prev.filter((i) => i !== item), - ) - } - /> - - )} + + + + setSelectedItems((prev) => + prev.filter((i) => i !== item), + ) + } + /> + ))} @@ -317,41 +282,36 @@ const FoodRequestFormModal: React.FC = ({ alert('Exceeded word limit'); } }} - disabled={readOnly} /> - {!readOnly && ( - - Max 250 words - - )} + + + Max 250 words + - {!readOnly && ( - - )} - {!readOnly && ( - - )} + + + - {readOnly && } + diff --git a/apps/frontend/src/containers/FormRequests.tsx b/apps/frontend/src/containers/FormRequests.tsx index 50d20601..65d000cd 100644 --- a/apps/frontend/src/containers/FormRequests.tsx +++ b/apps/frontend/src/containers/FormRequests.tsx @@ -106,7 +106,6 @@ const FormRequests: React.FC = () => { { - {request.order?.orderId ? ( + {request.orders?.[0]?.orderId ? ( ) : ( 'N/A' )} {formatDate(request.requestedAt)} - {request.order?.status ?? 'pending'} - {request.order?.status === 'pending' + {request.orders?.[0]?.status ?? 'pending'} + + + {request.orders?.[0]?.status === 'pending' ? 'N/A' - : request.order?.shippedBy ?? 'N/A'} + : request.orders?.[0]?.shippedBy ?? 'N/A'} {formatReceivedDate(request.dateReceived)} - {!request.order || request.order?.status === 'pending' ? ( + {!request.orders?.[0] || + request.orders?.[0]?.status === 'pending' ? ( Awaiting Order Assignment - ) : request.order?.status === 'delivered' ? ( + ) : request.orders?.[0]?.status === 'delivered' ? ( Food Request is Already Delivered ) : (