'use client' /** * Quality & Audit Page * * Ermoeglicht Auditoren: * - Chunk-Suche und Stichproben * - Traceability: Chunk → Requirement → Control * - Dokumenten-Vollstaendigkeitspruefung */ import { useState, useCallback } from 'react' import Link from 'next/link' import { PagePurpose } from '@/components/common/PagePurpose' const API_PROXY = '/api/legal-corpus' // Types interface ChunkDetail { id: string text: string regulation_code: string regulation_name: string article: string | null paragraph: string | null chunk_index: number chunk_position: 'beginning' | 'middle' | 'end' source_url: string score?: number } interface Requirement { id: string text: string category: string source_chunk_id: string regulation_code: string } interface Control { id: string name: string description: string source_requirement_ids: string[] regulation_codes: string[] } interface TraceabilityResult { chunk: ChunkDetail requirements: Requirement[] controls: Control[] } // Regulations for filtering const REGULATIONS = [ { code: 'GDPR', name: 'DSGVO' }, { code: 'EPRIVACY', name: 'ePrivacy' }, { code: 'TDDDG', name: 'TDDDG' }, { code: 'SCC', name: 'Standardvertragsklauseln' }, { code: 'DPF', name: 'EU-US DPF' }, { code: 'AIACT', name: 'EU AI Act' }, { code: 'CRA', name: 'Cyber Resilience Act' }, { code: 'NIS2', name: 'NIS2' }, { code: 'EUCSA', name: 'EU Cybersecurity Act' }, { code: 'DATAACT', name: 'Data Act' }, { code: 'DGA', name: 'Data Governance Act' }, { code: 'DSA', name: 'Digital Services Act' }, { code: 'EAA', name: 'Accessibility Act' }, { code: 'DSM', name: 'DSM-Urheberrecht' }, { code: 'PLD', name: 'Produkthaftung' }, { code: 'GPSR', name: 'Product Safety' }, { code: 'BSI-TR-03161-1', name: 'BSI-TR Teil 1' }, { code: 'BSI-TR-03161-2', name: 'BSI-TR Teil 2' }, { code: 'BSI-TR-03161-3', name: 'BSI-TR Teil 3' }, ] const TYPE_COLORS: Record = { eu_regulation: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400', eu_directive: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400', de_law: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400', bsi_standard: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', } export default function QualityPage() { // Search state const [searchQuery, setSearchQuery] = useState('') const [searchResults, setSearchResults] = useState([]) const [searching, setSearching] = useState(false) const [selectedRegulation, setSelectedRegulation] = useState('') const [topK, setTopK] = useState(10) // Traceability state const [selectedChunk, setSelectedChunk] = useState(null) const [traceability, setTraceability] = useState(null) const [loadingTrace, setLoadingTrace] = useState(false) // Quick sample queries for auditors const sampleQueries = [ { label: 'Art. 17 DSGVO (Recht auf Loeschung)', query: 'Recht auf Löschung Artikel 17', reg: 'GDPR' }, { label: 'Einwilligung TDDDG', query: 'Einwilligung Endeinrichtung speichern', reg: 'TDDDG' }, { label: 'AI Act Hochrisiko', query: 'Hochrisiko-KI-System Anforderungen', reg: 'AIACT' }, { label: 'NIS2 Sicherheitsmaßnahmen', query: 'Cybersicherheitsrisikomanagement Maßnahmen', reg: 'NIS2' }, { label: 'BSI Authentifizierung', query: 'Authentifizierung Zwei-Faktor mobile', reg: 'BSI-TR-03161-1' }, ] const handleSearch = useCallback(async () => { if (!searchQuery.trim()) return setSearching(true) setSearchResults([]) setSelectedChunk(null) setTraceability(null) try { let url = `${API_PROXY}?action=search&query=${encodeURIComponent(searchQuery)}&top_k=${topK}` if (selectedRegulation) { url += `®ulations=${encodeURIComponent(selectedRegulation)}` } const res = await fetch(url) if (res.ok) { const data = await res.json() setSearchResults(data.results || []) } } catch (error) { console.error('Search failed:', error) } finally { setSearching(false) } }, [searchQuery, selectedRegulation, topK]) const loadTraceability = useCallback(async (chunk: ChunkDetail) => { setSelectedChunk(chunk) setLoadingTrace(true) try { // Try to load traceability (requirements and controls derived from this chunk) const res = await fetch(`${API_PROXY}?action=traceability&chunk_id=${encodeURIComponent(chunk.id || chunk.regulation_code + '_' + chunk.chunk_index)}®ulation=${encodeURIComponent(chunk.regulation_code)}`) if (res.ok) { const data = await res.json() setTraceability({ chunk, requirements: data.requirements || [], controls: data.controls || [], }) } else { // If traceability endpoint doesn't exist yet, show placeholder setTraceability({ chunk, requirements: [], controls: [], }) } } catch (error) { console.error('Failed to load traceability:', error) setTraceability({ chunk, requirements: [], controls: [], }) } finally { setLoadingTrace(false) } }, []) const handleSampleQuery = (query: string, reg: string) => { setSearchQuery(query) setSelectedRegulation(reg) // Auto-search after setting setTimeout(() => { handleSearch() }, 100) } const highlightText = (text: string, query: string) => { if (!query) return text const words = query.toLowerCase().split(' ').filter(w => w.length > 2) let result = text words.forEach(word => { const regex = new RegExp(`(${word})`, 'gi') result = result.replace(regex, '$1') }) return result } return (
{/* Header */}

Qualitaet & Audit

Stichproben und Traceability fuer Compliance-Auditoren

← Zurueck zu RAG
{/* Quick Sample Queries */}

Schnell-Stichproben

{sampleQueries.map((sq, idx) => ( ))}
{/* Search Section */}

Chunk-Suche

{/* Search Input */}
setSearchQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} placeholder="z.B. 'Recht auf Löschung' oder 'Art. 17 Abs. 1'" className="w-full px-4 py-2 border border-gray-300 dark:border-slate-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-slate-700 dark:text-white" />
{/* Results Grid */} {searchResults.length > 0 && (
{/* Search Results List */}

Gefundene Chunks ({searchResults.length})

{searchResults.map((result, idx) => (
loadTraceability(result)} className={`p-4 border rounded-lg cursor-pointer transition-all ${ selectedChunk?.text === result.text ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20' : 'border-gray-200 dark:border-slate-700 hover:border-gray-300 dark:hover:border-slate-600' }`} > {/* Header */}
{result.regulation_code} {result.article && ( Art. {result.article} {result.paragraph && ` Abs. ${result.paragraph}`} )}
Score: {(result.score || 0).toFixed(3)}
{/* Text Preview */}

400 ? '...' : ''), searchQuery) }} /> {/* Metadata */}

Chunk #{result.chunk_index || idx} {result.text.length} Zeichen
))}
{/* Traceability Panel */}

Traceability

{!selectedChunk ? (

Waehlen Sie einen Chunk aus der Liste, um die Traceability zu sehen.

) : loadingTrace ? (

Lade Traceability...

) : traceability ? (
{/* Selected Chunk Detail */}

📄 Ausgewaehlter Chunk

{traceability.chunk.regulation_code} {traceability.chunk.article && ( Art. {traceability.chunk.article} {traceability.chunk.paragraph && ` Abs. ${traceability.chunk.paragraph}`} )}

{traceability.chunk.text}

{traceability.chunk.source_url && ( 🔗 Quelle oeffnen )}
{/* Arrow Down */}
{/* Requirements */}

📋 Extrahierte Anforderungen ({traceability.requirements.length})

{traceability.requirements.length > 0 ? (
{traceability.requirements.map((req, idx) => (
{req.category || 'Anforderung'}

{req.text}

))}
) : (

Keine Anforderungen aus diesem Chunk extrahiert.
(Requirements-Extraktion ist noch nicht implementiert)

)}
{/* Arrow Down */}
{/* Controls */}

✅ Abgeleitete Controls ({traceability.controls.length})

{traceability.controls.length > 0 ? (
{traceability.controls.map((ctrl, idx) => (
{ctrl.name}

{ctrl.description}

))}
) : (

Keine Controls aus diesem Chunk abgeleitet.
(Control-Ableitung ist noch nicht implementiert)

)}
) : null}
)} {/* Empty State */} {!searching && searchResults.length === 0 && searchQuery && (

Keine Ergebnisse gefunden

Versuchen Sie einen anderen Suchbegriff oder waehlen Sie eine andere Regulierung.

)} {/* Initial State */} {!searching && searchResults.length === 0 && !searchQuery && (

Bereit fuer Stichproben

Geben Sie einen Suchbegriff ein, um Chunks zu finden. Sie koennen nach Artikeln, Paragraphen oder spezifischen Textpassagen suchen.

)} {/* Audit Info */}

ℹ️ Hinweise fuer Auditoren

  • Die Suche ist semantisch - aehnliche Begriffe werden gefunden, auch wenn die exakte Formulierung abweicht
  • Jeder Chunk entspricht einem logischen Textabschnitt aus dem Originaldokument
  • Die Traceability zeigt, wie aus dem Originaltext Anforderungen und Controls abgeleitet wurden
  • Klicken Sie auf "Quelle oeffnen", um das Originaldokument zu pruefen
) }