feat: Ernten und Chargen im Admin loeschbar machen
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m27s

Neue deleteHarvestAction/deleteBatchAction, jeweils mit vorheriger
Referenzpruefung statt rohem Datenbankfehler: eine Ernte laesst sich nur
loeschen, wenn keine Chargen/Produkte mehr darauf verweisen, eine Charge
nur, wenn sie keinem Produkt zugeordnet ist und in keiner Bestellung
verwendet wurde (Chargen-Code steht auf Rechnungen/QR-Codes und darf aus
Nachweispflicht nie verschwinden). Loeschen-Button erscheint in den
Listen nur, wenn die Loeschung tatsaechlich moeglich ist, mit
Bestaetigungsdialog wie beim bestehenden Benutzer-Loeschen.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Sonnet 5 committed 2026-09-11 23:45:23 +02:00
1 parent c7286041a1
commit a3b11cc8e2
6 files changed
+138 -9

No files matched your search

+24 -3
View File
@@ -1,14 +1,20 @@
import Link from "next/link";
import { prisma } from "@/lib/prisma";
import { BackLink } from "@/components/back-link";
import { ConfirmForm } from "@/components/confirm-form";
import { formatDate } from "@/lib/format";
import { createHarvestAction, toggleHarvestPublishedAction, syncHarvestsFromApiCoreAction } from "../actions";
import {
createHarvestAction,
toggleHarvestPublishedAction,
syncHarvestsFromApiCoreAction,
deleteHarvestAction,
} from "../actions";
import { getLocale } from "@/i18n/locale";
import { getDictionary } from "@/i18n/get-dictionary";
export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten">) {
const t = getDictionary(await getLocale());
const { synced, updated, skipped, failed, syncError, batchSynced, batchSkipped, batchFailed, batchLoadError } =
const { synced, updated, skipped, failed, syncError, batchSynced, batchSkipped, batchFailed, batchLoadError, error } =
await props.searchParams;
const harvests = await prisma.harvest.findMany({
orderBy: [{ year: "desc" }, { harvestDate: "desc" }],
@@ -20,6 +26,9 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten"
<BackLink href="/admin" label={t.adminHarvestsPage.backToDashboard} />
<h1 className="text-2xl text-forest mb-8">{t.adminHarvestsPage.title}</h1>
{error ? (
<p className="mb-6 text-sm text-red-700 bg-red-50 border border-red-200 px-4 py-3">{error}</p>
) : null}
{synced !== undefined ? (
<p className="mb-6 text-sm text-forest bg-forest/5 border border-forest/20 px-4 py-3">
{t.adminHarvestsPage.syncResult(Number(synced), Number(updated ?? 0), Number(skipped ?? 0), Number(failed ?? 0))}
@@ -82,10 +91,22 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten"
<form action={toggleHarvestPublishedAction} className="inline">
<input type="hidden" name="id" value={harvest.id} />
{harvest.isPublished ? null : <input type="hidden" name="isPublished" value="on" />}
<button type="submit" className="text-forest underline">
<button type="submit" className="text-forest underline mr-4">
{harvest.isPublished ? t.adminHarvestsPage.unpublish : t.adminHarvestsPage.publish}
</button>
</form>
{harvest._count.batches === 0 && harvest._count.products === 0 ? (
<ConfirmForm
action={deleteHarvestAction}
confirmMessage={t.adminHarvestsPage.deleteConfirm(`${harvest.type} ${harvest.year}`)}
className="inline"
>
<input type="hidden" name="id" value={harvest.id} />
<button type="submit" className="text-red-700 underline">
{t.adminHarvestsPage.delete}
</button>
</ConfirmForm>
) : null}
</td>
</tr>
))}