feat: make pantry items and meal plan entries user-scoped; update related services and controllers

This commit is contained in:
Nils-Johan Gynther
2026-04-22 18:38:04 +02:00
parent 44b4e7ad73
commit 4482129fca
6 changed files with 123 additions and 35 deletions
+29 -10
View File
@@ -1,33 +1,52 @@
import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
import { MealPlanService } from './meal-plan.service';
import { CreateMealPlanEntryDto } from './dto/create-meal-plan-entry.dto';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
@Controller('meal-plan')
export class MealPlanController {
constructor(private readonly mealPlanService: MealPlanService) {}
@Get()
findByRange(@Query('from') from: string, @Query('to') to: string) {
return this.mealPlanService.findByRange(from, to);
findByRange(
@CurrentUser() user: { userId: number },
@Query('from') from: string,
@Query('to') to: string,
) {
return this.mealPlanService.findByRange(user.userId, from, to);
}
@Get('shopping-list')
shoppingList(@Query('from') from: string, @Query('to') to: string) {
return this.mealPlanService.shoppingList(from, to);
shoppingList(
@CurrentUser() user: { userId: number },
@Query('from') from: string,
@Query('to') to: string,
) {
return this.mealPlanService.shoppingList(user.userId, from, to);
}
@Get('inventory-compare')
inventoryCompare(@Query('from') from: string, @Query('to') to: string) {
return this.mealPlanService.inventoryCompare(from, to);
inventoryCompare(
@CurrentUser() user: { userId: number },
@Query('from') from: string,
@Query('to') to: string,
) {
return this.mealPlanService.inventoryCompare(user.userId, from, to);
}
@Post()
upsert(@Body() dto: CreateMealPlanEntryDto) {
return this.mealPlanService.upsert(dto);
upsert(
@CurrentUser() user: { userId: number },
@Body() dto: CreateMealPlanEntryDto,
) {
return this.mealPlanService.upsert(user.userId, dto);
}
@Delete(':date')
removeByDate(@Param('date') date: string) {
return this.mealPlanService.removeByDate(date);
removeByDate(
@CurrentUser() user: { userId: number },
@Param('date') date: string,
) {
return this.mealPlanService.removeByDate(user.userId, date);
}
}
+30 -11
View File
@@ -22,9 +22,10 @@ export class MealPlanService {
constructor(private readonly prisma: PrismaService) {}
/** Hämta matplan för ett datumintervall (default: nuvarande vecka) */
async findByRange(from: string, to: string) {
async findByRange(userId: number, from: string, to: string) {
return this.prisma.mealPlanEntry.findMany({
where: {
userId,
date: { gte: new Date(from), lte: new Date(to) },
},
include: { recipe: { select: recipeSelect } },
@@ -33,28 +34,43 @@ export class MealPlanService {
}
/** Sätt recept för ett datum (upsert — ett recept per dag) */
async upsert(dto: CreateMealPlanEntryDto) {
async upsert(userId: number, dto: CreateMealPlanEntryDto) {
const date = new Date(dto.date);
return this.prisma.mealPlanEntry.upsert({
where: { date },
create: { date, recipeId: dto.recipeId, servings: dto.servings ?? null },
where: {
userId_date: {
userId,
date,
},
},
create: {
userId,
date,
recipeId: dto.recipeId,
servings: dto.servings ?? null,
},
update: { recipeId: dto.recipeId, servings: dto.servings ?? null },
include: { recipe: { select: recipeSelect } },
});
}
/** Ta bort matplanspost för ett datum */
async removeByDate(date: string) {
async removeByDate(userId: number, date: string) {
const entry = await this.prisma.mealPlanEntry.findUnique({
where: { date: new Date(date) },
where: {
userId_date: {
userId,
date: new Date(date),
},
},
});
if (!entry) throw new NotFoundException('Ingen matplanspost för detta datum');
return this.prisma.mealPlanEntry.delete({ where: { id: entry.id } });
}
/** Samlad ingredienslista för ett datumintervall */
async shoppingList(from: string, to: string) {
const entries = await this.findByRange(from, to);
async shoppingList(userId: number, from: string, to: string) {
const entries = await this.findByRange(userId, from, to);
// Summera ingredienser per produkt+enhet (skalat per portionsantal)
const map = new Map<string, { productId: number; name: string; quantity: number; unit: string }>();
@@ -83,11 +99,14 @@ export class MealPlanService {
}
/** Jämför veckans ingrediensbehov mot inventariet */
async inventoryCompare(from: string, to: string) {
const entries = await this.findByRange(from, to);
async inventoryCompare(userId: number, from: string, to: string) {
const entries = await this.findByRange(userId, from, to);
// Hämta pantry-produkter — dessa anses alltid tillgängliga
const pantryItems = await this.prisma.pantryItem.findMany({ select: { productId: true } });
const pantryItems = await this.prisma.pantryItem.findMany({
where: { userId },
select: { productId: true },
});
const pantryProductIds = new Set(pantryItems.map((p) => p.productId));
// Aggregera ingredienser per produkt+enhet (skalat per portionsantal)