fix(admin/iace/benchmark): show Klaerungsfragen + Normen on Engine column

The Go init handler appends two annotated blocks to Hazard.Description
("Mit Anlagenbauer zu klaeren: ..." and "Referenzierte Normen: ...")
without changing the DB schema. The benchmark detail view only rendered
hazard.scenario || hazard.description, so the appended blocks were
silently hidden because scenario is always populated.

Split the description into three structured pieces:
1. extractScenario() — pure scenario text, stripped of trailing blocks
2. extractClarifications() — bullet list of "Mit Anlagenbauer zu klaeren"
3. extractEngineNorms() — pipe-separated norm references

Each piece is rendered as its own DetailRow. The FANUC DCS clarification
that already lives in the DB (48/115 hazards on the Bremse project) is
now visible in the Engine column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-17 00:42:41 +02:00
parent 5f2da1de88
commit 74f66c4c34
@@ -163,7 +163,7 @@ function DetailComparison({ gt, engine }: { gt: GroundTruthEntry; engine: Hazard
<div className="space-y-2">
<div className="font-semibold text-purple-700 dark:text-purple-400 uppercase text-[10px]">Engine (automatisch)</div>
<DetailRow label="Gefaehrdung" gt={engine.name} />
<DetailRow label="Szenario" gt={engine.scenario || engine.description || '-'} />
<DetailRow label="Szenario" gt={engine.scenario || extractScenario(engine.description) || '-'} />
<DetailRow label="Gefahrenstelle" gt={engine.zone || '-'} />
{engine.lifecycle_phase && (
<DetailRow label="Lebensphasen" gt={formatLifecycles(engine.lifecycle_phase)} />
@@ -178,11 +178,55 @@ function DetailComparison({ gt, engine }: { gt: GroundTruthEntry; engine: Hazard
) : (
<DetailRow label="Massnahmen" gt="(keine zugeordnet)" />
)}
{(() => {
const clarifications = extractClarifications(engine.description)
if (clarifications.length === 0) return null
return <DetailRow label="Mit Anlagenbauer zu klaeren" gt={clarifications.map(c => '• ' + c).join('\n')} multiline />
})()}
{(() => {
const norms = extractEngineNorms(engine.description)
if (norms.length === 0) return null
return <DetailRow label="Referenzierte Normen" gt={norms.join(' | ')} />
})()}
</div>
</div>
)
}
/**
* The Go init handler appends two annotated blocks to Hazard.Description:
* "<scenario>\n\nMit Anlagenbauer zu klaeren:\n- frage 1\n- frage 2\n\n
* Referenzierte Normen: EN 60204-1 Ziff. 6.2 | EN 61140"
* These helpers split that string back into structured pieces so the UI
* can render scenario, clarifications and norms as separate sections.
*/
function extractScenario(desc?: string): string {
if (!desc) return ''
const idx = desc.indexOf('\n\nMit Anlagenbauer zu klaeren')
const cut = idx >= 0 ? desc.slice(0, idx) : desc
// Also cut off a trailing norm line if it's the only suffix
const normIdx = cut.indexOf('\n\nReferenzierte Normen')
return (normIdx >= 0 ? cut.slice(0, normIdx) : cut).trim()
}
function extractClarifications(desc?: string): string[] {
if (!desc) return []
const start = desc.indexOf('Mit Anlagenbauer zu klaeren:')
if (start < 0) return []
const after = desc.slice(start + 'Mit Anlagenbauer zu klaeren:'.length)
// Stop at the next double-newline section (e.g. norm block)
const stop = after.indexOf('\n\n')
const block = stop >= 0 ? after.slice(0, stop) : after
return block.split('\n').map(s => s.replace(/^[\s-]+/, '').trim()).filter(Boolean)
}
function extractEngineNorms(desc?: string): string[] {
if (!desc) return []
const m = desc.match(/Referenzierte Normen:\s*([^\n]+)/)
if (!m) return []
return m[1].split('|').map(s => s.trim()).filter(Boolean)
}
function DetailRow({ label, gt, multiline }: { label: string; gt: string; multiline?: boolean }) {
return (
<div>