feat(iace-frontend): Risikobewertung tab with dual risk model + live formula
CI / detect-changes (push) Successful in 7s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / build-sha-integrity (push) Failing after 4s
CI / validate-canonical-controls (push) Successful in 11s
CI / nodejs-build (push) Successful in 2m23s
CI / test-go (push) Has been skipped
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / loc-budget (push) Successful in 14s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped

New tab /sdk/iace/[projectId]/risikobewertung. Per hazard it shows BOTH models
side by side — EN-62061-style (S/F/W/P) and Fine-Kinney (P/E/C) — with
BreakPilot's justified suggested values from public data, the visible formula,
and editable fields that recompute the score + risk band live. The professional
adjusts the values (e.g. from his own licensed DIN/Beuth data); we only supply
the formula + inputs, reproduce no norm table.

Consumes GET .../hazards/:hid/risk-suggestion. Registered in IACE_NAV_ITEMS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-06-09 15:40:59 +02:00
parent 77536f04b7
commit c6ebe61162
4 changed files with 287 additions and 0 deletions
@@ -0,0 +1,85 @@
'use client'
import { useEffect, useState } from 'react'
export interface SuggestedValue {
value: number
justification: string
}
export interface RiskSuggestion {
hazard_id: string
contact_mode: string
en62061: {
severity: SuggestedValue
frequency: SuggestedValue
probability: SuggestedValue
avoidance: SuggestedValue
score: number
level: string
formula: string
}
fine_kinney: {
probability: SuggestedValue
exposure: SuggestedValue
consequence: SuggestedValue
score: number
band: string
action: string
formula: string
}
note: string
}
export interface HazardLite {
id: string
name: string
category: string
scenario?: string
}
export function useRiskAssessment(projectId: string) {
const [hazards, setHazards] = useState<HazardLite[]>([])
const [suggestions, setSuggestions] = useState<Record<string, RiskSuggestion>>({})
const [loading, setLoading] = useState(true)
useEffect(() => {
let cancelled = false
async function load() {
setLoading(true)
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards`)
const json = res.ok ? await res.json() : {}
const hz: HazardLite[] = json.hazards || json || []
if (cancelled) return
setHazards(hz)
const entries = await Promise.all(
hz.map(async (h) => {
try {
const r = await fetch(
`/api/sdk/v1/iace/projects/${projectId}/hazards/${h.id}/risk-suggestion`,
)
return r.ok ? ([h.id, (await r.json()) as RiskSuggestion] as const) : null
} catch {
return null
}
}),
)
if (cancelled) return
const map: Record<string, RiskSuggestion> = {}
for (const e of entries) if (e) map[e[0]] = e[1]
setSuggestions(map)
} catch (err) {
console.error('Failed to load risk assessment:', err)
} finally {
if (!cancelled) setLoading(false)
}
}
load()
return () => {
cancelled = true
}
}, [projectId])
return { hazards, suggestions, loading }
}