Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
91 lines
4.1 KiB
TypeScript
91 lines
4.1 KiB
TypeScript
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import { Download, ListChecks } from "lucide-react";
|
|
import { requireSession } from "@/server/auth";
|
|
import { dbForTenant } from "@/server/db";
|
|
import { PageHead, Pill } from "@/components/mockup-ui";
|
|
import { Button } from "@/components/ui/button";
|
|
import { buildAbgabeRows } from "@/server/export-context";
|
|
import { PRUEFZIEL_LABEL } from "@/lib/export/vda-isa";
|
|
|
|
/**
|
|
* ABGABE-Vorschau (VDA-ISA-Prüfungsdokumentation) — Seite des Audit-Wizards.
|
|
* Zeigt je Control den zusammengesetzten „Beschreibung der Umsetzung"-Text
|
|
* (übernommene ControlDescription je Anforderung), den bestätigten Reifegrad
|
|
* und die Referenz-Dokumentation. Download über die geteilte Export-Route als CSV
|
|
* (`?format=abgabe`) oder als echte xlsx (`?format=abgabe-xlsx`).
|
|
*/
|
|
export default async function AbgabePreviewPage({ params }: { params: Promise<{ auditId: string }> }) {
|
|
const session = await requireSession();
|
|
const { auditId } = await params;
|
|
const db = dbForTenant(session.user.tenantId);
|
|
|
|
const audit = await db.audit.findFirst({ where: { id: auditId } });
|
|
if (!audit) notFound();
|
|
|
|
const rows = await buildAbgabeRows(db, session.user.tenantId);
|
|
const withText = rows.filter((r) => r.umsetzung).length;
|
|
|
|
return (
|
|
<main className="flex-1 p-6">
|
|
<PageHead
|
|
crumb={`Audit · ${audit.title}`}
|
|
title="ABGABE-Vorschau (VDA ISA)"
|
|
sub={`${withText} von ${rows.length} Controls mit übernommener Umsetzungsbeschreibung.`}
|
|
actions={
|
|
<>
|
|
<Button variant="outline" size="sm" nativeButton={false} render={<Link href={`/audit-readiness/${auditId}/controls`} />}>
|
|
<ListChecks className="size-3.5" /> Zu den Beschreibungen
|
|
</Button>
|
|
{/* Datei-Download über einen Route-Handler (kein Page-Route) — bewusst `<a>`, nicht `<Link>`. */}
|
|
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
|
|
<Button variant="outline" size="sm" nativeButton={false} render={<a href="/audit-readiness/export?format=abgabe" />}>
|
|
<Download className="size-3.5" /> ABGABE (CSV)
|
|
</Button>
|
|
{/* eslint-disable-next-line @next/next/no-html-link-for-pages */}
|
|
<Button variant="outline" size="sm" nativeButton={false} render={<a href="/audit-readiness/export?format=abgabe-xlsx" />}>
|
|
<Download className="size-3.5" /> ABGABE (xlsx)
|
|
</Button>
|
|
</>
|
|
}
|
|
/>
|
|
|
|
{rows.length === 0 ? (
|
|
<p className="text-[12.5px] text-muted-foreground">Keine Controls vorhanden.</p>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{rows.map((r) => (
|
|
<section key={r.control} className="shadow-card rounded-xl border bg-card p-4">
|
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
<h2 className="font-heading text-sm font-semibold">{r.control} · {r.frage}</h2>
|
|
<div className="flex items-center gap-2">
|
|
<Pill tone="info">{PRUEFZIEL_LABEL[r.pruefziel]}</Pill>
|
|
<Pill tone={r.reifegrad === null ? "mut" : "ok"}>
|
|
Reifegrad {r.reifegrad ?? "—"}{r.zielReifegrad !== null ? ` / Ziel ${r.zielReifegrad}` : ""}
|
|
</Pill>
|
|
</div>
|
|
</div>
|
|
|
|
{r.umsetzung ? (
|
|
<p className="mt-2 text-[12.5px] whitespace-pre-wrap">{r.umsetzung}</p>
|
|
) : (
|
|
<p className="mt-2 text-[12px] text-muted-foreground">Noch keine übernommene Beschreibung — im Beschreibungs-Schritt erzeugen und übernehmen.</p>
|
|
)}
|
|
|
|
{r.referenzen && (
|
|
<p className="mt-2 border-t pt-2 text-[11.5px] text-muted-foreground">
|
|
<span className="font-semibold">Referenz Dokumentation:</span> {r.referenzen}
|
|
</p>
|
|
)}
|
|
</section>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
<p className="mt-4 text-[11.5px] text-muted-foreground">
|
|
Download als CSV (DE-Excel, semikolongetrennt) oder als xlsx im VDA-ISA-ABGABE-Layout.
|
|
</p>
|
|
</main>
|
|
);
|
|
}
|