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:
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user