'use client' /** * Audit Workspace - Collaborative Audit Management * * Features: * - View all requirements with original text from regulations * - Document implementation details for each requirement * - Link to source documents (PDFs, EUR-Lex) * - Track audit status (pending, in_review, approved, rejected) * - Add code references and evidence * - Auditor notes and sign-off */ import { useState, useEffect } from 'react' import Link from 'next/link' import AdminLayout from '@/components/admin/AdminLayout' // Types interface Regulation { id: string code: string name: string full_name: string regulation_type: string source_url: string | null local_pdf_path: string | null requirement_count: number } interface Requirement { id: string regulation_id: string regulation_code?: string article: string paragraph: string | null title: string description: string | null requirement_text: string | null breakpilot_interpretation: string | null implementation_status: string implementation_details: string | null code_references: Array<{ file: string; line?: number; description: string }> | null evidence_description: string | null audit_status: string auditor_notes: string | null is_applicable: boolean applicability_reason: string | null priority: number source_page: number | null source_section: string | null } interface RequirementUpdate { implementation_status?: string implementation_details?: string code_references?: Array<{ file: string; line?: number; description: string }> evidence_description?: string audit_status?: string auditor_notes?: string is_applicable?: boolean applicability_reason?: string } const IMPLEMENTATION_STATUS = { not_started: { label: 'Nicht gestartet', color: 'bg-slate-400' }, in_progress: { label: 'In Arbeit', color: 'bg-yellow-500' }, implemented: { label: 'Implementiert', color: 'bg-blue-500' }, verified: { label: 'Verifiziert', color: 'bg-green-500' }, } const AUDIT_STATUS = { pending: { label: 'Ausstehend', color: 'bg-slate-400' }, in_review: { label: 'In Pruefung', color: 'bg-yellow-500' }, approved: { label: 'Genehmigt', color: 'bg-green-500' }, rejected: { label: 'Abgelehnt', color: 'bg-red-500' }, } const PRIORITY_LABELS: Record = { 1: { label: 'Kritisch', color: 'text-red-600' }, 2: { label: 'Hoch', color: 'text-orange-600' }, 3: { label: 'Mittel', color: 'text-yellow-600' }, } export default function AuditWorkspacePage() { const [regulations, setRegulations] = useState([]) const [requirements, setRequirements] = useState([]) const [selectedRegulation, setSelectedRegulation] = useState(null) const [selectedRequirement, setSelectedRequirement] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [filterAuditStatus, setFilterAuditStatus] = useState('all') const [filterImplStatus, setFilterImplStatus] = useState('all') const [searchQuery, setSearchQuery] = useState('') const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000' useEffect(() => { loadRegulations() }, []) useEffect(() => { if (selectedRegulation) { loadRequirements(selectedRegulation) } }, [selectedRegulation]) const loadRegulations = async () => { try { const res = await fetch(`${BACKEND_URL}/api/v1/compliance/regulations`) if (res.ok) { const data = await res.json() setRegulations(data.regulations || []) // Select first regulation by default if (data.regulations?.length > 0) { setSelectedRegulation(data.regulations[0].code) } } } catch (err) { console.error('Failed to load regulations:', err) } finally { setLoading(false) } } const loadRequirements = async (regCode: string) => { try { const res = await fetch(`${BACKEND_URL}/api/v1/compliance/regulations/${regCode}/requirements`) if (res.ok) { const data = await res.json() setRequirements(data.requirements || []) } } catch (err) { console.error('Failed to load requirements:', err) } } const updateRequirement = async (reqId: string, updates: RequirementUpdate) => { setSaving(true) try { const res = await fetch(`${BACKEND_URL}/api/v1/compliance/requirements/${reqId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates), }) if (res.ok) { const updated = await res.json() setRequirements(prev => prev.map(r => r.id === reqId ? { ...r, ...updates } : r)) if (selectedRequirement?.id === reqId) { setSelectedRequirement({ ...selectedRequirement, ...updates }) } } } catch (err) { console.error('Failed to update requirement:', err) } finally { setSaving(false) } } const filteredRequirements = requirements.filter(req => { if (filterAuditStatus !== 'all' && req.audit_status !== filterAuditStatus) return false if (filterImplStatus !== 'all' && req.implementation_status !== filterImplStatus) return false if (searchQuery) { const query = searchQuery.toLowerCase() return ( req.title.toLowerCase().includes(query) || req.article.toLowerCase().includes(query) || req.requirement_text?.toLowerCase().includes(query) ) } return true }) const currentRegulation = regulations.find(r => r.code === selectedRegulation) // Statistics const stats = { total: requirements.length, verified: requirements.filter(r => r.implementation_status === 'verified').length, approved: requirements.filter(r => r.audit_status === 'approved').length, pending: requirements.filter(r => r.audit_status === 'pending').length, } return ( {/* Header with back link */}
Zurueck zu Compliance
{stats.approved}/{stats.total} genehmigt | {stats.verified}/{stats.total} verifiziert
{/* Left Sidebar - Regulation & Requirement List */}
{/* Regulation Selector */}
{currentRegulation?.source_url && ( Originaldokument oeffnen )} {currentRegulation?.local_pdf_path && ( Lokale PDF )}
{/* Filters */}
setSearchQuery(e.target.value)} placeholder="Artikel, Titel..." className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg" />
{/* Requirements List */}
Anforderungen ({filteredRequirements.length})
{filteredRequirements.map(req => ( ))}
{/* Right Panel - Requirement Detail */}
{selectedRequirement ? ( updateRequirement(selectedRequirement.id, updates)} saving={saving} /> ) : (

Waehlen Sie eine Anforderung aus der Liste

)}
) } // AI Interpretation Types interface AIInterpretation { summary: string applicability: string technical_measures: string[] affected_modules: string[] risk_level: string implementation_hints: string[] confidence_score: number error?: string } // Requirement Detail Panel Component function RequirementDetailPanel({ requirement, regulation, onUpdate, saving, }: { requirement: Requirement regulation: Regulation | undefined onUpdate: (updates: RequirementUpdate) => void saving: boolean }) { const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000' const [editMode, setEditMode] = useState(false) const [aiLoading, setAiLoading] = useState(false) const [aiInterpretation, setAiInterpretation] = useState(null) const [showAiPanel, setShowAiPanel] = useState(false) const [localData, setLocalData] = useState({ implementation_status: requirement.implementation_status, implementation_details: requirement.implementation_details || '', evidence_description: requirement.evidence_description || '', audit_status: requirement.audit_status, auditor_notes: requirement.auditor_notes || '', is_applicable: requirement.is_applicable, applicability_reason: requirement.applicability_reason || '', }) const [newCodeRef, setNewCodeRef] = useState({ file: '', line: '', description: '' }) useEffect(() => { setLocalData({ implementation_status: requirement.implementation_status, implementation_details: requirement.implementation_details || '', evidence_description: requirement.evidence_description || '', audit_status: requirement.audit_status, auditor_notes: requirement.auditor_notes || '', is_applicable: requirement.is_applicable, applicability_reason: requirement.applicability_reason || '', }) setEditMode(false) setAiInterpretation(null) setShowAiPanel(false) }, [requirement.id]) const generateAiInterpretation = async () => { setAiLoading(true) setShowAiPanel(true) try { const res = await fetch(`${BACKEND_URL}/api/v1/compliance/ai/interpret`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ requirement_id: requirement.id }), }) if (res.ok) { const data = await res.json() setAiInterpretation(data) } else { const err = await res.json() setAiInterpretation({ summary: '', applicability: '', technical_measures: [], affected_modules: [], risk_level: 'unknown', implementation_hints: [], confidence_score: 0, error: err.detail || 'Fehler bei AI-Analyse' }) } } catch (err) { setAiInterpretation({ summary: '', applicability: '', technical_measures: [], affected_modules: [], risk_level: 'unknown', implementation_hints: [], confidence_score: 0, error: 'Netzwerkfehler bei AI-Analyse' }) } finally { setAiLoading(false) } } const handleSave = () => { onUpdate(localData) setEditMode(false) } const addCodeReference = () => { if (!newCodeRef.file) return const refs = requirement.code_references || [] onUpdate({ code_references: [...refs, { file: newCodeRef.file, line: newCodeRef.line ? parseInt(newCodeRef.line) : undefined, description: newCodeRef.description, }], }) setNewCodeRef({ file: '', line: '', description: '' }) } return (
{/* Header */}
{requirement.article}{requirement.paragraph ? ` ${requirement.paragraph}` : ''} {AUDIT_STATUS[requirement.audit_status as keyof typeof AUDIT_STATUS]?.label || requirement.audit_status} {IMPLEMENTATION_STATUS[requirement.implementation_status as keyof typeof IMPLEMENTATION_STATUS]?.label || requirement.implementation_status}

{requirement.title}

{editMode ? ( <> ) : ( )}
{/* Original Requirement Text */}

Originaler Anforderungstext

{requirement.requirement_text || 'Kein Originaltext hinterlegt'}

{requirement.source_page && (

Quelle: {regulation?.code} Seite {requirement.source_page} {requirement.source_section ? `, ${requirement.source_section}` : ''}

)}
{/* Applicability */}

Anwendbarkeit auf Breakpilot

{editMode ? (