diff --git a/src/app/admin/actions/apicore-sync.ts b/src/app/admin/actions/apicore-sync.ts index 282213e..dc378dc 100644 --- a/src/app/admin/actions/apicore-sync.ts +++ b/src/app/admin/actions/apicore-sync.ts @@ -370,11 +370,13 @@ async function syncBatches(sources: ApiCoreBottlingBatchWithHarvestId[]): Promis * Formularfeld und waere sonst eine offene Weiterleitung — ein praeparierter * Wert koennte Admins auf eine fremde Seite schicken. */ -const RETURN_TARGETS = ["/admin/ernten", "/admin/produkte", "/admin/chargen"] as const; +// "/admin" ist der Normalfall (der eine Sync-Knopf sitzt auf dem Dashboard); +// die uebrigen bleiben erlaubt, damit alte Lesezeichen/Formulare nicht ins Leere laufen. +const RETURN_TARGETS = ["/admin", "/admin/ernten", "/admin/produkte", "/admin/chargen"] as const; function returnTargetFrom(formData: FormData): string { const requested = formData.get("returnTo"); - return RETURN_TARGETS.find((target) => target === requested) ?? "/admin/ernten"; + return RETURN_TARGETS.find((target) => target === requested) ?? "/admin"; } /** diff --git a/src/app/admin/ernten/page.tsx b/src/app/admin/ernten/page.tsx index 551cb8c..4effb64 100644 --- a/src/app/admin/ernten/page.tsx +++ b/src/app/admin/ernten/page.tsx @@ -14,13 +14,11 @@ import { AdminRowActions, } from "@/components/admin-list"; import { createHarvestAction, toggleHarvestPublishedAction, deleteHarvestAction } from "../actions"; -import { ApiCoreSyncPanel, ApiCoreSyncResult } from "@/components/apicore-sync-panel"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; -export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten">) { +export default async function AdminHarvestsPage() { const t = getDictionary(await getLocale()); - const searchParams = await props.searchParams; const harvests = await prisma.harvest.findMany({ orderBy: [{ year: "desc" }, { harvestDate: "desc" }], include: { _count: { select: { batches: true, products: true } } }, @@ -31,7 +29,6 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten" -
@@ -94,7 +91,7 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten" {harvests.length === 0 ? : null} -
+

{t.adminHarvestsPage.newHarvestHeading}

@@ -112,7 +109,6 @@ export default async function AdminHarvestsPage(props: PageProps<"/admin/ernten"
-
); diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index efa5df7..0d18dab 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import { ApiCoreSyncPanel, ApiCoreSyncResult } from "@/components/apicore-sync-panel"; import { prisma } from "@/lib/prisma"; import { formatPrice } from "@/lib/format"; import { getLocale } from "@/i18n/locale"; @@ -100,7 +101,8 @@ function daysAgo(n: number): Date { return d; } -export default async function AdminDashboardPage() { +export default async function AdminDashboardPage(props: PageProps<"/admin">) { + const searchParams = await props.searchParams; const locale = await getLocale(); const t = getDictionary(locale); const [ @@ -306,6 +308,9 @@ export default async function AdminDashboardPage() { + {/* Ergebnis des ApiCore-Laufs (nur direkt nach dem Klick sichtbar). */} + + {/* Fehlen Dateien, zu denen die Datenbank noch Pfade haelt, ist das fast immer ein nicht persistentes Upload-Verzeichnis (in Kubernetes ein "emptyDir" statt eines PersistentVolumeClaim). Ohne diesen @@ -576,6 +581,11 @@ export default async function AdminDashboardPage() { {t.adminDashboardPage.viewAllOrders}

+ + {/* Der eine Sync-Knopf fuer Ernten, Chargen und Produkte. */} +
+ +
); } diff --git a/src/app/admin/produkte/page.tsx b/src/app/admin/produkte/page.tsx index 6378040..8ebf54e 100644 --- a/src/app/admin/produkte/page.tsx +++ b/src/app/admin/produkte/page.tsx @@ -3,7 +3,6 @@ import { BackLink } from "@/components/back-link"; import { formatPrice } from "@/lib/format"; import { ConfirmForm } from "@/components/confirm-form"; import { deleteProductAction } from "../actions"; -import { ApiCoreSyncPanel, ApiCoreSyncResult } from "@/components/apicore-sync-panel"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; import { isArchiveView } from "@/lib/archive"; @@ -49,7 +48,6 @@ export default async function AdminProductsPage(props: PageProps<"/admin/produkt - -
- -
); } diff --git a/src/components/apicore-sync-panel.tsx b/src/components/apicore-sync-panel.tsx index bb58a1e..be93c7d 100644 --- a/src/components/apicore-sync-panel.tsx +++ b/src/components/apicore-sync-panel.tsx @@ -6,10 +6,10 @@ import { Button } from "@/components/ui"; /** * Der eine Sync-Knopf und die Anzeige seines Ergebnisses. * - * Gemeinsam genutzt von /admin/ernten und /admin/produkte: es ist derselbe - * Vorgang, egal von welcher Seite er ausgeloest wird — Ernten, Chargen und - * Produkte laufen ohnehin immer zusammen. Zwei getrennte Bloecke in zwei - * Seiten waeren dieselbe Anzeige zweimal gepflegt. + * Sitzt genau einmal: auf dem Admin-Dashboard. Ernten, Chargen und Produkte + * laufen ohnehin immer zusammen durch (siehe syncApiCoreAction) — derselbe + * Knopf zusaetzlich auf /admin/ernten und /admin/produkte sah aus wie zwei + * verschiedene Synchronisierungen. */ /** Rohwerte aus der Adresszeile — Next liefert Query-Parameter so aus. */