'use client' import React, { useState, useEffect } from 'react' import Link from 'next/link' import { useParams } from 'next/navigation' import { SuggestedNorms } from './_components/SuggestedNorms' import { ComplianceAlerts } from './_components/ComplianceAlerts' interface ProjectOverview { id: string machine_name: string machine_type: string manufacturer: string status: string completeness_pct: number created_at: string updated_at: string metadata?: { limits_form?: Record } risk_summary?: { critical?: number high?: number medium?: number low?: number total?: number } component_count: number hazard_count: number mitigation_count: number } const QUICK_ACTIONS = [ { href: '/components', label: 'Komponenten verwalten', icon: 'cube', description: 'SW/FW/AI/HMI Baum bearbeiten' }, { href: '/classification', label: 'Klassifikation pruefen', icon: 'tag', description: 'AI Act, MVO, CRA, NIS2' }, { href: '/hazards', label: 'Hazard Log oeffnen', icon: 'warning', description: 'Gefaehrdungen und Risiken' }, { href: '/mitigations', label: 'Massnahmen planen', icon: 'shield', description: 'Design, Schutz, Information' }, { href: '/verification', label: 'Verifikationsplan', icon: 'check', description: 'Nachweise zuordnen' }, { href: '/evidence', label: 'Nachweise hochladen', icon: 'document', description: 'Dokumente und Berichte' }, { href: '/tech-file', label: 'CE-Akte generieren', icon: 'folder', description: 'Technische Dokumentation' }, { href: '/monitoring', label: 'Monitoring', icon: 'activity', description: 'Post-Market Ueberwachung' }, ] function RiskGauge({ label, value, max, color }: { label: string; value: number; max: number; color: string }) { const pct = max > 0 ? Math.round((value / max) * 100) : 0 return (
{value}
{label}
) } const STATUS_WORKFLOW = [ { key: 'draft', label: 'Entwurf' }, { key: 'in_progress', label: 'In Bearbeitung' }, { key: 'review', label: 'In Pruefung' }, { key: 'approved', label: 'Freigegeben' }, ] export default function ProjectOverviewPage() { const params = useParams() const projectId = params.projectId as string const [project, setProject] = useState(null) const [loading, setLoading] = useState(true) useEffect(() => { fetchProject() }, [projectId]) async function fetchProject() { try { // Only fetch project detail + lightweight risk summary (NO heavy lists) const [projRes, riskRes] = await Promise.all([ fetch(`/api/sdk/v1/iace/projects/${projectId}`), fetch(`/api/sdk/v1/iace/projects/${projectId}/risk-summary`), ]) if (!projRes.ok) return const json = await projRes.json() // Live risk summary from dedicated endpoint (lightweight — just counts) let rs = json.risk_summary || {} let hazCount = 0 let mitCount = 0 if (riskRes.ok) { const riskJson = await riskRes.json() const live = riskJson.risk_summary || riskJson || {} rs = { critical: live.critical || 0, high: live.high || 0, medium: live.medium || 0, low: live.low || 0, negligible: live.negligible || 0, total: live.total_hazards || live.total || 0, } hazCount = live.total_hazards || live.total || 0 mitCount = live.total_mitigations || 0 } // Calculate dynamic completeness percentage from CE process steps const compCount = json.components?.length || 0 const limitsForm = json.metadata?.limits_form || {} const hasLimits = Object.keys(limitsForm).length > 0 const hasComponents = compCount > 0 const hasHazards = hazCount > 0 const hasMitigations = mitCount > 0 const stepsComplete = [hasLimits, hasComponents, hasHazards, hasMitigations].filter(Boolean).length const completeness = Math.round((stepsComplete / 6) * 100) setProject({ ...json, completeness_pct: completeness, component_count: compCount, hazard_count: hazCount, mitigation_count: mitCount, metadata: json.metadata, risk_summary: { critical: rs.critical || 0, high: rs.high || 0, medium: rs.medium || 0, low: rs.low || 0, total: rs.total || hazCount, }, }) } catch (err) { console.error('Failed to fetch project:', err) } finally { setLoading(false) } } if (loading) { return (
) } if (!project) { return (

Projekt nicht gefunden

Zurueck zur Uebersicht
) } const currentStatusIndex = STATUS_WORKFLOW.findIndex((s) => s.key === project.status) return (
{/* Header */}

{project.machine_name}

{project.machine_type} {project.manufacturer ? `-- ${project.manufacturer}` : ''}

{/* Status Workflow */}

Projektstatus

{STATUS_WORKFLOW.map((step, index) => (
{index < currentStatusIndex ? ( ) : index === currentStatusIndex ? (
) : (
)} {step.label}
{index < STATUS_WORKFLOW.length - 1 && (
)} ))}
{/* Machine Info */}

Maschineninformationen

Maschinenname
{project.machine_name}
Typ
{project.machine_type || '--'}
Hersteller
{project.manufacturer || '--'}
Vollstaendigkeit
{project.completeness_pct}%
{/* Risk Summary — live from /risk-summary endpoint */}

Risikozusammenfassung

{/* Risk level bars */}
{[ { label: 'Kritisch', value: project.risk_summary?.critical || 0, color: 'bg-red-500', text: 'text-red-700' }, { label: 'Hoch', value: project.risk_summary?.high || 0, color: 'bg-orange-500', text: 'text-orange-700' }, { label: 'Mittel', value: project.risk_summary?.medium || 0, color: 'bg-yellow-500', text: 'text-yellow-700' }, { label: 'Niedrig', value: project.risk_summary?.low || 0, color: 'bg-green-500', text: 'text-green-700' }, ].map((level) => { const total = project.risk_summary?.total || 1 const pct = Math.round((level.value / total) * 100) return (
{level.label}
{level.value}
) })}
{/* Counts */}
{project.component_count}
Komponenten
{project.hazard_count}
Gefaehrdungen
{project.mitigation_count}
Massnahmen
{/* RPZ threshold info */}
RPZ-Schwellen: Kritisch >100 | Hoch 60-100 | Mittel 20-60 | Niedrig <20
{/* Progress Tracker */}

Projektfortschritt

{project.completeness_pct}%
{(() => { const hasLimits = Object.keys(project.metadata?.limits_form || {}).length > 0 const steps = [ { done: hasLimits, label: 'Grenzen definiert', detail: hasLimits ? 'Felder ausgefuellt' : 'ausstehend' }, { done: project.component_count > 0, label: 'Komponenten erfasst', detail: `${project.component_count} Komponenten` }, { done: project.hazard_count > 0, label: 'Gefaehrdungen identifiziert', detail: `${project.hazard_count} bewertet` }, { done: project.mitigation_count > 0, label: 'Massnahmen zugeordnet', detail: `${project.mitigation_count} Massnahmen` }, { done: false, label: 'Verifikation', detail: 'ausstehend' }, { done: false, label: 'CE-Akte', detail: 'ausstehend' }, ] const firstPending = steps.find((s) => !s.done) return ( <> {steps.map((step) => (
{step.done ? '\u2713' : '\u25CB'} {step.label} {step.detail}
))}
Naechster Schritt: {firstPending?.label || 'Alle Schritte abgeschlossen'}
) })()}
{/* Compliance Alerts */} {/* Suggested Norms */} {/* Quick Actions */}

Schnellzugriff

{QUICK_ACTIONS.map((action) => (
{action.label}
{action.description}
))}
) }