'use client' import React, { useState, useEffect } from 'react' import { useParams } from 'next/navigation' interface Mitigation { id: string title: string description: string reduction_type: 'design' | 'protection' | 'information' status: 'planned' | 'implemented' | 'verified' linked_hazard_ids: string[] linked_hazard_names: string[] created_at: string verified_at: string | null verified_by: string | null source?: string } interface Hazard { id: string name: string risk_level: string category?: string } interface ProtectiveMeasure { id: string reduction_type: string sub_type: string name: string description: string hazard_category: string examples: string[] } interface SuggestedMeasure { id: string reduction_type: string sub_type: string name: string description: string hazard_category: string examples: string[] tags?: string[] } const REDUCTION_TYPES = { design: { label: 'Stufe 1: Design', description: 'Inhaerent sichere Konstruktion', color: 'border-blue-200 bg-blue-50', headerColor: 'bg-blue-100 text-blue-800', subTypes: [ { value: 'geometry', label: 'Geometrie & Anordnung' }, { value: 'force_energy', label: 'Kraft & Energie' }, { value: 'material', label: 'Material & Stabilitaet' }, { value: 'ergonomics', label: 'Ergonomie' }, { value: 'control_design', label: 'Steuerungstechnik' }, { value: 'fluid_design', label: 'Pneumatik / Hydraulik' }, ], icon: ( ), }, protection: { label: 'Stufe 2: Schutz', description: 'Technische Schutzmassnahmen', color: 'border-green-200 bg-green-50', headerColor: 'bg-green-100 text-green-800', subTypes: [ { value: 'fixed_guard', label: 'Feststehende Schutzeinrichtung' }, { value: 'movable_guard', label: 'Bewegliche Schutzeinrichtung' }, { value: 'electro_sensitive', label: 'Optoelektronisch' }, { value: 'pressure_sensitive', label: 'Druckempfindlich' }, { value: 'emergency_stop', label: 'Not-Halt' }, { value: 'electrical_protection', label: 'Elektrischer Schutz' }, { value: 'thermal_protection', label: 'Thermischer Schutz' }, { value: 'fluid_protection', label: 'Hydraulik/Pneumatik-Schutz' }, { value: 'extraction', label: 'Absaugung / Kapselung' }, ], icon: ( ), }, information: { label: 'Stufe 3: Information', description: 'Hinweise und Schulungen', color: 'border-yellow-200 bg-yellow-50', headerColor: 'bg-yellow-100 text-yellow-800', subTypes: [ { value: 'signage', label: 'Beschilderung & Kennzeichnung' }, { value: 'manual', label: 'Betriebsanleitung' }, { value: 'training', label: 'Schulung & Unterweisung' }, { value: 'ppe', label: 'PSA (Schutzausruestung)' }, { value: 'organizational', label: 'Organisatorisch' }, { value: 'marking', label: 'Markierung & Codierung' }, ], icon: ( ), }, } function StatusBadge({ status }: { status: string }) { const colors: Record = { planned: 'bg-gray-100 text-gray-700', implemented: 'bg-blue-100 text-blue-700', verified: 'bg-green-100 text-green-700', } const labels: Record = { planned: 'Geplant', implemented: 'Umgesetzt', verified: 'Verifiziert', } return ( {labels[status] || status} ) } function HierarchyWarning({ onDismiss }: { onDismiss: () => void }) { return (

Hierarchie-Warnung: Massnahmen vom Typ "Information"

Hinweismassnahmen (Stufe 3) duerfen nicht als Primaermassnahme akzeptiert werden, wenn konstruktive (Stufe 1) oder technische (Stufe 2) Massnahmen moeglich und zumutbar sind. Pruefen Sie, ob hoeherwertige Massnahmen ergaenzt werden koennen.

) } function MeasuresLibraryModal({ measures, onSelect, onClose, filterType, }: { measures: ProtectiveMeasure[] onSelect: (measure: ProtectiveMeasure) => void onClose: () => void filterType?: string }) { const [search, setSearch] = useState('') const [selectedSubType, setSelectedSubType] = useState('') const filtered = measures.filter((m) => { if (filterType && m.reduction_type !== filterType) return false if (selectedSubType && m.sub_type !== selectedSubType) return false if (search) { const q = search.toLowerCase() return m.name.toLowerCase().includes(q) || m.description.toLowerCase().includes(q) } return true }) const subTypes = [...new Set(measures.filter((m) => !filterType || m.reduction_type === filterType).map((m) => m.sub_type))].filter(Boolean) return (

Massnahmen-Bibliothek

setSearch(e.target.value)} placeholder="Massnahme suchen..." className="flex-1 px-4 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" /> {subTypes.length > 1 && ( )}
{filtered.length} Massnahmen
{filtered.map((m) => (
onSelect(m)} >
{m.id} {m.sub_type && ( {m.sub_type} )}

{m.name}

{m.description}

{m.examples && m.examples.length > 0 && (
{m.examples.map((ex, i) => ( {ex} ))}
)}
))} {filtered.length === 0 && (
Keine Massnahmen gefunden
)}
) } // ============================================================================ // Suggest Measures Modal (Phase 5) // ============================================================================ function SuggestMeasuresModal({ hazards, projectId, onAddMeasure, onClose, }: { hazards: Hazard[] projectId: string onAddMeasure: (title: string, description: string, reductionType: string, hazardId: string) => void onClose: () => void }) { const [selectedHazard, setSelectedHazard] = useState('') const [suggested, setSuggested] = useState([]) const [loadingSuggestions, setLoadingSuggestions] = useState(false) const riskColors: Record = { not_acceptable: 'border-red-400 bg-red-50', very_high: 'border-red-300 bg-red-50', critical: 'border-red-300 bg-red-50', high: 'border-orange-300 bg-orange-50', medium: 'border-yellow-300 bg-yellow-50', low: 'border-green-300 bg-green-50', } async function handleSelectHazard(hazardId: string) { setSelectedHazard(hazardId) setSuggested([]) if (!hazardId) return setLoadingSuggestions(true) try { const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards/${hazardId}/suggest-measures`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }) if (res.ok) { const json = await res.json() setSuggested(json.suggested_measures || []) } } catch (err) { console.error('Failed to suggest measures:', err) } finally { setLoadingSuggestions(false) } } const groupedByType = { design: suggested.filter(m => m.reduction_type === 'design'), protection: suggested.filter(m => m.reduction_type === 'protection'), information: suggested.filter(m => m.reduction_type === 'information'), } return (

Massnahmen-Vorschlaege

Waehlen Sie eine Gefaehrdung, um passende Massnahmen vorgeschlagen zu bekommen.

{hazards.map(h => ( ))}
{loadingSuggestions ? (
) : suggested.length > 0 ? (
{(['design', 'protection', 'information'] as const).map(type => { const items = groupedByType[type] if (items.length === 0) return null const config = REDUCTION_TYPES[type] return (
{config.icon} {config.label} {items.length}
{items.map(m => (
{m.id} {m.sub_type && ( {m.sub_type} )}
{m.name}
{m.description}
))}
) })}
) : selectedHazard ? (
Keine Vorschlaege fuer diese Gefaehrdung gefunden.
) : (
Waehlen Sie eine Gefaehrdung aus, um Vorschlaege zu erhalten.
)}
) } interface MitigationFormData { title: string description: string reduction_type: 'design' | 'protection' | 'information' linked_hazard_ids: string[] } function MitigationForm({ onSubmit, onCancel, hazards, preselectedType, onOpenLibrary, }: { onSubmit: (data: MitigationFormData) => void onCancel: () => void hazards: Hazard[] preselectedType?: 'design' | 'protection' | 'information' onOpenLibrary: (type?: string) => void }) { const [formData, setFormData] = useState({ title: '', description: '', reduction_type: preselectedType || 'design', linked_hazard_ids: [], }) function toggleHazard(id: string) { setFormData((prev) => ({ ...prev, linked_hazard_ids: prev.linked_hazard_ids.includes(id) ? prev.linked_hazard_ids.filter((h) => h !== id) : [...prev.linked_hazard_ids, id], })) } return (

Neue Massnahme

setFormData({ ...formData, title: e.target.value })} placeholder="z.B. Lichtvorhang an Gefahrenstelle" className="w-full px-4 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" />