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:
@@ -1,9 +1,12 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/iace"
|
||||
@@ -216,3 +219,64 @@ func (h *IACEHandler) AnswerClarification(c *gin.Context) {
|
||||
"answer": answers[cid],
|
||||
})
|
||||
}
|
||||
|
||||
// ExportClarificationsCSV handles GET /projects/:id/clarifications.csv.
|
||||
// Returns the aggregated clarifications as a CSV for handover to the
|
||||
// Anlagenbauer — one row per question with all referenced hazards and
|
||||
// the current answer state.
|
||||
func (h *IACEHandler) ExportClarificationsCSV(c *gin.Context) {
|
||||
projectID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
|
||||
return
|
||||
}
|
||||
ctx := c.Request.Context()
|
||||
project, err := h.store.GetProject(ctx, projectID)
|
||||
if err != nil || project == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "project not found"})
|
||||
return
|
||||
}
|
||||
hazards, _ := h.store.ListHazards(ctx, projectID)
|
||||
answers, _ := readClarificationAnswers(project.Metadata)
|
||||
narrative := extractNarrativeFromMetadata(project.Metadata)
|
||||
hazardToPatterns := h.reconstructHazardPatterns(narrative, project.MachineType, hazards)
|
||||
manufHits := iace.LookupManufacturerFeaturesInText(narrative)
|
||||
clarifications := iace.BuildProjectClarifications(hazards, hazardToPatterns, manufHits, answers)
|
||||
|
||||
sort.Slice(clarifications, func(i, j int) bool {
|
||||
if clarifications[i].Status != clarifications[j].Status {
|
||||
return clarifications[i].Status == "open"
|
||||
}
|
||||
return clarifications[i].Source < clarifications[j].Source
|
||||
})
|
||||
|
||||
filename := fmt.Sprintf("klaerungen_%s_%s.csv", project.MachineName, time.Now().Format("2006-01-02"))
|
||||
filename = strings.ReplaceAll(filename, " ", "_")
|
||||
c.Header("Content-Type", "text/csv; charset=utf-8")
|
||||
c.Header("Content-Disposition", `attachment; filename="`+filename+`"`)
|
||||
// Excel-Erkennung: UTF-8 BOM voranstellen
|
||||
c.Writer.Write([]byte{0xEF, 0xBB, 0xBF})
|
||||
w := csv.NewWriter(c.Writer)
|
||||
w.Comma = ';'
|
||||
_ = w.Write([]string{
|
||||
"ID", "Quelle", "Kategorie", "Frage", "Status", "Antwort", "Begruendung",
|
||||
"Bearbeiter", "Beantwortet_am", "Anzahl_Gefaehrdungen", "Gefaehrdungen", "Norm_Referenzen",
|
||||
})
|
||||
for _, cl := range clarifications {
|
||||
_ = w.Write([]string{
|
||||
cl.ID,
|
||||
cl.Source,
|
||||
cl.Category,
|
||||
cl.Question,
|
||||
cl.Status,
|
||||
cl.Answer,
|
||||
cl.Reasoning,
|
||||
cl.AnsweredBy,
|
||||
cl.AnsweredAt,
|
||||
fmt.Sprintf("%d", len(cl.AffectedHazardIDs)),
|
||||
strings.Join(cl.AffectedHazardNames, " | "),
|
||||
strings.Join(cl.NormReferences, " | "),
|
||||
})
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user