feat(iace): surface OSHA distance anchor in Maßnahmen tab (name-resolved)
Makes the OSHA minimum-distance anchor visible per measure in a project without a DB schema change or re-seed: persisted mitigations store the measure NAME verbatim (not the catalog ID), and measure names are unique across the 578-entry library (pinned by test), so a name→ID resolver bridges the gap. Backend: MeasureIDByName + MinimumDistancesForMeasureName/LinksForMeasureName; /iace/minimum-distances now accepts ?measure_name=; link table enriched with measure_name for one-request UI matching. Frontend: useMinimumDistances loads the link table once and keys it by name; OshaDistanceNote renders the anchor (value/CFR/license/EU-hint/relation) on the matching measure group in the Maßnahmen tab. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export interface EuNormHint {
|
||||
norm: string
|
||||
section?: string
|
||||
din_comparison_note?: string
|
||||
}
|
||||
|
||||
export interface MinimumDistance {
|
||||
id: string
|
||||
source_cfr?: string
|
||||
source_table?: string
|
||||
license: string
|
||||
context: string
|
||||
body_part?: string
|
||||
recommended_mm?: number
|
||||
recommended_min_mm?: number
|
||||
recommended_max_mm?: number
|
||||
formula_description?: string
|
||||
formula_mm_per_second?: number
|
||||
rounding_note?: string
|
||||
eu_norm_hints?: EuNormHint[]
|
||||
}
|
||||
|
||||
export interface MeasureDistanceLink {
|
||||
measure_id: string
|
||||
measure_name?: string
|
||||
distance_ids: string[]
|
||||
relation: string // "value_source" | "public_domain_crossref"
|
||||
note?: string
|
||||
}
|
||||
|
||||
export interface OshaAnchor {
|
||||
link: MeasureDistanceLink
|
||||
distances: MinimumDistance[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the OSHA minimum-distance link table ONCE and returns a lookup keyed by
|
||||
* (lower-cased) measure name. A persisted mitigation stores the measure name
|
||||
* verbatim, so the Maßnahmen tab can surface the OSHA anchor by matching on name
|
||||
* — no per-row request, no catalog ID needed.
|
||||
*/
|
||||
export function useMinimumDistances() {
|
||||
const [byName, setByName] = useState<Record<string, OshaAnchor>>({})
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
async function load() {
|
||||
try {
|
||||
const res = await fetch('/api/sdk/v1/iace/minimum-distances')
|
||||
if (!res.ok) return
|
||||
const json = (await res.json()) as { distances: MinimumDistance[]; links: MeasureDistanceLink[] }
|
||||
const byId = Object.fromEntries((json.distances || []).map((d) => [d.id, d]))
|
||||
const map: Record<string, OshaAnchor> = {}
|
||||
for (const link of json.links || []) {
|
||||
if (!link.measure_name) continue
|
||||
map[link.measure_name.toLowerCase()] = {
|
||||
link,
|
||||
distances: link.distance_ids.map((id) => byId[id]).filter(Boolean),
|
||||
}
|
||||
}
|
||||
if (!cancelled) setByName(map)
|
||||
} catch (err) {
|
||||
console.error('Failed to load minimum distances:', err)
|
||||
}
|
||||
}
|
||||
load()
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [])
|
||||
|
||||
return { byName }
|
||||
}
|
||||
Reference in New Issue
Block a user