From 6cedd26731c658c28cae89fcd2fa816e03f82af0 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 26 Mar 2025 18:09:46 -0400 Subject: [PATCH 1/8] frontend component skeleton --- apps/frontend/src/app.tsx | 5 + .../containers/foodManufacturerDashboard.tsx | 131 ++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 apps/frontend/src/containers/foodManufacturerDashboard.tsx diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index e56b4029..50c8ff8f 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -17,6 +17,7 @@ import PantryApplication from '@containers/pantryApplication'; import { submitPantryApplicationForm } from '@components/forms/pantryApplicationForm'; import ApprovePantries from '@containers/approvePantries'; import DonationManagement from '@containers/donationManagement'; +import FoodManufacturerDashboard from '@containers/foodManufacturerDashboard'; const router = createBrowserRouter([ { @@ -73,6 +74,10 @@ const router = createBrowserRouter([ path: '/approve-pantries', element: , }, + { + path: '/food-manufacturer-dashboard', + element: , + }, ], }, ]); diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx new file mode 100644 index 00000000..6ecfb967 --- /dev/null +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -0,0 +1,131 @@ +import { + Menu, + Button, + MenuButton, + MenuList, + MenuItem, + Link, + Image, + Card, + CardHeader, + CardBody, + Heading, + Text, + Box, + Select, + Stack, +} from '@chakra-ui/react'; +import { HamburgerIcon } from '@chakra-ui/icons'; + +const FoodManufacturerDashboard: React.FC = () => { + const HamburgerMenu = () => { + return ( + + + + + + + Profile + + + Donation Management + + + Orders + + + Donation Statistics + + + Sign Out + + + + ); + }; + + const ManufacturerCard = () => { + return ( + + + Client Report + + + + + + + + + + ); + }; + + const ManufacturerDetailsBox = () => { + return ( + + Details + + ); + }; + + const UpdateFrequencyBox = () => { + return ( + + Frequency + + + + ); + }; + + return ( + <> + + + + + Icon + + + + ); +}; + +export default FoodManufacturerDashboard; From 8bf822bd09f501311146a6f15b16db9a00cebdfe Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Thu, 27 Mar 2025 16:18:52 -0400 Subject: [PATCH 2/8] initial backend for manufacturer --- .../manufacturer.controller.ts | 15 +++++++++++++ .../foodManufacturers/manufacturer.module.ts | 6 ++++++ .../foodManufacturers/manufacturer.service.ts | 21 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 apps/backend/src/foodManufacturers/manufacturer.controller.ts create mode 100644 apps/backend/src/foodManufacturers/manufacturer.service.ts diff --git a/apps/backend/src/foodManufacturers/manufacturer.controller.ts b/apps/backend/src/foodManufacturers/manufacturer.controller.ts new file mode 100644 index 00000000..d701b633 --- /dev/null +++ b/apps/backend/src/foodManufacturers/manufacturer.controller.ts @@ -0,0 +1,15 @@ +import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { FoodManufacturer } from './manufacturer.entity'; +import { ManufacturerService } from './manufacturer.service'; + +@Controller('manufacturer') +export class ManufacturerController { + constructor(private manufacturerService: ManufacturerService) {} + + @Get('/get/:manufacturerId') + async getManufacturer( + @Param('manufacturerId', ParseIntPipe) manufacturerId: number, + ): Promise { + return this.manufacturerService.get(manufacturerId); + } +} diff --git a/apps/backend/src/foodManufacturers/manufacturer.module.ts b/apps/backend/src/foodManufacturers/manufacturer.module.ts index 2ba2b117..21fef323 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.module.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.module.ts @@ -1,8 +1,14 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { FoodManufacturer } from './manufacturer.entity'; +import { ManufacturerController } from './manufacturer.controller'; +import { ManufacturerService } from './manufacturer.service'; +import { JwtStrategy } from '../auth/jwt.strategy'; +import { AuthService } from '../auth/auth.service'; @Module({ imports: [TypeOrmModule.forFeature([FoodManufacturer])], + controllers: [ManufacturerController], + providers: [ManufacturerService, AuthService, JwtStrategy], }) export class ManufacturerModule {} diff --git a/apps/backend/src/foodManufacturers/manufacturer.service.ts b/apps/backend/src/foodManufacturers/manufacturer.service.ts new file mode 100644 index 00000000..4ad62fae --- /dev/null +++ b/apps/backend/src/foodManufacturers/manufacturer.service.ts @@ -0,0 +1,21 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { FoodManufacturer } from './manufacturer.entity'; + +@Injectable() +export class ManufacturerService { + constructor( + @InjectRepository(FoodManufacturer) + private repo: Repository, + ) {} + + async get(manufacturerId: number) { + if (!manufacturerId || manufacturerId < 1) { + throw new NotFoundException('Invalid manufacturer ID'); + } + return await this.repo.find({ + where: { foodManufacturerId: manufacturerId }, + }); + } +} From 4d8f66a0e046f494034488a511b006fba4ab8268 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Fri, 28 Mar 2025 15:30:49 -0400 Subject: [PATCH 3/8] frontend design finalized --- .../containers/foodManufacturerDashboard.tsx | 96 ++++++++++++++++--- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index 6ecfb967..ab601e0b 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -14,6 +14,8 @@ import { Box, Select, Stack, + HStack, + VStack, } from '@chakra-ui/react'; import { HamburgerIcon } from '@chakra-ui/icons'; @@ -78,13 +80,15 @@ const FoodManufacturerDashboard: React.FC = () => { const ManufacturerCard = () => { return ( - + - Client Report + + Welcome to Food Manufacturer Admin page - FOODMANID + - + @@ -95,22 +99,88 @@ const FoodManufacturerDashboard: React.FC = () => { const ManufacturerDetailsBox = () => { return ( - - Details + + About Manufacturer 1: +
+ + + + Assigned SSF Contact: + + + Pantry Partner since + + + + + + Total donations: + + + Manufacturer Industry: + + + + + + Email Address: + + + Phone Number: + + + + + + Address for Food Shipments: + + +
); }; const UpdateFrequencyBox = () => { return ( - - Frequency - - + + Update Frequency of Donations +
+ Current Frequency: x donations a month + + + A donation + + + +
); }; From 4bb76ac690fa5c98238db4ca793b9927710a49f8 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Tue, 1 Apr 2025 15:37:53 -0400 Subject: [PATCH 4/8] adding new manufacturer details to db, setting up route to display details to frontend, --- apps/backend/src/config/typeorm.ts | 4 ++- .../manufacturer.controller.ts | 8 ++--- .../foodManufacturers/manufacturer.entity.ts | 15 ++++++++++ .../foodManufacturers/manufacturer.service.ts | 18 ++++++++++-- .../1743518493960-AddManufacturerDetails.ts | 25 ++++++++++++++++ apps/frontend/src/api/apiClient.ts | 16 +++++++++- apps/frontend/src/app.tsx | 2 +- .../containers/foodManufacturerDashboard.tsx | 29 +++++++++++++++++++ apps/frontend/src/types/types.ts | 14 +++++++++ 9 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 apps/backend/src/migrations/1743518493960-AddManufacturerDetails.ts diff --git a/apps/backend/src/config/typeorm.ts b/apps/backend/src/config/typeorm.ts index ea9ddf9d..4deffe7b 100644 --- a/apps/backend/src/config/typeorm.ts +++ b/apps/backend/src/config/typeorm.ts @@ -11,6 +11,8 @@ import { UpdateDonationColTypes1741708808976 } from '../migrations/1741708808976 import { UpdatePantriesTable1738172265266 } from '../migrations/1738172265266-updatePantriesTable'; import { UpdatePantriesTable1739056029076 } from '../migrations/1739056029076-updatePantriesTable'; import { AddOrders1739496585940 } from '../migrations/1739496585940-addOrders'; +import { AddManufacturerDetails1743518493960 } from '../migrations/1743518493960-AddManufacturerDetails'; + const config = { type: 'postgres', host: `${process.env.DATABASE_HOST}`, @@ -33,8 +35,8 @@ const config = { UpdateDonationColTypes1741708808976, UpdatePantriesTable1738172265266, UpdatePantriesTable1739056029076, - UpdateDonations1738697216020, AddOrders1739496585940, + AddManufacturerDetails1743518493960, ], }; diff --git a/apps/backend/src/foodManufacturers/manufacturer.controller.ts b/apps/backend/src/foodManufacturers/manufacturer.controller.ts index d701b633..db328ad1 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.controller.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.controller.ts @@ -6,10 +6,10 @@ import { ManufacturerService } from './manufacturer.service'; export class ManufacturerController { constructor(private manufacturerService: ManufacturerService) {} - @Get('/get/:manufacturerId') - async getManufacturer( + @Get('/getDetails/:manufacturerId') + async getManufacturerDetails( @Param('manufacturerId', ParseIntPipe) manufacturerId: number, - ): Promise { - return this.manufacturerService.get(manufacturerId); + ): Promise { + return this.manufacturerService.getDetails(manufacturerId); } } diff --git a/apps/backend/src/foodManufacturers/manufacturer.entity.ts b/apps/backend/src/foodManufacturers/manufacturer.entity.ts index efbcc71a..e75e48ee 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.entity.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.entity.ts @@ -22,4 +22,19 @@ export class FoodManufacturer { referencedColumnName: 'id', }) foodManufacturerRepresentative: User; + + @Column({ type: 'varchar', length: 255, nullable: true }) + industry: string; + + @Column({ type: 'varchar', length: 255, nullable: true }) + email: string; + + @Column({ type: 'varchar', length: 255, nullable: true }) + phone: string; + + @Column({ type: 'varchar', length: 255, nullable: true }) + address: string; + + @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) + signupDate: Date; } diff --git a/apps/backend/src/foodManufacturers/manufacturer.service.ts b/apps/backend/src/foodManufacturers/manufacturer.service.ts index 4ad62fae..5292c584 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.service.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.service.ts @@ -10,12 +10,26 @@ export class ManufacturerService { private repo: Repository, ) {} - async get(manufacturerId: number) { + async getDetails(manufacturerId: number): Promise { if (!manufacturerId || manufacturerId < 1) { throw new NotFoundException('Invalid manufacturer ID'); } - return await this.repo.find({ + return await this.repo.findOne({ where: { foodManufacturerId: manufacturerId }, + relations: ['foodManufacturerRepresentative'], + select: { + foodManufacturerId: true, + foodManufacturerName: true, + industry: true, + email: true, + phone: true, + address: true, + signupDate: true, + foodManufacturerRepresentative: { + firstName: true, + lastName: true, + }, + }, }); } } diff --git a/apps/backend/src/migrations/1743518493960-AddManufacturerDetails.ts b/apps/backend/src/migrations/1743518493960-AddManufacturerDetails.ts new file mode 100644 index 00000000..4168c0ac --- /dev/null +++ b/apps/backend/src/migrations/1743518493960-AddManufacturerDetails.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddManufacturerDetails1743518493960 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE food_manufacturers + ADD COLUMN industry VARCHAR(255), + ADD COLUMN email VARCHAR(255), + ADD COLUMN phone VARCHAR(255), + ADD COLUMN address VARCHAR(255), + ADD COLUMN signup_date TIMESTAMP NOT NULL DEFAULT NOW(); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE food_manufacturers + DROP COLUMN industry, + DROP COLUMN email, + DROP COLUMN phone, + DROP COLUMN address, + DROP COLUMN signup_date; + `); + } +} diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index bbd46f0b..2945e327 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -1,5 +1,11 @@ import axios, { type AxiosInstance } from 'axios'; -import { Donation, DonationItem, User, Pantry } from 'types/types'; +import { + Donation, + DonationItem, + User, + Pantry, + ManufacturerDetails, +} from 'types/types'; const defaultBaseUrl = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:3000'; @@ -31,6 +37,14 @@ export class ApiClient { .then((response) => response.data); } + public async getManufacturerDetails( + manufacturerId: number, + ): Promise { + return this.axiosInstance.get( + `/api/manufacturer/getDetails/${manufacturerId}`, + ) as Promise; + } + public async updatePantry( pantryId: number, decision: 'approve' | 'deny', diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index 50c8ff8f..5b9c4e87 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -75,7 +75,7 @@ const router = createBrowserRouter([ element: , }, { - path: '/food-manufacturer-dashboard', + path: '/food-manufacturer-dashboard/:manufacturerId', element: , }, ], diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index ab601e0b..13e6dc67 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -18,8 +18,37 @@ import { VStack, } from '@chakra-ui/react'; import { HamburgerIcon } from '@chakra-ui/icons'; +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import ApiClient from '@api/apiClient'; +import { ManufacturerDetails } from 'types/types'; const FoodManufacturerDashboard: React.FC = () => { + const { manufacturerId } = useParams<{ manufacturerId: string }>(); + const [manufacturerDetails, setManufacturerDetails] = + useState(); + + useEffect(() => { + if (!manufacturerId) { + console.error('Error: manufacturerId is undefined'); + return; + } + + const fetchDetails = async () => { + try { + const details = await ApiClient.getManufacturerDetails( + parseInt(manufacturerId, 10), + ); + console.log(details); + setManufacturerDetails(details); + } catch (error) { + console.error('Error fetching manufacturer details: ', error); + } + }; + + fetchDetails(); + }, [manufacturerId]); + const HamburgerMenu = () => { return ( diff --git a/apps/frontend/src/types/types.ts b/apps/frontend/src/types/types.ts index 39ca94a2..e02586e7 100644 --- a/apps/frontend/src/types/types.ts +++ b/apps/frontend/src/types/types.ts @@ -80,3 +80,17 @@ export interface FoodRequest { requestedItems: string[]; additionalInformation: string; } + +export interface ManufacturerDetails { + foodManufacturerId: number; + foodManufacturerName: string; + industry: string; + email: string; + phone: string; + address: string; + signupDate: Date; + foodManufacturerRepresentative: { + firstName: string; + lastName: string; + }; +} From 79dc2b497cdb95a41c964e16ea810cc4f96aa002 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Wed, 2 Apr 2025 17:41:55 -0400 Subject: [PATCH 5/8] enabling update of manufacturer donation frequency --- apps/backend/src/config/typeorm.ts | 2 + .../manufacturer.controller.ts | 13 ++- .../foodManufacturers/manufacturer.entity.ts | 3 + .../foodManufacturers/manufacturer.service.ts | 15 ++++ ...272909-AddManufacturerDonationFrequency.ts | 19 ++++ apps/frontend/src/api/apiClient.ts | 17 +++- .../containers/foodManufacturerDashboard.tsx | 87 ++++++++++++++----- apps/frontend/src/types/types.ts | 1 + 8 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 apps/backend/src/migrations/1743623272909-AddManufacturerDonationFrequency.ts diff --git a/apps/backend/src/config/typeorm.ts b/apps/backend/src/config/typeorm.ts index 4deffe7b..837b2162 100644 --- a/apps/backend/src/config/typeorm.ts +++ b/apps/backend/src/config/typeorm.ts @@ -12,6 +12,7 @@ import { UpdatePantriesTable1738172265266 } from '../migrations/1738172265266-up import { UpdatePantriesTable1739056029076 } from '../migrations/1739056029076-updatePantriesTable'; import { AddOrders1739496585940 } from '../migrations/1739496585940-addOrders'; import { AddManufacturerDetails1743518493960 } from '../migrations/1743518493960-AddManufacturerDetails'; +import { AddManufacturerDonationFrequency1743623272909 } from '../migrations/1743623272909-AddManufacturerDonationFrequency'; const config = { type: 'postgres', @@ -37,6 +38,7 @@ const config = { UpdatePantriesTable1739056029076, AddOrders1739496585940, AddManufacturerDetails1743518493960, + AddManufacturerDonationFrequency1743623272909, ], }; diff --git a/apps/backend/src/foodManufacturers/manufacturer.controller.ts b/apps/backend/src/foodManufacturers/manufacturer.controller.ts index db328ad1..406186de 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.controller.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'; +import { Controller, Get, Param, ParseIntPipe, Patch } from '@nestjs/common'; import { FoodManufacturer } from './manufacturer.entity'; import { ManufacturerService } from './manufacturer.service'; @@ -12,4 +12,15 @@ export class ManufacturerController { ): Promise { return this.manufacturerService.getDetails(manufacturerId); } + + @Patch('/updateFrequency/:manufacturerId/:frequency') + async updateManufacturerFrequency( + @Param('manufacturerId') manufacturerId: number, + @Param('frequency') frequency: string, + ): Promise { + return this.manufacturerService.updateManufacturerFrequency( + manufacturerId, + frequency, + ); + } } diff --git a/apps/backend/src/foodManufacturers/manufacturer.entity.ts b/apps/backend/src/foodManufacturers/manufacturer.entity.ts index e75e48ee..0dc0bafc 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.entity.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.entity.ts @@ -37,4 +37,7 @@ export class FoodManufacturer { @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) signupDate: Date; + + @Column({ type: 'varchar', length: 255, nullable: true }) + donationFrequency: string; } diff --git a/apps/backend/src/foodManufacturers/manufacturer.service.ts b/apps/backend/src/foodManufacturers/manufacturer.service.ts index 5292c584..2f7abbcd 100644 --- a/apps/backend/src/foodManufacturers/manufacturer.service.ts +++ b/apps/backend/src/foodManufacturers/manufacturer.service.ts @@ -10,6 +10,20 @@ export class ManufacturerService { private repo: Repository, ) {} + async updateManufacturerFrequency( + manufacturerId: number, + donationFrequency: string, + ): Promise { + const manufacturer = await this.repo.findOne({ + where: { foodManufacturerId: manufacturerId }, + }); + if (!manufacturer) { + return null; + } + manufacturer.donationFrequency = donationFrequency; + return this.repo.save(manufacturer); + } + async getDetails(manufacturerId: number): Promise { if (!manufacturerId || manufacturerId < 1) { throw new NotFoundException('Invalid manufacturer ID'); @@ -25,6 +39,7 @@ export class ManufacturerService { phone: true, address: true, signupDate: true, + donationFrequency: true, foodManufacturerRepresentative: { firstName: true, lastName: true, diff --git a/apps/backend/src/migrations/1743623272909-AddManufacturerDonationFrequency.ts b/apps/backend/src/migrations/1743623272909-AddManufacturerDonationFrequency.ts new file mode 100644 index 00000000..c32f5cbf --- /dev/null +++ b/apps/backend/src/migrations/1743623272909-AddManufacturerDonationFrequency.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddManufacturerDonationFrequency1743623272909 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE food_manufacturers + ADD COLUMN donation_frequency VARCHAR(255); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE food_manufacturers + DROP COLUMN donation_frequency; + `); + } +} diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 2945e327..6fb331ba 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -40,9 +40,9 @@ export class ApiClient { public async getManufacturerDetails( manufacturerId: number, ): Promise { - return this.axiosInstance.get( - `/api/manufacturer/getDetails/${manufacturerId}`, - ) as Promise; + return this.axiosInstance + .get(`/api/manufacturer/getDetails/${manufacturerId}`) + .then((response) => response.data) as Promise; } public async updatePantry( @@ -95,6 +95,17 @@ export class ApiClient { ) as Promise; } + public async updateDonationFrequency( + manufacturerId: number, + frequency: string, + body?: unknown, + ): Promise { + return this.patch( + `/api/manufacturer/updateFrequency/${manufacturerId}/${frequency}`, + body, + ) as Promise; + } + public async updateDonationItemQuantity( itemId: number, body?: unknown, diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index 13e6dc67..2760b0db 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -22,11 +22,14 @@ import React, { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import ApiClient from '@api/apiClient'; import { ManufacturerDetails } from 'types/types'; +import apiClient from '@api/apiClient'; const FoodManufacturerDashboard: React.FC = () => { const { manufacturerId } = useParams<{ manufacturerId: string }>(); const [manufacturerDetails, setManufacturerDetails] = useState(); + const [currentSelectedFrequency, setCurrentSelectedFrequency] = + useState(); useEffect(() => { if (!manufacturerId) { @@ -36,11 +39,11 @@ const FoodManufacturerDashboard: React.FC = () => { const fetchDetails = async () => { try { - const details = await ApiClient.getManufacturerDetails( + const response = await ApiClient.getManufacturerDetails( parseInt(manufacturerId, 10), ); - console.log(details); - setManufacturerDetails(details); + setManufacturerDetails(response); + setCurrentSelectedFrequency(response.donationFrequency); } catch (error) { console.error('Error fetching manufacturer details: ', error); } @@ -49,6 +52,26 @@ const FoodManufacturerDashboard: React.FC = () => { fetchDetails(); }, [manufacturerId]); + const handleUpdate = async () => { + try { + if (manufacturerId && currentSelectedFrequency) { + const response = await ApiClient.updateDonationFrequency( + parseInt(manufacturerId, 10), + currentSelectedFrequency, + ); + alert('update frequency successful'); + } + } catch (error) { + console.error('Error updating manufacturer frequency: ', error); + } + }; + + const handleFrequencyChange = ( + event: React.ChangeEvent, + ) => { + setCurrentSelectedFrequency(event.target.value); + }; + const HamburgerMenu = () => { return ( @@ -112,7 +135,8 @@ const FoodManufacturerDashboard: React.FC = () => { - Welcome to Food Manufacturer Admin page - FOODMANID + Welcome to Food Manufacturer Admin page -{' '} + {manufacturerDetails?.foodManufacturerName} @@ -129,39 +153,52 @@ const FoodManufacturerDashboard: React.FC = () => { const ManufacturerDetailsBox = () => { return ( - About Manufacturer 1: + + About Manufacturer {manufacturerDetails?.foodManufacturerName} +
- + - Assigned SSF Contact: + + Assigned SSF Contact:{' '} + {manufacturerDetails?.foodManufacturerRepresentative.firstName}{' '} + {manufacturerDetails?.foodManufacturerRepresentative.lastName} + - - Pantry Partner since + + + Pantry Partner since{' '} + {manufacturerDetails?.signupDate.toString().substring(0, 4)} + - + Total donations: - - Manufacturer Industry: + + + Manufacturer Industry: {manufacturerDetails?.industry} + - + - Email Address: + Email Address: {manufacturerDetails?.email} - - Phone Number: + + Phone Number: {manufacturerDetails?.phone} - Address for Food Shipments: + + Address for Food Shipments: {manufacturerDetails?.address} + @@ -183,7 +220,11 @@ const FoodManufacturerDashboard: React.FC = () => { > Update Frequency of Donations
- Current Frequency: x donations a month +

+ {' '} + Current Frequency: x donations{' '} + {manufacturerDetails?.donationFrequency}{' '} +

{ justifyContent="center" > A donation - @@ -209,7 +254,9 @@ const FoodManufacturerDashboard: React.FC = () => { - +
); }; diff --git a/apps/frontend/src/types/types.ts b/apps/frontend/src/types/types.ts index e02586e7..1e24db16 100644 --- a/apps/frontend/src/types/types.ts +++ b/apps/frontend/src/types/types.ts @@ -89,6 +89,7 @@ export interface ManufacturerDetails { phone: string; address: string; signupDate: Date; + donationFrequency: string; foodManufacturerRepresentative: { firstName: string; lastName: string; From 5058e5cc777fafb592c83cebf20b2a97f17989b3 Mon Sep 17 00:00:00 2001 From: Justin Wang Date: Thu, 3 Apr 2025 21:52:21 -0400 Subject: [PATCH 6/8] adding support for number of donations of manufacturer --- apps/backend/src/donations/donations.controller.ts | 8 ++++++++ apps/backend/src/donations/donations.service.ts | 7 +++++++ apps/frontend/src/api/apiClient.ts | 8 ++++++++ .../src/containers/foodManufacturerDashboard.tsx | 13 ++++++++++--- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/donations/donations.controller.ts b/apps/backend/src/donations/donations.controller.ts index cdeb268a..cced0079 100644 --- a/apps/backend/src/donations/donations.controller.ts +++ b/apps/backend/src/donations/donations.controller.ts @@ -6,6 +6,7 @@ import { Patch, Param, NotFoundException, + ParseIntPipe, } from '@nestjs/common'; import { ApiBody } from '@nestjs/swagger'; import { Donation } from './donations.entity'; @@ -20,6 +21,13 @@ export class DonationsController { return this.donationService.getAll(); } + @Get('/getManufacturerDonationCount/:manufacturerId') + async getManufacturerDonationCount( + @Param('manufacturerId', ParseIntPipe) manufacturerId: number, + ): Promise { + return this.donationService.getManufacturerDonationCount(manufacturerId); + } + @Post('/create') @ApiBody({ description: 'Details for creating a donation', diff --git a/apps/backend/src/donations/donations.service.ts b/apps/backend/src/donations/donations.service.ts index 2058375e..95e1959a 100644 --- a/apps/backend/src/donations/donations.service.ts +++ b/apps/backend/src/donations/donations.service.ts @@ -11,6 +11,13 @@ export class DonationService { return this.repo.find(); } + async getManufacturerDonationCount(manufacturerId: number) { + const count = await this.repo.count({ + where: { foodManufacturerId: manufacturerId }, + }); + return count; + } + async create( foodManufacturerId: number, dateDonated: Date, diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 6fb331ba..40975c54 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -58,6 +58,14 @@ export class ApiClient { return this.get(`/api/pantries/${pantryId}`) as Promise; } + public async getManufacturerDonationCount( + manufacturerId: number, + ): Promise { + return this.get( + `/api/donations/getManufacturerDonationCount/${manufacturerId}`, + ) as Promise; + } + public async getPantrySSFRep(pantryId: number): Promise { return this.get(`/api/pantries/${pantryId}/ssf-contact`) as Promise; } diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index 2760b0db..ce589c64 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -30,6 +30,7 @@ const FoodManufacturerDashboard: React.FC = () => { useState(); const [currentSelectedFrequency, setCurrentSelectedFrequency] = useState(); + const [manufacturerDonations, setManufacturerDonations] = useState(); useEffect(() => { if (!manufacturerId) { @@ -44,6 +45,11 @@ const FoodManufacturerDashboard: React.FC = () => { ); setManufacturerDetails(response); setCurrentSelectedFrequency(response.donationFrequency); + + const numberDonations = await ApiClient.getManufacturerDonationCount( + parseInt(manufacturerId, 10), + ); + setManufacturerDonations(numberDonations); } catch (error) { console.error('Error fetching manufacturer details: ', error); } @@ -69,7 +75,7 @@ const FoodManufacturerDashboard: React.FC = () => { const handleFrequencyChange = ( event: React.ChangeEvent, ) => { - setCurrentSelectedFrequency(event.target.value); + setCurrentSelectedFrequency('' + event.target.value); }; const HamburgerMenu = () => { @@ -176,7 +182,7 @@ const FoodManufacturerDashboard: React.FC = () => { - Total donations: + Total donations: {manufacturerDonations} @@ -243,8 +249,9 @@ const FoodManufacturerDashboard: React.FC = () => { A donation Date: Mon, 15 Sep 2025 22:24:18 -0400 Subject: [PATCH 8/8] Handling for manufacturer missing, refactoring to reload on freq change, utilizing get helper for getManufacturerDetails --- apps/frontend/src/api/apiClient.ts | 6 ++-- .../containers/foodManufacturerDashboard.tsx | 28 ++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 40975c54..eff64ccf 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -40,9 +40,9 @@ export class ApiClient { public async getManufacturerDetails( manufacturerId: number, ): Promise { - return this.axiosInstance - .get(`/api/manufacturer/getDetails/${manufacturerId}`) - .then((response) => response.data) as Promise; + return this.get( + `/api/manufacturer/getDetails/${manufacturerId}`, + ) as Promise; } public async updatePantry( diff --git a/apps/frontend/src/containers/foodManufacturerDashboard.tsx b/apps/frontend/src/containers/foodManufacturerDashboard.tsx index e1ee6af4..786c53fb 100644 --- a/apps/frontend/src/containers/foodManufacturerDashboard.tsx +++ b/apps/frontend/src/containers/foodManufacturerDashboard.tsx @@ -30,6 +30,7 @@ const FoodManufacturerDashboard: React.FC = () => { const [currentSelectedFrequency, setCurrentSelectedFrequency] = useState(); const [manufacturerDonations, setManufacturerDonations] = useState(); + const [notFound, setNotFound] = useState(); useEffect(() => { if (!manufacturerId) { @@ -42,6 +43,12 @@ const FoodManufacturerDashboard: React.FC = () => { const response = await ApiClient.getManufacturerDetails( parseInt(manufacturerId, 10), ); + + if (!response) { + setNotFound(true); + return; + } + if (response?.signupDate) { response.signupDate = new Date(response.signupDate); } @@ -54,6 +61,7 @@ const FoodManufacturerDashboard: React.FC = () => { setManufacturerDonations(numberDonations); } catch (error) { console.error('Error fetching manufacturer details: ', error); + setNotFound(true); } }; @@ -63,10 +71,17 @@ const FoodManufacturerDashboard: React.FC = () => { const handleUpdate = async () => { try { if (manufacturerId && currentSelectedFrequency) { - const response = await ApiClient.updateDonationFrequency( + await ApiClient.updateDonationFrequency( parseInt(manufacturerId, 10), currentSelectedFrequency, ); + + setManufacturerDetails((prev) => + prev + ? { ...prev, donationFrequency: currentSelectedFrequency } + : prev, + ); + alert('update frequency successful'); } } catch (error) { @@ -273,6 +288,17 @@ const FoodManufacturerDashboard: React.FC = () => { ); }; + if (notFound) { + return ( + + + Manufacturer not found + + The manufacturer you are looking for does not exist. + + ); + } + return ( <>