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
@@ -212,42 +212,13 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
// Join all applicable lifecycles as comma-separated string
lifecycleStr := strings.Join(mp.ApplicableLifecycles, ",")
// Append pattern-defined clarification questions to the
// description so they're visible in the UI without DB-
// schema changes. The engine does NOT invent commentary;
// it only hangs the standard ISO/EN clarification points
// onto the hazard so the operator knows what to verify
// with the Anlagenbauer.
// Phase 2: clarification questions are no longer embedded
// in the hazard description they live in the dedicated
// /clarifications API and the UI loads them on demand.
// The hazard description stays clean and focused on the
// scenario itself. Only the aggregated norm-references
// block is appended below for an at-a-glance audit trail.
desc := mp.ScenarioDE
clarBuckets := mp.ClarificationQuestionsDE
// Manufacturer-specific clarifications: if the narrative
// mentions a known manufacturer (FANUC/KUKA/Siemens/...),
// append its feature-specific questions to the matching
// hazard categories. Markennennung ist nominative use
// (§ 23 MarkenG), Fakten ueber Safety-Features sind nicht
// urheberrechtlich geschuetzt.
for _, mf := range iace.LookupManufacturerFeaturesInText(narrativeText) {
applies := len(mf.AppliesToHazardCats) == 0
for _, hc := range mf.AppliesToHazardCats {
if hc == cat {
applies = true
break
}
}
if !applies {
continue
}
prefix := mf.Manufacturer + " (" + mf.FeatureName + "): "
for _, q := range mf.Clarifications {
clarBuckets = append(clarBuckets, prefix+q)
}
}
if len(clarBuckets) > 0 {
desc += "\n\nMit Anlagenbauer zu klaeren:"
for _, q := range clarBuckets {
desc += "\n- " + q
}
}
hz, cerr := h.store.CreateHazard(ctx, iace.CreateHazardRequest{
ProjectID: projectID,