From 7d5c1724879d2fd7b9489efeb555d32866eb48a6 Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Sat, 12 Sep 2026 04:08:39 +0200 Subject: [PATCH] feat: eigene Bilder pro Charge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chargen hatten bisher kein eigenes Bild-Feld (nur Ernten und Produkte) — neue Migration ergaenzt Batch.images (Json-Array, gleiches Muster wie Harvest.images/Product.images). Admin kann Chargenfotos jetzt unter /admin/chargen/[id] hochladen (AdminImageListField, identisch zum Ernten-Formular). Die oeffentliche Chargen-Seite (/charge/[code]) zeigt diese Fotos jetzt bevorzugt, faellt ohne eigene Chargenbilder weiterhin auf die Erntebilder zurueck. Hinweis fuer den naechsten Live-Deploy: die generierte Migration hat nebenbei eine bereits laenger bestehende Schema-Abweichung mitkorrigiert (harvests.images und products.images/tasteTags werden jetzt als NOT NULL erzwungen, wie im Schema schon laenger deklariert) - "prisma migrate deploy" schlaegt fehl, falls dort noch echte NULL-Werte stehen sollten. Co-Authored-By: Claude Sonnet 5 --- .../migration.sql | 17 +++++++++++++++++ prisma/schema.prisma | 5 +++++ src/app/admin/actions/harvests-batches.ts | 2 ++ src/app/admin/chargen/[id]/page.tsx | 12 ++++++++++-- src/app/charge/[code]/page.tsx | 7 +++++-- src/i18n/dictionaries/de.ts | 1 + src/i18n/dictionaries/en.ts | 1 + src/i18n/dictionary-type.ts | 1 + src/lib/data.ts | 9 +++++++-- 9 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 prisma/migrations/20260912020343_add_batch_images/migration.sql diff --git a/prisma/migrations/20260912020343_add_batch_images/migration.sql b/prisma/migrations/20260912020343_add_batch_images/migration.sql new file mode 100644 index 0000000..abf77d1 --- /dev/null +++ b/prisma/migrations/20260912020343_add_batch_images/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - Made the column `images` on table `harvests` required. This step will fail if there are existing NULL values in that column. + - Made the column `tasteTags` on table `products` required. This step will fail if there are existing NULL values in that column. + - Made the column `images` on table `products` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "batches" ADD COLUMN "images" JSONB NOT NULL DEFAULT '[]'; + +-- AlterTable +ALTER TABLE "harvests" ALTER COLUMN "images" SET NOT NULL; + +-- AlterTable +ALTER TABLE "products" ALTER COLUMN "tasteTags" SET NOT NULL, +ALTER COLUMN "images" SET NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 505ffd3..78a6ef5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -171,6 +171,11 @@ model Batch { bestBeforeDate DateTime glassCount Int remainingGlasses Int + /// Liste von Bild-URLs als JSON-Array (String[] ist nicht Provider-uebergreifend: + /// MySQL kennt keine nativen Skalar-Arrays) — siehe lib/json-array.ts fuer den + /// Normalisierungs-Helfer beim Lesen. Eigene Fotos dieser konkreten Charge + /// (z.B. Etikett/Abfuellung), unabhaengig von Harvest.images. + images Json @default("[]") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/app/admin/actions/harvests-batches.ts b/src/app/admin/actions/harvests-batches.ts index 6becd5a..69cc23f 100644 --- a/src/app/admin/actions/harvests-batches.ts +++ b/src/app/admin/actions/harvests-batches.ts @@ -147,6 +147,7 @@ export async function createBatchAction(formData: FormData): Promise { bestBeforeDate: new Date(str(formData, "bestBeforeDate")), glassCount: num(formData, "glassCount"), remainingGlasses: num(formData, "remainingGlasses"), + images: strArray(formData, "images").filter(isValidUploadPath), }, }); @@ -167,6 +168,7 @@ export async function updateBatchAction(formData: FormData): Promise { bestBeforeDate: new Date(str(formData, "bestBeforeDate")), glassCount: num(formData, "glassCount"), remainingGlasses: num(formData, "remainingGlasses"), + images: strArray(formData, "images").filter(isValidUploadPath), }, }); diff --git a/src/app/admin/chargen/[id]/page.tsx b/src/app/admin/chargen/[id]/page.tsx index 4b5f38e..9de8387 100644 --- a/src/app/admin/chargen/[id]/page.tsx +++ b/src/app/admin/chargen/[id]/page.tsx @@ -1,6 +1,8 @@ import { notFound } from "next/navigation"; import { prisma } from "@/lib/prisma"; +import { asStringArray } from "@/lib/json-array"; import { BackLink } from "@/components/back-link"; +import { AdminImageListField } from "@/components/admin-image-list-field"; import { updateBatchAction } from "../../actions"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; @@ -8,8 +10,9 @@ import { getDictionary } from "@/i18n/get-dictionary"; export default async function AdminBatchEditPage(props: PageProps<"/admin/chargen/[id]">) { const t = getDictionary(await getLocale()); const { id } = await props.params; - const batch = await prisma.batch.findUnique({ where: { id }, include: { harvest: true } }); - if (!batch) notFound(); + const rawBatch = await prisma.batch.findUnique({ where: { id }, include: { harvest: true } }); + if (!rawBatch) notFound(); + const batch = { ...rawBatch, images: asStringArray(rawBatch.images) }; return (
@@ -64,6 +67,11 @@ export default async function AdminBatchEditPage(props: PageProps<"/admin/charge />
+ diff --git a/src/app/charge/[code]/page.tsx b/src/app/charge/[code]/page.tsx index 89f90e1..146b6b3 100644 --- a/src/app/charge/[code]/page.tsx +++ b/src/app/charge/[code]/page.tsx @@ -39,6 +39,9 @@ export default async function BatchPage(props: PageProps<"/charge/[code]">) { { label: t.chargePage.stepFilling, value: formatDate(batch.fillDate, locale) }, { label: t.chargePage.stepBestBefore, value: formatDate(batch.bestBeforeDate, locale) }, ]; + // Eigene Chargenfotos (z.B. Etikett/Abfuellung dieser konkreten Charge) + // gehen vor, falls gepflegt — sonst wie bisher die Erntebilder. + const displayImages = batch.images.length > 0 ? batch.images : batch.harvest.images; return (
@@ -57,7 +60,7 @@ export default async function BatchPage(props: PageProps<"/charge/[code]">) {
) { & { + images: string[]; harvest: HarvestWithImages; products: (Omit & { images: string[] })[]; }) @@ -164,5 +165,9 @@ export async function getBatchByCode( include: { harvest: true, products: true }, }); if (!batch) return null; - return { ...batch, harvest: withImages(batch.harvest), products: batch.products.map(withImages) }; + return { + ...withImages(batch), + harvest: withImages(batch.harvest), + products: batch.products.map(withImages), + }; }