L5 Berichte & Unterschrift: Backoffice- und Mobil-Oberflächen

/reports mit Filtern (zur Prüfung zuerst), /reports/[id] mit Aktionen, Versionen und PDF-Link;
mobile Komponenten für Bericht, Prüfung und Unterschrift inkl. Signature-Pad; Texte de/en.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 12:22:40 +02:00
co-authored by Claude Opus 5
parent 8f53df6208
commit 94092ade3f
20 changed files with 1805 additions and 3 deletions
+35
View File
@@ -0,0 +1,35 @@
"use client";
import { CheckCircle2, XCircle } from "lucide-react";
import { useTranslations } from "next-intl";
import type { ReportActionState } from "@/lib/reports/action-state";
import { BlockerList } from "./blocker-list";
const FIELD_MESSAGES: Record<string, string> = {
reason: "errors.reasonRequired",
signerName: "errors.signerRequired",
image: "errors.imageRequired",
};
/** Inline feedback for report actions (errors directly at the form, Brandbook §12.5). */
export function ActionMessage({ state, okText }: { state: ReportActionState; okText?: string }) {
const t = useTranslations("reports");
if (state.status === "ok") {
if (!okText) return null;
return (
<p role="status" className="flex items-center gap-2 text-[13px] font-semibold text-[var(--ok)]">
<CheckCircle2 className="size-4" aria-hidden />
{okText}
</p>
);
}
if (state.status !== "error") return null;
if (state.blockers?.length) return <BlockerList blockers={state.blockers} title={t("errors.blocked")} />;
const key = state.field && FIELD_MESSAGES[state.field] ? FIELD_MESSAGES[state.field] : state.code === "forbidden" && state.field === undefined ? "errors.forbidden" : `errors.${state.code}`;
return (
<p role="alert" className="flex items-center gap-2 rounded-lg border border-[var(--risk)] px-3 py-2 text-[13px] font-semibold text-[var(--risk)]">
<XCircle className="size-4 shrink-0" aria-hidden />
{t(key)}
</p>
);
}