'use client' import { useState, useEffect } from 'react' import { useParams } from 'next/navigation' import { REDUCTION_TYPES, Mitigation } from './_components/types' import { HierarchyWarning } from './_components/HierarchyWarning' import { MeasuresLibraryModal } from './_components/MeasuresLibraryModal' import { SuggestMeasuresModal } from './_components/SuggestMeasuresModal' import { MitigationForm } from './_components/MitigationForm' import { StatusBadge } from './_components/StatusBadge' import { ProtectiveMeasure } from './_components/types' import { useMitigations } from './_hooks/useMitigations' export default function MitigationsPage() { const params = useParams() const projectId = params.projectId as string const { hazards, loading, hierarchyWarning, setHierarchyWarning, measures, byType, fetchMeasuresLibrary, handleSubmit, handleAddSuggestedMeasure, handleVerify, handleDelete, } = useMitigations(projectId) const [measureNorms, setMeasureNorms] = useState>({}) useEffect(() => { fetch('/api/sdk/v1/iace/protective-measures-library') .then(r => r.ok ? r.json() : null) .then(json => { if (!json?.protective_measures) return const map: Record = {} for (const m of json.protective_measures) { if (m.norm_references?.length > 0) { map[(m.name || '').toLowerCase()] = m.norm_references } } setMeasureNorms(map) }) .catch(() => {}) }, []) const [showForm, setShowForm] = useState(false) const [preselectedType, setPreselectedType] = useState<'design' | 'protection' | 'information' | undefined>() const [showLibrary, setShowLibrary] = useState(false) const [libraryFilter, setLibraryFilter] = useState() const [showSuggest, setShowSuggest] = useState(false) const [expanded, setExpanded] = useState>({ design: true, protection: true, information: true }) const [mitPages, setMitPages] = useState>({ design: 1, protection: 1, information: 1 }) const [selected, setSelected] = useState>(new Set()) const [batchAction, setBatchAction] = useState<'verify' | 'delete' | null>(null) const [expandedMeasure, setExpandedMeasure] = useState(null) function toggleSection(type: string) { setExpanded((prev) => ({ ...prev, [type]: !prev[type] })) } function toggleSelect(id: string) { setSelected((prev) => { const next = new Set(prev) if (next.has(id)) next.delete(id); else next.add(id) return next }) } function selectAllInType(type: string) { const items = byType[type as keyof typeof byType] setSelected((prev) => { const next = new Set(prev) const allSelected = items.every((m) => next.has(m.id)) if (allSelected) { items.forEach((m) => next.delete(m.id)) } else { items.forEach((m) => next.add(m.id)) } return next }) } async function handleBatchVerify() { setBatchAction('verify') for (const id of selected) { await handleVerify(id) } setSelected(new Set()) setBatchAction(null) } async function handleBatchDelete() { if (!confirm(`${selected.size} Massnahmen wirklich loeschen?`)) return setBatchAction('delete') for (const id of selected) { await handleDelete(id) } setSelected(new Set()) setBatchAction(null) } function handleOpenLibrary(type?: string) { setLibraryFilter(type) fetchMeasuresLibrary(type) setShowLibrary(true) } function handleSelectMeasure(measure: ProtectiveMeasure) { setShowLibrary(false) setShowForm(true) setPreselectedType(measure.reduction_type as 'design' | 'protection' | 'information') } if (loading) { return (
) } const totalMeasures = byType.design.length + byType.protection.length + byType.information.length return (
{/* Header */}

Massnahmen

{totalMeasures} Massnahmen nach 3-Stufen-Verfahren: Design ({byType.design.length}) → Schutz ({byType.protection.length}) → Information ({byType.information.length})

{selected.size > 0 && ( <> {selected.size} ausgewaehlt )} {selected.size === 0 && ( <> )}
{hierarchyWarning && setHierarchyWarning(false)} />} {showForm && ( { const ok = await handleSubmit(data); if (ok) setShowForm(false) }} onCancel={() => setShowForm(false)} hazards={hazards} preselectedType={preselectedType} onOpenLibrary={handleOpenLibrary} /> )} {showLibrary && setShowLibrary(false)} filterType={libraryFilter} />} {showSuggest && setShowSuggest(false)} />} {/* 3-Step Accordions */} {(['design', 'protection', 'information'] as const).map((type) => { const config = REDUCTION_TYPES[type] const items = byType[type] const isExpanded = expanded[type] const allSelected = items.length > 0 && items.every((m) => selected.has(m.id)) return (
{/* Accordion Header */} {/* Accordion Content — Table rows */} {isExpanded && items.length > 0 && (
{/* Table header */}
selectAllInType(type)} className="accent-purple-600" title="Alle auswaehlen" />
Massnahme
Gefaehrdung
Status
{/* Rows — paginated */} {items.slice(0, (mitPages[type] || 1) * 50).map((m) => { const isDetailOpen = expandedMeasure === m.id const catMatch = (m.description || '').match(/Kategorie\s+(\S+)/) const category = catMatch?.[1] const refs = measureNorms[(m.title || '').toLowerCase()] return (
setExpandedMeasure(isDetailOpen ? null : m.id)} className={`grid grid-cols-[24px_2fr_1fr_80px] gap-2 px-4 py-2 border-t border-gray-50 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors cursor-pointer ${selected.has(m.id) ? 'bg-purple-50 dark:bg-purple-900/10' : ''}`}>
e.stopPropagation()}> toggleSelect(m.id)} className="accent-purple-600" />
{m.title || ''}
{!isDetailOpen && category &&
Kategorie: {category}
}
{(m.linked_hazard_names || []).join(', ') || '-'}
{isDetailOpen && (
{m.description &&

{m.description}

} {category &&

Diese Massnahme gilt fuer alle Gefaehrdungen der Kategorie {category}.

} {refs?.length > 0 &&

Normen: {refs.join(', ')}

}
)}
) })} {items.length > (mitPages[type] || 1) * 50 && ( )}
)} {isExpanded && items.length === 0 && (
Keine Massnahmen in dieser Stufe
)}
) })}
) }