From a93ba9ee40fa9408a5145bb117575b92d9b1eceb Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 7 May 2026 16:09:50 +0200 Subject: [PATCH] feat: Custom Hazard Modal + Residual Risk Panel - CustomHazardModal: Eigene Gefaehrdung erstellen mit S/E/P/A Slidern - ResidualRiskPanel: Akzeptabel-Toggle pro Hazard + Fortschrittsbalken - RiskAssessmentTable: Accept/Reject Buttons pro Zeile integriert Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hazards/_components/CustomHazardModal.tsx | 195 ++++++++++++++++++ .../hazards/_components/ResidualRiskPanel.tsx | 115 +++++++++++ .../_components/RiskAssessmentTable.tsx | 35 +++- .../app/sdk/iace/[projectId]/hazards/page.tsx | 49 ++++- 4 files changed, 386 insertions(+), 8 deletions(-) create mode 100644 admin-compliance/app/sdk/iace/[projectId]/hazards/_components/CustomHazardModal.tsx create mode 100644 admin-compliance/app/sdk/iace/[projectId]/hazards/_components/ResidualRiskPanel.tsx diff --git a/admin-compliance/app/sdk/iace/[projectId]/hazards/_components/CustomHazardModal.tsx b/admin-compliance/app/sdk/iace/[projectId]/hazards/_components/CustomHazardModal.tsx new file mode 100644 index 0000000..3d9efc2 --- /dev/null +++ b/admin-compliance/app/sdk/iace/[projectId]/hazards/_components/CustomHazardModal.tsx @@ -0,0 +1,195 @@ +'use client' + +import { useState } from 'react' +import { + HazardFormData, HAZARD_CATEGORIES, CATEGORY_LABELS, getRiskColor, getRiskLevelISO, RoleInfo, +} from './types' +import { RiskBadge } from './RiskBadge' + +interface CustomHazardModalProps { + onSubmit: (data: HazardFormData) => void + onClose: () => void + roles: RoleInfo[] +} + +const INITIAL_FORM: HazardFormData = { + name: '', description: '', category: 'mechanical', component_id: '', + severity: 3, exposure: 3, probability: 3, avoidance: 3, + lifecycle_phase: '', trigger_event: '', affected_person: '', + possible_harm: '', hazardous_zone: '', machine_module: '', +} + +export function CustomHazardModal({ onSubmit, onClose, roles }: CustomHazardModalProps) { + const [form, setForm] = useState(INITIAL_FORM) + const [submitting, setSubmitting] = useState(false) + + const rInherent = form.severity * form.exposure * form.probability * form.avoidance + const riskLevel = getRiskLevelISO(rInherent) + + function set(key: K, val: HazardFormData[K]) { + setForm(prev => ({ ...prev, [key]: val })) + } + + async function handleSave() { + if (!form.name.trim()) return + setSubmitting(true) + try { + await onSubmit(form) + } finally { + setSubmitting(false) + } + } + + const inputCls = 'w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent dark:bg-gray-700 dark:border-gray-600 dark:text-white text-sm' + const labelCls = 'block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1' + + return ( +
+
+
+
+
+

Eigene Gefaehrdung erstellen

+

+ Maschinenspezifische Gefaehrdung definieren, die nicht in der Bibliothek enthalten ist. +

+
+ +
+ +
+ {/* Name + Category */} +
+
+ + set('name', e.target.value)} + placeholder="z.B. Quetschung durch Sondergreifer" className={inputCls} /> +
+
+ + +
+
+ + {/* Scenario */} +
+ +