feat(iace): Klaerungen Phase 2 — Sidebar-Counter + CSV-Export + Hazard-Banner

Three pieces complete the Klaerungen UX:

1. Sidebar-Counter: layout.tsx polls /clarifications and shows a
   colored open-count badge on the "Klaerungen" nav item. Refreshes
   whenever the user changes route.

2. CSV-Export: new backend endpoint
   GET /sdk/v1/iace/projects/:id/clarifications.csv produces a UTF-8-
   BOM-prefixed semicolon-separated CSV (Excel-friendly) with ID,
   Quelle, Kategorie, Frage, Status, Antwort, Begruendung, Bearbeiter,
   answered_at, anzahl Gefaehrdungen, Gefaehrdungs-Namen, Norm-Refs.
   Frontend Klaerungen-Seite bekommt einen "CSV-Export"-Button.

3. Hazard-Banner statt Fragentext im Benchmark-Detail: the previous
   bulleted clarification list was duplicated across 48 hazards for a
   single FANUC question. Phase 2 replaces it with a compact status
   badge — "N offene Klaerung(en) — Klaerungen-Seite oeffnen" (orange)
   or "Alle N Klaerungen beantwortet" (green) with a direct link.

Backend cleanup: iace_handler_init.go no longer appends the "Mit
Anlagenbauer zu klaeren" block to Hazard.Description. The description
stays focused on the scenario; clarifications live in the dedicated
endpoint and answers persist across re-inits via project.metadata.
The aggregated "Referenzierte Normen" line on the hazard is kept.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-17 01:25:36 +02:00
parent 525038359a
commit f19a75d83d
6 changed files with 205 additions and 62 deletions
@@ -100,13 +100,26 @@ export default function ClarificationsPage() {
Standardisierte Prüffragen aus Norm- und Herstellerwissen. Eine Antwort gilt für alle referenzierten Gefährdungen.
</p>
</div>
{data && (
<div className="flex gap-2 text-sm">
<Badge color="bg-orange-100 text-orange-800" label={`${data.open_count} offen`} />
<Badge color="bg-green-100 text-green-800" label={`${data.answered_count} beantwortet`} />
<Badge color="bg-gray-100 text-gray-700" label={`${data.total} gesamt`} />
</div>
)}
<div className="flex items-center gap-3">
{data && (
<div className="flex gap-2 text-sm">
<Badge color="bg-orange-100 text-orange-800" label={`${data.open_count} offen`} />
<Badge color="bg-green-100 text-green-800" label={`${data.answered_count} beantwortet`} />
<Badge color="bg-gray-100 text-gray-700" label={`${data.total} gesamt`} />
</div>
)}
<a
href={`/api/sdk/v1/iace/projects/${projectId}/clarifications.csv`}
download
className="text-xs px-3 py-1.5 rounded border border-gray-300 bg-white hover:bg-gray-50 inline-flex items-center gap-1.5"
title="CSV-Export für die Übergabe an den Anlagenbauer"
>
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5 5-5M12 15V3" />
</svg>
CSV-Export
</a>
</div>
</div>
<div className="flex gap-3 mb-4 items-center">