From 41c2af7284ad8a343ef0357b2962cdb28d7224bb Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Mon, 5 Oct 2026 21:28:15 +0200 Subject: [PATCH] feat(ui): article stock overview and production batch editing - New article page /products/[id]: remaining stock summed over all production batches (or bottlings for honey), each batch linked to its own page for withdrawals and counts; "Bestand ansehen" in the list. - Production batches can be corrected (lot number, date, quantity, best-before date, note) via PATCH; the article stays fixed. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../[id]/edit-production-batch-dialog.tsx | 172 +++++++++++ .../(app)/production-batches/[id]/page.tsx | 72 +++-- .../src/app/(app)/products/[id]/page.spec.tsx | 95 ++++++ .../src/app/(app)/products/[id]/page.tsx | 273 ++++++++++++++++++ .../src/app/(app)/products/page.tsx | 15 +- .../src/lib/production-batches/api.ts | 11 + .../src/lib/production-batches/types.ts | 9 + 7 files changed, 626 insertions(+), 21 deletions(-) create mode 100644 ApiCore-Frontend/src/app/(app)/production-batches/[id]/edit-production-batch-dialog.tsx create mode 100644 ApiCore-Frontend/src/app/(app)/products/[id]/page.spec.tsx create mode 100644 ApiCore-Frontend/src/app/(app)/products/[id]/page.tsx diff --git a/ApiCore-Frontend/src/app/(app)/production-batches/[id]/edit-production-batch-dialog.tsx b/ApiCore-Frontend/src/app/(app)/production-batches/[id]/edit-production-batch-dialog.tsx new file mode 100644 index 0000000..f649a07 --- /dev/null +++ b/ApiCore-Frontend/src/app/(app)/production-batches/[id]/edit-production-batch-dialog.tsx @@ -0,0 +1,172 @@ +"use client"; + +import { FormEvent, useState } from "react"; +import { Pencil } from "lucide-react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { toast } from "sonner"; +import { updateProductionBatch } from "@/lib/production-batches/api"; +import { ProductionBatch } from "@/lib/production-batches/types"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +/** + * Corrects a production batch entered with a typo. The article stays fixed: + * moving a batch with sales to another article would rewrite where those + * sales came from. A changed quantity changes the remaining stock - in the + * shop as well. + */ +export function EditProductionBatchDialog({ + batch, +}: { + batch: ProductionBatch; +}) { + const [open, setOpen] = useState(false); + return ( + + + + Bearbeiten + + } + /> + + + Herstellungscharge bearbeiten + + {open ? ( + setOpen(false)} /> + ) : null} + + + ); +} + +function EditForm({ + batch, + onSaved, +}: { + batch: ProductionBatch; + onSaved: () => void; +}) { + const queryClient = useQueryClient(); + const [lotNumber, setLotNumber] = useState(batch.lotNumber); + const [date, setDate] = useState(batch.date.slice(0, 10)); + const [quantity, setQuantity] = useState(String(batch.quantity)); + const [bestBeforeDate, setBestBeforeDate] = useState( + batch.bestBeforeDate?.slice(0, 10) ?? "", + ); + const [notes, setNotes] = useState(batch.notes ?? ""); + const [error, setError] = useState(null); + + const mutation = useMutation({ + mutationFn: () => + updateProductionBatch(batch.id, { + lotNumber: lotNumber.trim(), + date: new Date(date).toISOString(), + quantity: Number(quantity), + bestBeforeDate: bestBeforeDate + ? new Date(bestBeforeDate).toISOString() + : null, + notes: notes.trim(), + }), + onSuccess: () => { + void queryClient.invalidateQueries({ queryKey: ["production-batches"] }); + void queryClient.invalidateQueries({ + queryKey: ["stock", "production-batches", batch.id], + }); + toast.success("Charge gespeichert"); + onSaved(); + }, + onError: (mutationError: Error) => { + const message = + mutationError.message || "Charge konnte nicht gespeichert werden"; + setError(message); + toast.error(message); + }, + }); + + function handleSubmit(event: FormEvent) { + event.preventDefault(); + setError(null); + mutation.mutate(); + } + + return ( +
+
+ + setLotNumber(event.target.value)} + /> +
+
+ + setDate(event.target.value)} + /> +
+
+ + setQuantity(event.target.value)} + /> +

+ Ändert den Rest (auch im Webshop). Fehlende oder gefundene Stück bitte + über „Inventur korrigieren“ buchen, nicht hier. +

+
+
+ + setBestBeforeDate(event.target.value)} + /> +
+
+ +