56892cf7dc
Automatische Erkennung von DSGVO/AI Act/CRA/NIS2/Data Act Implikationen bei CE-Gefaehrdungen. 50 Trigger-Mappings auf Hazard-Patterns → Compliance-Module mit Modul-Links. - compliance_triggers.go: 50 Pattern→Regulation Mappings - compliance_crossover.go: Engine die Projekt-Hazards gegen Trigger prueft - iace_handler_compliance.go: GET /compliance-triggers API - ComplianceAlerts.tsx: Frontend Alert-Panel auf Projekt-Uebersicht Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
219 lines
8.5 KiB
TypeScript
219 lines
8.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useEffect } from 'react'
|
|
import Link from 'next/link'
|
|
|
|
interface ComplianceTrigger {
|
|
id: string
|
|
regulation: string
|
|
article: string
|
|
title: string
|
|
severity: 'high' | 'medium' | 'low'
|
|
reason: string
|
|
affected_hazard_count?: number
|
|
module_path: string
|
|
module_label: string
|
|
}
|
|
|
|
interface TriggersResponse {
|
|
triggers: ComplianceTrigger[]
|
|
total: number
|
|
}
|
|
|
|
const SEVERITY_CONFIG: Record<string, { border: string; bg: string; text: string; badge: string; icon: string }> = {
|
|
high: {
|
|
border: 'border-red-200 dark:border-red-800',
|
|
bg: 'bg-red-50 dark:bg-red-900/20',
|
|
text: 'text-red-700 dark:text-red-400',
|
|
badge: 'bg-red-100 text-red-800 dark:bg-red-900/50 dark:text-red-300',
|
|
icon: 'text-red-500',
|
|
},
|
|
medium: {
|
|
border: 'border-yellow-200 dark:border-yellow-800',
|
|
bg: 'bg-yellow-50 dark:bg-yellow-900/20',
|
|
text: 'text-yellow-700 dark:text-yellow-400',
|
|
badge: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/50 dark:text-yellow-300',
|
|
icon: 'text-yellow-500',
|
|
},
|
|
low: {
|
|
border: 'border-blue-200 dark:border-blue-800',
|
|
bg: 'bg-blue-50 dark:bg-blue-900/20',
|
|
text: 'text-blue-700 dark:text-blue-400',
|
|
badge: 'bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-300',
|
|
icon: 'text-blue-500',
|
|
},
|
|
}
|
|
|
|
const SEVERITY_LABELS: Record<string, string> = {
|
|
high: 'HOCH',
|
|
medium: 'MITTEL',
|
|
low: 'NIEDRIG',
|
|
}
|
|
|
|
const REGULATION_BADGES: { key: string; label: string; activeColor: string }[] = [
|
|
{ key: 'DSGVO', label: 'DSGVO', activeColor: 'bg-red-100 text-red-800 border-red-300' },
|
|
{ key: 'AI Act', label: 'AI Act', activeColor: 'bg-orange-100 text-orange-800 border-orange-300' },
|
|
{ key: 'CRA', label: 'CRA', activeColor: 'bg-yellow-100 text-yellow-800 border-yellow-300' },
|
|
{ key: 'NIS2', label: 'NIS2', activeColor: 'bg-indigo-100 text-indigo-800 border-indigo-300' },
|
|
{ key: 'Data Act', label: 'Data Act', activeColor: 'bg-amber-100 text-amber-800 border-amber-300' },
|
|
]
|
|
|
|
function WarningIcon({ className }: { className?: string }) {
|
|
return (
|
|
<svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
function ChevronIcon({ open }: { open: boolean }) {
|
|
return (
|
|
<svg className={`w-4 h-4 text-gray-400 transition-transform ${open ? 'rotate-180' : ''}`}
|
|
fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function ComplianceAlerts({ projectId }: { projectId: string }) {
|
|
const [data, setData] = useState<TriggersResponse | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [collapsed, setCollapsed] = useState(false)
|
|
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set())
|
|
|
|
useEffect(() => {
|
|
fetch(`/api/sdk/v1/iace/projects/${projectId}/compliance-triggers`)
|
|
.then((r) => (r.ok ? r.json() : null))
|
|
.then((json) => {
|
|
if (json?.triggers) setData(json)
|
|
else if (Array.isArray(json)) setData({ triggers: json, total: json.length })
|
|
})
|
|
.catch(() => {})
|
|
.finally(() => setLoading(false))
|
|
}, [projectId])
|
|
|
|
if (loading) return null
|
|
if (!data || data.triggers.length === 0) return null
|
|
|
|
const triggers = data.triggers
|
|
const activeRegulations = new Set(triggers.map((t) => t.regulation))
|
|
|
|
function toggleExpanded(id: string) {
|
|
setExpandedIds((prev) => {
|
|
const next = new Set(prev)
|
|
if (next.has(id)) next.delete(id)
|
|
else next.add(id)
|
|
return next
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-gray-800 rounded-xl border border-red-200 dark:border-red-800">
|
|
{/* Header */}
|
|
<button
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
className="w-full flex items-center justify-between p-6 text-left"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-red-50 dark:bg-red-900/30 rounded-lg flex items-center justify-center">
|
|
<WarningIcon className="w-5 h-5 text-red-600" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-sm font-semibold text-gray-900 dark:text-white">
|
|
{triggers.length} Compliance-Hinweise erkannt
|
|
</h2>
|
|
<p className="text-xs text-gray-500">
|
|
Basierend auf den identifizierten Gefaehrdungen bestehen rechtliche Implikationen
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="w-8 h-8 flex items-center justify-center rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors flex-shrink-0">
|
|
<ChevronIcon open={!collapsed} />
|
|
</div>
|
|
</button>
|
|
|
|
{!collapsed && (
|
|
<div className="px-6 pb-6 space-y-4">
|
|
{/* Regulation summary badges */}
|
|
<div className="flex flex-wrap gap-2">
|
|
{REGULATION_BADGES.map((reg) => {
|
|
const active = activeRegulations.has(reg.key)
|
|
return (
|
|
<span
|
|
key={reg.key}
|
|
className={`px-2.5 py-1 text-xs font-medium rounded-full border ${
|
|
active
|
|
? reg.activeColor
|
|
: 'bg-gray-50 text-gray-400 border-gray-200 dark:bg-gray-700 dark:text-gray-500 dark:border-gray-600'
|
|
}`}
|
|
>
|
|
{reg.label}
|
|
</span>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{/* Trigger list */}
|
|
<div className="space-y-2">
|
|
{triggers.map((trigger) => {
|
|
const sev = SEVERITY_CONFIG[trigger.severity] || SEVERITY_CONFIG.low
|
|
const isOpen = expandedIds.has(trigger.id)
|
|
|
|
return (
|
|
<div key={trigger.id} className={`rounded-lg border ${sev.border} ${sev.bg} overflow-hidden`}>
|
|
{/* Trigger header row */}
|
|
<button
|
|
onClick={() => toggleExpanded(trigger.id)}
|
|
className="w-full flex items-center gap-3 px-4 py-3 text-left"
|
|
>
|
|
<ChevronIcon open={isOpen} />
|
|
<div className="flex-1 min-w-0">
|
|
<span className="text-sm font-medium text-gray-900 dark:text-white">
|
|
{trigger.regulation} {trigger.article} — {trigger.title}
|
|
</span>
|
|
</div>
|
|
<span className={`px-2 py-0.5 text-xs font-bold rounded ${sev.badge}`}>
|
|
{SEVERITY_LABELS[trigger.severity] || trigger.severity}
|
|
</span>
|
|
</button>
|
|
|
|
{/* Expanded detail */}
|
|
{isOpen && (
|
|
<div className="px-4 pb-4 pt-0 ml-7 space-y-2">
|
|
<p className="text-xs text-gray-700 dark:text-gray-300">
|
|
<span className="font-medium">Grund:</span> {trigger.reason}
|
|
</p>
|
|
{trigger.affected_hazard_count != null && trigger.affected_hazard_count > 0 && (
|
|
<p className="text-xs text-gray-500">
|
|
Betroffene Gefaehrdungen: {trigger.affected_hazard_count}
|
|
</p>
|
|
)}
|
|
<Link
|
|
href={trigger.module_path}
|
|
className={`inline-flex items-center gap-1.5 text-xs font-medium ${sev.text} hover:underline`}
|
|
>
|
|
{trigger.module_label} oeffnen
|
|
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
|
</svg>
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{/* Disclaimer */}
|
|
<div className="p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 text-xs text-amber-800 dark:text-amber-300">
|
|
<strong>Hinweis:</strong> Diese Compliance-Hinweise werden automatisch aus den
|
|
Gefaehrdungen und Klassifikationen abgeleitet. Der CE-Fachmann muss die
|
|
regulatorischen Anforderungen im jeweiligen Modul verifizieren.
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|