'use client' import React, { useState, useEffect } from 'react' import { useSDK } from '@/lib/sdk' // ============================================================================= // TYPES // ============================================================================= interface QualityMetric { id: string name: string category: 'accuracy' | 'fairness' | 'robustness' | 'explainability' | 'performance' score: number threshold: number trend: 'up' | 'down' | 'stable' last_measured: string ai_system: string | null } interface QualityTest { id: string name: string status: 'passed' | 'failed' | 'warning' | 'pending' last_run: string duration: string | null ai_system: string | null details: string | null } interface Stats { total_metrics: number avg_score: number metrics_above_threshold: number passed: number failed: number warning: number total_tests: number } // ============================================================================= // COMPONENTS // ============================================================================= function MetricCard({ metric, onEdit }: { metric: QualityMetric; onEdit: (m: QualityMetric) => void }) { const isAboveThreshold = metric.score >= metric.threshold const categoryColors = { accuracy: 'bg-blue-100 text-blue-700', fairness: 'bg-purple-100 text-purple-700', robustness: 'bg-green-100 text-green-700', explainability: 'bg-yellow-100 text-yellow-700', performance: 'bg-orange-100 text-orange-700', } const categoryLabels = { accuracy: 'Genauigkeit', fairness: 'Fairness', robustness: 'Robustheit', explainability: 'Erklaerbarkeit', performance: 'Performance', } return (
{categoryLabels[metric.category]}

{metric.name}

{metric.ai_system &&

{metric.ai_system}

}
{metric.trend === 'up' && } {metric.trend === 'down' && } {metric.trend === 'stable' && }
{metric.score}%
Schwellenwert: {metric.threshold}%
) } function TestRow({ test, onDelete }: { test: QualityTest; onDelete: (id: string) => void }) { const statusColors = { passed: 'bg-green-100 text-green-700', failed: 'bg-red-100 text-red-700', warning: 'bg-yellow-100 text-yellow-700', pending: 'bg-gray-100 text-gray-500', } const statusLabels = { passed: 'Bestanden', failed: 'Fehlgeschlagen', warning: 'Warnung', pending: 'Ausstehend', } return (
{test.name}
{test.ai_system &&
{test.ai_system}
} {statusLabels[test.status]} {new Date(test.last_run).toLocaleString('de-DE')} {test.duration || '-'} {test.details || '-'} ) } // ============================================================================= // MODALS // ============================================================================= function MetricModal({ metric, onClose, onSave }: { metric?: QualityMetric onClose: () => void onSave: (data: any) => void }) { const [form, setForm] = useState({ name: metric?.name || '', category: metric?.category || 'accuracy', score: metric?.score ?? 0, threshold: metric?.threshold ?? 80, trend: metric?.trend || 'stable', ai_system: metric?.ai_system || '', }) return (

{metric ? 'Metrik bearbeiten' : 'Messung hinzufuegen'}

setForm(p => ({ ...p, name: e.target.value }))} className="w-full border rounded px-3 py-2 text-sm" placeholder="z.B. Accuracy Score" />
setForm(p => ({ ...p, score: parseFloat(e.target.value) || 0 }))} className="w-full border rounded px-3 py-2 text-sm" />
setForm(p => ({ ...p, threshold: parseFloat(e.target.value) || 80 }))} className="w-full border rounded px-3 py-2 text-sm" />
setForm(p => ({ ...p, ai_system: e.target.value }))} className="w-full border rounded px-3 py-2 text-sm" placeholder="z.B. Bewerber-Screening" />
) } function TestModal({ onClose, onSave }: { onClose: () => void; onSave: (data: any) => void }) { const [form, setForm] = useState({ name: '', status: 'pending', duration: '', ai_system: '', details: '' }) return (

Test hinzufuegen

setForm(p => ({ ...p, name: e.target.value }))} className="w-full border rounded px-3 py-2 text-sm" placeholder="z.B. Bias Detection Test" />
setForm(p => ({ ...p, duration: e.target.value }))} className="w-full border rounded px-3 py-2 text-sm" placeholder="z.B. 45min" />
setForm(p => ({ ...p, ai_system: e.target.value }))} className="w-full border rounded px-3 py-2 text-sm" placeholder="z.B. Bewerber-Screening" />
setForm(p => ({ ...p, details: e.target.value }))} className="w-full border rounded px-3 py-2 text-sm" placeholder="Ergebnis-Zusammenfassung" />
) } // ============================================================================= // MAIN PAGE // ============================================================================= const API_BASE = '/api/sdk/v1/compliance/quality' export default function QualityPage() { const { state } = useSDK() const [metrics, setMetrics] = useState([]) const [tests, setTests] = useState([]) const [apiStats, setApiStats] = useState(null) const [loading, setLoading] = useState(true) const [showMetricModal, setShowMetricModal] = useState(false) const [showTestModal, setShowTestModal] = useState(false) const [editMetric, setEditMetric] = useState(undefined) useEffect(() => { loadAll() }, []) async function loadAll() { setLoading(true) try { const [metricsRes, testsRes, statsRes] = await Promise.all([ fetch(`${API_BASE}/metrics?limit=100`), fetch(`${API_BASE}/tests?limit=100`), fetch(`${API_BASE}/stats`), ]) if (metricsRes.ok) { const d = await metricsRes.json() setMetrics(Array.isArray(d.metrics) ? d.metrics : []) } if (testsRes.ok) { const d = await testsRes.json() setTests(Array.isArray(d.tests) ? d.tests : []) } if (statsRes.ok) { setApiStats(await statsRes.json()) } } catch (err) { console.error('Failed to load quality data:', err) } finally { setLoading(false) } } async function handleCreateMetric(form: any) { try { const res = await fetch(`${API_BASE}/metrics`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form), }) if (res.ok) { const created = await res.json() setMetrics(prev => [...prev, created]) setShowMetricModal(false) loadAll() } } catch (err) { console.error('Failed to create metric:', err) } } async function handleUpdateMetric(form: any) { if (!editMetric) return try { const res = await fetch(`${API_BASE}/metrics/${editMetric.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form), }) if (res.ok) { const updated = await res.json() setMetrics(prev => prev.map(m => m.id === updated.id ? updated : m)) setEditMetric(undefined) setShowMetricModal(false) loadAll() } } catch (err) { console.error('Failed to update metric:', err) } } async function handleCreateTest(form: any) { try { const res = await fetch(`${API_BASE}/tests`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(form), }) if (res.ok) { const created = await res.json() setTests(prev => [created, ...prev]) setShowTestModal(false) loadAll() } } catch (err) { console.error('Failed to create test:', err) } } async function handleDeleteTest(id: string) { try { const res = await fetch(`${API_BASE}/tests/${id}`, { method: 'DELETE' }) if (res.ok || res.status === 204) { setTests(prev => prev.filter(t => t.id !== id)) loadAll() } } catch (err) { console.error('Failed to delete test:', err) } } // Derived stats — prefer API stats, fallback to computed const avgScore = apiStats ? apiStats.avg_score : (metrics.length > 0 ? Math.round(metrics.reduce((s, m) => s + m.score, 0) / metrics.length) : 0) const metricsAboveThreshold = apiStats ? apiStats.metrics_above_threshold : metrics.filter(m => m.score >= m.threshold).length const passedTests = apiStats ? apiStats.passed : tests.filter(t => t.status === 'passed').length const failedTests = apiStats ? apiStats.failed : tests.filter(t => t.status === 'failed').length const failingMetrics = metrics.filter(m => m.score < m.threshold) return (
{/* Header */}

AI Quality Dashboard

Ueberwachen Sie die Qualitaet und Fairness Ihrer KI-Systeme

{/* Stats */}
Durchschnittlicher Score
{avgScore}%
Metriken ueber Schwellenwert
{metricsAboveThreshold}/{metrics.length}
Tests bestanden
{passedTests}
Tests fehlgeschlagen
{failedTests}
{/* Alert for failed metrics */} {failingMetrics.length > 0 && (

{failingMetrics.length} Metrik(en) unter Schwellenwert

Ueberpruefen Sie die betroffenen KI-Systeme und ergreifen Sie Korrekturmassnahmen.

)} {/* Metrics Grid */}

Qualitaetsmetriken

{loading ? (
Lade Metriken...
) : metrics.length === 0 ? (
Noch keine Metriken erfasst. Klicken Sie auf "Messung hinzufuegen".
) : (
{metrics.map(metric => ( { setEditMetric(m); setShowMetricModal(true) }} /> ))}
)}
{/* Tests Table */}

Testergebnisse

{tests.length === 0 ? ( ) : tests.map(test => ( ))}
Test Status Letzter Lauf Dauer Details Aktion
Noch keine Tests erfasst.
{/* Modals */} {showMetricModal && ( { setShowMetricModal(false); setEditMetric(undefined) }} onSave={editMetric ? handleUpdateMetric : handleCreateMetric} /> )} {showTestModal && ( setShowTestModal(false)} onSave={handleCreateTest} /> )}
) }