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:
@@ -116,6 +116,23 @@ export default function IACELayout({ children }: { children: React.ReactNode })
|
||||
const [variantInfo, setVariantInfo] = React.useState<{
|
||||
parentProjectId?: string; parentName?: string; variantCount?: number
|
||||
}>({})
|
||||
const [openClarifications, setOpenClarifications] = React.useState<number | null>(null)
|
||||
|
||||
// Poll the clarifications endpoint so the sidebar always shows the
|
||||
// current "offene Klaerungen" counter. Refresh whenever the user
|
||||
// navigates back to this layout (i.e. when pathname changes).
|
||||
React.useEffect(() => {
|
||||
if (!projectId) return
|
||||
let cancelled = false
|
||||
fetch(`/api/sdk/v1/iace/projects/${projectId}/clarifications`)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(d => {
|
||||
if (cancelled || !d || typeof d.open_count !== 'number') return
|
||||
setOpenClarifications(d.open_count)
|
||||
})
|
||||
.catch(() => {})
|
||||
return () => { cancelled = true }
|
||||
}, [projectId, pathname])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!projectId) return
|
||||
@@ -219,7 +236,15 @@ export default function IACELayout({ children }: { children: React.ReactNode })
|
||||
}`}
|
||||
>
|
||||
<NavIcon icon={item.icon} className="w-4 h-4 flex-shrink-0" />
|
||||
<span className="truncate">{item.label}</span>
|
||||
<span className="truncate flex-1">{item.label}</span>
|
||||
{item.id === 'clarifications' && openClarifications !== null && openClarifications > 0 && (
|
||||
<span
|
||||
className="ml-auto inline-flex items-center justify-center min-w-[20px] px-1.5 py-0.5 text-[10px] font-semibold rounded-full bg-orange-100 text-orange-800 dark:bg-orange-900/40 dark:text-orange-300"
|
||||
title={`${openClarifications} offene Klärung${openClarifications === 1 ? '' : 'en'}`}
|
||||
>
|
||||
{openClarifications}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
Reference in New Issue
Block a user