refactor(admin): split evidence, process-tasks, iace/hazards pages

Extract components and hooks into _components/ and _hooks/ subdirectories
to reduce each page.tsx to under 500 LOC (was 1545/1383/1316).

Final line counts: evidence=213, process-tasks=304, hazards=157.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-16 17:12:15 +02:00
parent e0c1d21879
commit 1fcd8244b1
27 changed files with 2621 additions and 4083 deletions

View File

@@ -0,0 +1,173 @@
'use client'
import { useState, useEffect } from 'react'
import {
Hazard, LibraryHazard, LifecyclePhase, RoleInfo, HazardFormData, MatchOutput,
CATEGORY_LABELS,
} from '../_components/types'
export function useHazards(projectId: string) {
const [hazards, setHazards] = useState<Hazard[]>([])
const [library, setLibrary] = useState<LibraryHazard[]>([])
const [lifecyclePhases, setLifecyclePhases] = useState<LifecyclePhase[]>([])
const [roles, setRoles] = useState<RoleInfo[]>([])
const [loading, setLoading] = useState(true)
const [showForm, setShowForm] = useState(false)
const [showLibrary, setShowLibrary] = useState(false)
const [suggestingAI, setSuggestingAI] = useState(false)
const [matchingPatterns, setMatchingPatterns] = useState(false)
const [matchResult, setMatchResult] = useState<MatchOutput | null>(null)
const [applyingPatterns, setApplyingPatterns] = useState(false)
useEffect(() => {
fetchHazards()
fetchLifecyclePhases()
fetchRoles()
}, [projectId]) // eslint-disable-line react-hooks/exhaustive-deps
async function fetchHazards() {
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards`)
if (res.ok) {
const json = await res.json()
setHazards(json.hazards || json || [])
}
} catch (err) {
console.error('Failed to fetch hazards:', err)
} finally {
setLoading(false)
}
}
async function fetchLifecyclePhases() {
try {
const res = await fetch('/api/sdk/v1/iace/lifecycle-phases')
if (res.ok) { const json = await res.json(); setLifecyclePhases(json.lifecycle_phases || []) }
} catch (err) { console.error('Failed to fetch lifecycle phases:', err) }
}
async function fetchRoles() {
try {
const res = await fetch('/api/sdk/v1/iace/roles')
if (res.ok) { const json = await res.json(); setRoles(json.roles || []) }
} catch (err) { console.error('Failed to fetch roles:', err) }
}
async function fetchLibrary() {
try {
const res = await fetch('/api/sdk/v1/iace/hazard-library')
if (res.ok) { const json = await res.json(); setLibrary(json.hazard_library || json.hazards || json || []) }
} catch (err) { console.error('Failed to fetch hazard library:', err) }
setShowLibrary(true)
}
async function handleAddFromLibrary(item: LibraryHazard) {
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: item.name, description: item.description, category: item.category,
sub_category: item.sub_category || '', severity: item.default_severity,
exposure: item.default_exposure || 3, probability: item.default_probability,
avoidance: item.default_avoidance || 3,
}),
})
if (res.ok) await fetchHazards()
} catch (err) { console.error('Failed to add from library:', err) }
}
async function handleSubmit(data: HazardFormData) {
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
if (res.ok) { setShowForm(false); await fetchHazards() }
} catch (err) { console.error('Failed to add hazard:', err) }
}
async function handleAISuggestions() {
setSuggestingAI(true)
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards/suggest`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
if (res.ok) await fetchHazards()
} catch (err) { console.error('Failed to get AI suggestions:', err) }
finally { setSuggestingAI(false) }
}
async function handlePatternMatching() {
setMatchingPatterns(true)
setMatchResult(null)
try {
const compRes = await fetch(`/api/sdk/v1/iace/projects/${projectId}/components`)
let componentLibraryIds: string[] = []
let energySourceIds: string[] = []
if (compRes.ok) {
const compJson = await compRes.json()
const comps = compJson.components || compJson || []
componentLibraryIds = comps.map((c: { library_component_id?: string }) => c.library_component_id).filter(Boolean) as string[]
const allEnergyIds = comps.flatMap((c: { energy_source_ids?: string[] }) => c.energy_source_ids || [])
energySourceIds = [...new Set(allEnergyIds)] as string[]
}
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/match-patterns`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ component_library_ids: componentLibraryIds, energy_source_ids: energySourceIds, lifecycle_phases: [], custom_tags: [] }),
})
if (res.ok) setMatchResult(await res.json())
} catch (err) { console.error('Failed to match patterns:', err) }
finally { setMatchingPatterns(false) }
}
async function handleApplyPatterns(
acceptedHazardCats: string[], acceptedMeasureIds: string[],
acceptedEvidenceIds: string[], patternIds: string[],
) {
setApplyingPatterns(true)
try {
const acceptedHazards = acceptedHazardCats.map(cat => ({
name: `Auto: ${CATEGORY_LABELS[cat] || cat}`,
description: `Automatisch erkannte Gefaehrdung aus Pattern-Matching (Kategorie: ${cat})`,
category: cat, severity: 3, exposure: 3, probability: 3, avoidance: 3,
}))
const acceptedMeasures = acceptedMeasureIds.map(id => ({
name: `Auto: Massnahme ${id}`,
description: `Automatisch vorgeschlagene Massnahme aus Pattern-Matching`,
reduction_type: 'design',
}))
const acceptedEvidence = acceptedEvidenceIds.map(id => ({
title: `Auto: Nachweis ${id}`,
description: `Automatisch vorgeschlagener Nachweis aus Pattern-Matching`,
method: 'test_report',
}))
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/apply-patterns`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accepted_hazards: acceptedHazards, accepted_measures: acceptedMeasures, accepted_evidence: acceptedEvidence, source_pattern_ids: patternIds }),
})
if (res.ok) { setMatchResult(null); await fetchHazards() }
} catch (err) { console.error('Failed to apply patterns:', err) }
finally { setApplyingPatterns(false) }
}
async function handleDelete(id: string) {
if (!confirm('Gefaehrdung wirklich loeschen?')) return
try {
const res = await fetch(`/api/sdk/v1/iace/projects/${projectId}/hazards/${id}`, { method: 'DELETE' })
if (res.ok) await fetchHazards()
} catch (err) { console.error('Failed to delete hazard:', err) }
}
return {
hazards, library, lifecyclePhases, roles, loading,
showForm, setShowForm, showLibrary, setShowLibrary,
suggestingAI, matchingPatterns, matchResult, setMatchResult, applyingPatterns,
fetchLibrary, handleAddFromLibrary, handleSubmit,
handleAISuggestions, handlePatternMatching, handleApplyPatterns, handleDelete,
}
}