Skip to content
Open
4 changes: 4 additions & 0 deletions apps/backend/src/config/typeorm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { UpdateFoodRequests1744051370129 } from '../migrations/1744051370129-upd
import { UpdateRequestTable1741571847063 } from '../migrations/1741571847063-updateRequestTable';
import { RemoveOrderIdFromRequests1744133526650 } from '../migrations/1744133526650-removeOrderIdFromRequests';
import { AddOrders1739496585940 } from '../migrations/1739496585940-addOrders';
import { AddManufacturerDetails1743518493960 } from '../migrations/1743518493960-AddManufacturerDetails';
import { AddManufacturerDonationFrequency1743623272909 } from '../migrations/1743623272909-AddManufacturerDonationFrequency';
import { UpdatePantriesTable1742739750279 } from '../migrations/1742739750279-updatePantriesTable';

const config = {
Expand All @@ -42,6 +44,8 @@ const config = {
UpdatePantriesTable1739056029076,
AssignmentsPantryIdNotUnique1758384669652,
AddOrders1739496585940,
AddManufacturerDetails1743518493960,
AddManufacturerDonationFrequency1743623272909,
UpdateOrdersTable1740367964915,
UpdateRequestTable1741571847063,
UpdateFoodRequests1744051370129,
Expand Down
12 changes: 12 additions & 0 deletions apps/backend/src/donations/donations.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ export class DonationsController {
return this.donationService.findOne(donationId);
}

@Get('/get-all-donations')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have a getAllDonations

async getAllDonations(): Promise<Donation[]> {
return this.donationService.getAll();
}

@Get('/getManufacturerDonationCount/:manufacturerId')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we update this to /:manufacturerId/donationCount

async getManufacturerDonationCount(
@Param('manufacturerId', ParseIntPipe) manufacturerId: number,
): Promise<number> {
return this.donationService.getManufacturerDonationCount(manufacturerId);
}

@Post('/create')
@ApiBody({
description: 'Details for creating a donation',
Expand Down
7 changes: 7 additions & 0 deletions apps/backend/src/donations/donations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export class DonationService {
return this.repo.count();
}

async getManufacturerDonationCount(manufacturerId: number) {
const count = await this.repo.count({
where: { foodManufacturerId: manufacturerId },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be updated to reflect the entity changes

});
return count;
}

async create(
foodManufacturerId: number,
dateDonated: Date,
Expand Down
26 changes: 26 additions & 0 deletions apps/backend/src/foodManufacturers/manufacturer.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Controller, Get, Param, ParseIntPipe, Patch } from '@nestjs/common';
import { FoodManufacturer } from './manufacturer.entity';
import { ManufacturerService } from './manufacturer.service';

@Controller('manufacturer')
export class ManufacturerController {
constructor(private manufacturerService: ManufacturerService) {}

@Get('/getDetails/:manufacturerId')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we change this to /:manufacturerId/details

async getManufacturerDetails(
@Param('manufacturerId', ParseIntPipe) manufacturerId: number,
): Promise<FoodManufacturer | null> {
return this.manufacturerService.getDetails(manufacturerId);
}

@Patch('/updateFrequency/:manufacturerId/:frequency')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also swap id and update frequency in the route here too

async updateManufacturerFrequency(
@Param('manufacturerId') manufacturerId: number,
@Param('frequency') frequency: string,
): Promise<FoodManufacturer | null> {
return this.manufacturerService.updateManufacturerFrequency(
manufacturerId,
frequency,
);
}
}
17 changes: 17 additions & 0 deletions apps/backend/src/foodManufacturers/manufacturer.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ export class FoodManufacturer {
})
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;

@Column({ type: 'varchar', length: 255, nullable: true })
donationFrequency: string;
@OneToMany(() => Donation, (donation) => donation.foodManufacturer)
donations: Donation[];
}
6 changes: 6 additions & 0 deletions apps/backend/src/foodManufacturers/manufacturer.module.ts
Original file line number Diff line number Diff line change
@@ -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 {}
50 changes: 50 additions & 0 deletions apps/backend/src/foodManufacturers/manufacturer.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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<FoodManufacturer>,
) {}

async updateManufacturerFrequency(
manufacturerId: number,
donationFrequency: string,
): Promise<FoodManufacturer | null> {
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<FoodManufacturer | null> {
if (!manufacturerId || manufacturerId < 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use validate id here

throw new NotFoundException('Invalid manufacturer ID');
}
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,
donationFrequency: true,
foodManufacturerRepresentative: {
firstName: true,
lastName: true,
},
},
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddManufacturerDetails1743518493960 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
await queryRunner.query(`
ALTER TABLE food_manufacturers
DROP COLUMN industry,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add IF EXISTS to these just in case

DROP COLUMN email,
DROP COLUMN phone,
DROP COLUMN address,
DROP COLUMN signup_date;
`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddManufacturerDonationFrequency1743623272909
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE food_manufacturers
ADD COLUMN donation_frequency VARCHAR(255);
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE food_manufacturers
DROP COLUMN donation_frequency;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IF EXISTS here too

`);
}
}
61 changes: 59 additions & 2 deletions apps/frontend/src/api/apiClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import axios, { type AxiosInstance, AxiosResponse } from 'axios';
import {
Donation,
DonationItem,
User,
Pantry,
ManufacturerDetails,
Order,
FoodRequest,
FoodManufacturer,
DonationItem,
Donation,
Allocation,
PantryApplicationDto,
VolunteerPantryAssignment,
Expand All @@ -31,6 +32,51 @@ export class ApiClient {
return this.axiosInstance.get(path).then((response) => response.data);
}

public async getRepresentativeUser(userId: number): Promise<User> {
return this.axiosInstance
.get(`/api/users/${userId}`)
.then((response) => response.data);
}

public async getAllPendingPantries(): Promise<Pantry[]> {
return this.axiosInstance
.get('/api/pantries/pending')
.then((response) => response.data);
}

public async getManufacturerDetails(
manufacturerId: number,
): Promise<ManufacturerDetails> {
return this.get(
`/api/manufacturer/getDetails/${manufacturerId}`,
) as Promise<ManufacturerDetails>;
}

public async updatePantry(
pantryId: number,
decision: 'approve' | 'deny',
): Promise<void> {
await this.axiosInstance.post(`/api/pantries/${decision}/${pantryId}`, {
pantryId,
});
}

public async getPantry(pantryId: number): Promise<Pantry> {
return this.get(`/api/pantries/${pantryId}`) as Promise<Pantry>;
}

public async getManufacturerDonationCount(
manufacturerId: number,
): Promise<number> {
return this.get(
`/api/donations/getManufacturerDonationCount/${manufacturerId}`,
) as Promise<number>;
}

public async getPantrySSFRep(pantryId: number): Promise<User> {
return this.get(`/api/pantries/${pantryId}/ssf-contact`) as Promise<User>;
}

private async post(path: string, body: unknown): Promise<unknown> {
return this.axiosInstance
.post(path, body)
Expand Down Expand Up @@ -76,6 +122,17 @@ export class ApiClient {
) as Promise<Donation>;
}

public async updateDonationFrequency(
manufacturerId: number,
frequency: string,
body?: unknown,
): Promise<ManufacturerDetails> {
return this.patch(
`/api/manufacturer/updateFrequency/${manufacturerId}/${frequency}`,
body,
) as Promise<ManufacturerDetails>;
}

public async updateDonationItemQuantity(
itemId: number,
body?: unknown,
Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ApprovePantries from '@containers/approvePantries';
import VolunteerManagement from '@containers/volunteerManagement';
import FoodManufacturerOrderDashboard from '@containers/foodManufacturerOrderDashboard';
import DonationManagement from '@containers/donationManagement';
import FoodManufacturerDashboard from '@containers/foodManufacturerDashboard';
import Homepage from '@containers/homepage';

const router = createBrowserRouter([
Expand Down Expand Up @@ -84,6 +85,10 @@ const router = createBrowserRouter([
path: '/approve-pantries',
element: <ApprovePantries />,
},
{
path: '/food-manufacturer-dashboard/:manufacturerId',
element: <FoodManufacturerDashboard />,
},
{
path: '/volunteer-management',
element: <VolunteerManagement />,
Expand Down
Loading