feat: Implement admin pantry item management with create and update functionality, including category selection and validation
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { IsInt, IsOptional, Min } from 'class-validator';
|
||||
import { CreatePantryItemDto } from './create-pantry-item.dto';
|
||||
|
||||
export class CreateAdminPantryItemDto extends CreatePantryItemDto {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
userId?: number;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { IsInt, IsOptional, IsPositive, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
export class UpdatePantryItemDto {
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@IsPositive()
|
||||
productId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(50)
|
||||
location?: string;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Query } from '@nestjs/common';
|
||||
import { PantryService } from './pantry.service';
|
||||
import { CreatePantryItemDto } from './dto/create-pantry-item.dto';
|
||||
import { CurrentUser } from '../auth/decorators/current-user.decorator';
|
||||
import { Roles } from '../auth/decorators/roles.decorator';
|
||||
import { CreateInventoryDto } from '../inventory/dto/create-inventory.dto';
|
||||
import { CreateAdminPantryItemDto } from './dto/create-admin-pantry-item.dto';
|
||||
import { UpdatePantryItemDto } from './dto/update-pantry-item.dto';
|
||||
|
||||
@Controller('pantry')
|
||||
export class PantryController {
|
||||
@@ -31,6 +33,24 @@ export class PantryController {
|
||||
});
|
||||
}
|
||||
|
||||
@Roles('admin')
|
||||
@Post('admin')
|
||||
createAdmin(
|
||||
@CurrentUser() user: { userId: number },
|
||||
@Body() body: CreateAdminPantryItemDto,
|
||||
) {
|
||||
return this.pantryService.createAdmin(user.userId, body, body.userId);
|
||||
}
|
||||
|
||||
@Roles('admin')
|
||||
@Patch('admin/:id')
|
||||
updateAdmin(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() body: UpdatePantryItemDto,
|
||||
) {
|
||||
return this.pantryService.updateAdmin(id, body);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(
|
||||
@CurrentUser() user: { userId: number },
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PrismaService } from '../prisma/prisma.service';
|
||||
import { CreatePantryItemDto } from './dto/create-pantry-item.dto';
|
||||
import { CreateInventoryDto } from '../inventory/dto/create-inventory.dto';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { UpdatePantryItemDto } from './dto/update-pantry-item.dto';
|
||||
|
||||
type PantryQuery = {
|
||||
userId?: number;
|
||||
@@ -12,11 +13,43 @@ type PantryQuery = {
|
||||
export class PantryService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
private readonly productWithCategoryInclude = {
|
||||
include: {
|
||||
categoryRef: {
|
||||
include: {
|
||||
parent: {
|
||||
include: {
|
||||
parent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
private async ensureProductExistsAny(productId: number) {
|
||||
const product = await this.prisma.product.findUnique({ where: { id: productId } });
|
||||
if (!product) {
|
||||
throw new NotFoundException('Product not found');
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
private async ensureUserExists(userId: number) {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { id: true },
|
||||
});
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User with id ${userId} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
findAll(userId: number) {
|
||||
return this.prisma.pantryItem.findMany({
|
||||
where: { userId },
|
||||
include: {
|
||||
product: true,
|
||||
product: this.productWithCategoryInclude,
|
||||
},
|
||||
orderBy: {
|
||||
product: { name: 'asc' },
|
||||
@@ -36,17 +69,7 @@ export class PantryService {
|
||||
},
|
||||
},
|
||||
product: {
|
||||
include: {
|
||||
categoryRef: {
|
||||
include: {
|
||||
parent: {
|
||||
include: {
|
||||
parent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...this.productWithCategoryInclude,
|
||||
},
|
||||
},
|
||||
orderBy: [
|
||||
@@ -76,7 +99,87 @@ export class PantryService {
|
||||
productId: data.productId,
|
||||
location: data.location?.trim() || null,
|
||||
},
|
||||
include: { product: true },
|
||||
include: { product: this.productWithCategoryInclude },
|
||||
});
|
||||
}
|
||||
|
||||
async createAdmin(adminUserId: number, data: CreatePantryItemDto, targetUserId?: number) {
|
||||
const effectiveUserId = typeof targetUserId === 'number' ? targetUserId : adminUserId;
|
||||
await this.ensureUserExists(effectiveUserId);
|
||||
await this.ensureProductExistsAny(data.productId);
|
||||
|
||||
const existing = await this.prisma.pantryItem.findUnique({
|
||||
where: {
|
||||
userId_productId: {
|
||||
userId: effectiveUserId,
|
||||
productId: data.productId,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (existing) {
|
||||
throw new ConflictException('Produkten finns redan i baslagret');
|
||||
}
|
||||
|
||||
return this.prisma.pantryItem.create({
|
||||
data: {
|
||||
userId: effectiveUserId,
|
||||
productId: data.productId,
|
||||
location: data.location?.trim() || null,
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
product: this.productWithCategoryInclude,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async updateAdmin(id: number, data: UpdatePantryItemDto) {
|
||||
const existing = await this.prisma.pantryItem.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
throw new NotFoundException(`PantryItem med id ${id} hittades inte`);
|
||||
}
|
||||
|
||||
if (typeof data.productId === 'number') {
|
||||
await this.ensureProductExistsAny(data.productId);
|
||||
const duplicate = await this.prisma.pantryItem.findUnique({
|
||||
where: {
|
||||
userId_productId: {
|
||||
userId: existing.userId,
|
||||
productId: data.productId,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (duplicate && duplicate.id !== id) {
|
||||
throw new ConflictException('Produkten finns redan i baslagret');
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma.pantryItem.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(typeof data.productId === 'number'
|
||||
? { product: { connect: { id: data.productId } } }
|
||||
: {}),
|
||||
...(typeof data.location === 'string'
|
||||
? { location: data.location.trim() || null }
|
||||
: {}),
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
product: this.productWithCategoryInclude,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,7 +226,7 @@ export class PantryService {
|
||||
suitableFor: data.suitableFor?.trim() || undefined,
|
||||
comment: data.comment?.trim() || undefined,
|
||||
},
|
||||
include: { product: true },
|
||||
include: { product: this.productWithCategoryInclude },
|
||||
});
|
||||
|
||||
await tx.pantryItem.delete({ where: { id: item.id } });
|
||||
|
||||
Reference in New Issue
Block a user