feat: IACE CE-Compliance Module — Normen, Risikobewertung, Production Lines
Major features: - 215 norms library with section references + Beuth URLs (A/B1/B2/C norms) - 173 hazard patterns with detail fields (scenario, trigger, harm, zone) - Deterministic pattern matching: Component × Lifecycle × Pattern cross-product - SIL/PL auto-calculation from S×E×P risk graph - Risk assessment table with editable S/E/P dropdowns - Production Line Dashboard with animated station flow (Running Dots) - IACE process flow + norms coverage on start page - Non-blocking cookie banner, ProcessFlow SSR fix - 104 Playwright E2E tests passing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { ProcessFlow } from './_components/ProcessFlow'
|
||||
|
||||
interface IACEProject {
|
||||
id: string
|
||||
@@ -10,7 +11,7 @@ interface IACEProject {
|
||||
manufacturer: string
|
||||
status: string
|
||||
completeness_pct: number
|
||||
risk_summary: {
|
||||
risk_summary?: {
|
||||
critical: number
|
||||
high: number
|
||||
medium: number
|
||||
@@ -54,34 +55,35 @@ function CompletenessBar({ pct }: { pct: number }) {
|
||||
)
|
||||
}
|
||||
|
||||
function RiskDots({ summary }: { summary: IACEProject['risk_summary'] }) {
|
||||
function RiskDots({ summary }: { summary?: IACEProject['risk_summary'] }) {
|
||||
const s = summary || { critical: 0, high: 0, medium: 0, low: 0 }
|
||||
return (
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
{summary.critical > 0 && (
|
||||
{s.critical > 0 && (
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-red-500" />
|
||||
<span className="text-gray-600">{summary.critical}</span>
|
||||
<span className="text-gray-600">{s.critical}</span>
|
||||
</span>
|
||||
)}
|
||||
{summary.high > 0 && (
|
||||
{s.high > 0 && (
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-orange-500" />
|
||||
<span className="text-gray-600">{summary.high}</span>
|
||||
<span className="text-gray-600">{s.high}</span>
|
||||
</span>
|
||||
)}
|
||||
{summary.medium > 0 && (
|
||||
{s.medium > 0 && (
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-yellow-500" />
|
||||
<span className="text-gray-600">{summary.medium}</span>
|
||||
<span className="text-gray-600">{s.medium}</span>
|
||||
</span>
|
||||
)}
|
||||
{summary.low > 0 && (
|
||||
{s.low > 0 && (
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="w-2.5 h-2.5 rounded-full bg-green-500" />
|
||||
<span className="text-gray-600">{summary.low}</span>
|
||||
<span className="text-gray-600">{s.low}</span>
|
||||
</span>
|
||||
)}
|
||||
{summary.critical === 0 && summary.high === 0 && summary.medium === 0 && summary.low === 0 && (
|
||||
{s.critical === 0 && s.high === 0 && s.medium === 0 && s.low === 0 && (
|
||||
<span className="text-gray-400">Keine Risiken</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -142,7 +144,13 @@ export default function IACEDashboardPage() {
|
||||
const res = await fetch('/api/sdk/v1/iace/projects')
|
||||
if (res.ok) {
|
||||
const json = await res.json()
|
||||
setProjects(json.projects || json || [])
|
||||
const raw = json.projects || json || []
|
||||
// Map API fields to frontend expectations
|
||||
setProjects(raw.map((p: Record<string, unknown>) => ({
|
||||
...p,
|
||||
completeness_pct: p.completeness_pct ?? p.completeness_score ?? 0,
|
||||
risk_summary: p.risk_summary || { critical: 0, high: 0, medium: 0, low: 0 },
|
||||
})))
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch IACE projects:', err)
|
||||
@@ -219,6 +227,36 @@ export default function IACEDashboardPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Production Lines Quick Access */}
|
||||
<Link
|
||||
href="/sdk/iace/lines"
|
||||
className="block bg-gradient-to-r from-purple-50 to-indigo-50 dark:from-purple-900/20 dark:to-indigo-900/20 rounded-xl border border-purple-200 dark:border-purple-800 p-6 hover:shadow-md hover:border-purple-300 transition-all group"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-purple-100 dark:bg-purple-900/40 rounded-xl flex items-center justify-center flex-shrink-0">
|
||||
<svg className="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
Produktionslinien
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Verkettete Fertigungsstrassen mit aggregierter Risikoansicht und animiertem Stationsfluss
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<svg className="w-5 h-5 text-purple-400 group-hover:text-purple-600 transition-colors flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Process Flow */}
|
||||
<ProcessFlow />
|
||||
|
||||
{/* Create Form */}
|
||||
{showCreateForm && (
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-6">
|
||||
|
||||
Reference in New Issue
Block a user