import { NextResponse } from "next/server"; import { z } from "zod"; import { ItemSource } from "@prisma/client"; import { db } from "@/lib/db"; import { requireAuth } from "@/lib/auth"; const PANTRY_SELECT = { id: true, name: true, description: true, quantity: true, unit: true, storedAt: true, bestBefore: true, source: true, plantId: true, notes: true, pantryCategory: true, pantrySubcategory: true, pricePaid: true, priceUnit: true, location: { select: { id: true, name: true } }, plant: { select: { id: true, commonName: true, variety: true } }, labels: { select: { label: { select: { id: true, name: true } } } }, } as const; async function syncTags(itemId: string, tags: string[]) { const names = Array.from(new Set(tags.map(t => t.trim()).filter(Boolean))); const labels = await Promise.all( names.map(name => db.label.upsert({ where: { name }, update: {}, create: { name }, select: { id: true } })) ); await db.labelOnItem.deleteMany({ where: { itemId } }); if (labels.length) { await db.labelOnItem.createMany({ data: labels.map(l => ({ itemId, labelId: l.id })), skipDuplicates: true, }); } } const updateSchema = z.object({ name: z.string().min(1).optional(), description: z.string().nullable().optional(), quantity: z.number().min(0).optional(), unit: z.string().nullable().optional(), packageSize: z.string().nullable().optional(), locationId: z.string().nullable().optional(), storedAt: z.string().nullable().optional(), bestBefore: z.string().nullable().optional(), source: z.nativeEnum(ItemSource).nullable().optional(), plantId: z.string().nullable().optional(), pantryCategory: z.string().nullable().optional(), pantrySubcategory: z.string().nullable().optional(), pricePaid: z.number().nullable().optional(), priceUnit: z.string().nullable().optional(), notes: z.string().nullable().optional(), tags: z.array(z.string()).optional(), }); export async function PATCH(req: Request, { params }: { params: { id: string } }) { try { const session = await requireAuth(); const data = updateSchema.parse(await req.json()); await db.item.update({ where: { id: params.id }, data: { ...(data.name !== undefined && { name: data.name }), ...(data.description !== undefined && { description: data.description ?? data.packageSize ?? null }), ...(data.quantity !== undefined && { quantity: data.quantity }), ...(data.unit !== undefined && { unit: data.unit ?? null }), ...(data.locationId !== undefined && { locationId: data.locationId ?? null }), ...(data.storedAt !== undefined && { storedAt: data.storedAt ? new Date(data.storedAt) : null }), ...(data.bestBefore !== undefined && { bestBefore: data.bestBefore ? new Date(data.bestBefore) : null }), ...(data.source !== undefined && { source: data.source ?? null }), ...(data.plantId !== undefined && { plantId: data.plantId ?? null }), ...(data.pantryCategory !== undefined && { pantryCategory: data.pantryCategory ?? null }), ...(data.pantrySubcategory !== undefined && { pantrySubcategory: data.pantrySubcategory ?? null }), ...(data.pricePaid !== undefined && { pricePaid: data.pricePaid ?? null }), ...(data.priceUnit !== undefined && { priceUnit: data.priceUnit ?? null }), ...(data.notes !== undefined && { notes: data.notes ?? null }), }, }); if (data.tags !== undefined) await syncTags(params.id, data.tags); const item = await db.item.findUniqueOrThrow({ where: { id: params.id }, select: PANTRY_SELECT }); await db.auditEvent.create({ data: { action: "pantry.update", entityId: item.id, actorId: session.user.id, payload: { name: item.name } }, }); return NextResponse.json({ ...item, quantity: Number(item.quantity), pricePaid: item.pricePaid != null ? Number(item.pricePaid) : null, tags: item.labels.map(l => l.label.name), }); } catch (err) { if (err instanceof z.ZodError) return NextResponse.json({ error: "Invalid input" }, { status: 400 }); if (err instanceof Error && err.message === "Unauthorized") return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); console.error(err); return NextResponse.json({ error: "Server error" }, { status: 500 }); } } export async function DELETE(_req: Request, { params }: { params: { id: string } }) { try { const session = await requireAuth(); await db.item.update({ where: { id: params.id }, data: { active: false } }); await db.auditEvent.create({ data: { action: "pantry.delete", entityId: params.id, actorId: session.user.id }, }); return NextResponse.json({ ok: true }); } catch (err) { if (err instanceof Error && err.message === "Unauthorized") return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); console.error(err); return NextResponse.json({ error: "Server error" }, { status: 500 }); } }