import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { fetchAIRegistry, fetchDPIATemplate, fetchIncidents, createIncident, updateIncident, fetchRetentionPolicies, updateRetentionPolicy, type AIRegistryEntry, type ComplianceIncident, type RetentionPolicyEntry, type IncidentCreate, } from '../api/compliance'; type SubTab = 'registry' | 'incidents' | 'retention'; export function ComplianceTab() { const { t } = useTranslation(); const [subTab, setSubTab] = useState('registry'); const subTabs: { key: SubTab; label: string }[] = [ { key: 'registry', label: t('compliance.aiRegistry', 'AI-Register') }, { key: 'incidents', label: t('compliance.incidents', 'Vorfälle') }, { key: 'retention', label: t('compliance.retention', 'Aufbewahrung') }, ]; return (
{subTab === 'registry' && } {subTab === 'incidents' && } {subTab === 'retention' && }
); } // ─── AI Registry Panel ─── function AIRegistryPanel() { const { t } = useTranslation(); const { data, isLoading, error } = useQuery({ queryKey: ['compliance', 'ai-registry'], queryFn: fetchAIRegistry, }); const handleDPIAExport = async (agentId: string, agentName: string) => { try { const template = await fetchDPIATemplate(agentId); const blob = new Blob([JSON.stringify(template, null, 2)], { type: 'application/json', }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `dpia_${agentName.replace(/\s+/g, '_').toLowerCase()}.json`; a.click(); URL.revokeObjectURL(url); } catch { // Error handled by query client } }; if (isLoading) { return

{t('common.loading', 'Laden...')}

; } if (error) { return

{t('common.error', 'Fehler beim Laden')}

; } if (!data || data.items.length === 0) { return

{t('compliance.noAgents', 'Keine AI-Agenten gefunden')}

; } return (
{data.items.map((entry: AIRegistryEntry) => { const meta = entry.ai_use_case_metadata as Record; return ( ); })}
{t('compliance.name', 'Name')} {t('compliance.intendedPurpose', 'Zweck')} {t('compliance.owner', 'Verantwortlich')} {t('compliance.riskClass', 'Risiko')} {t('compliance.oversight', 'Oversight')} {t('compliance.dataCategories', 'Daten')} {t('compliance.status', 'Status')} {t('compliance.actions', 'Aktionen')}
{entry.name} {String(meta.intended_purpose || '')} {String(meta.owner || '')} {String(meta.risk_class || 'low')} {String(meta.oversight_policy || '')} {Array.isArray(meta.data_categories) ? (meta.data_categories as string[]).join(', ') : ''} {entry.is_active ? t('compliance.active', 'Aktiv') : t('compliance.inactive', 'Inaktiv')}
{data.items.some((e) => e.validation_warnings.length > 0) && (
{t('compliance.warningsNote', 'Einige Agenten haben Validierungswarnungen. Siehe Details im AI-Register.')}
)}
); } // ─── Incidents Panel ─── function IncidentsPanel() { const { t } = useTranslation(); const queryClient = useQueryClient(); const [showForm, setShowForm] = useState(false); const [formData, setFormData] = useState({ incident_type: 'ai', title: '', description: '', provider: '', measures_taken: '', status: 'open', }); const { data, isLoading, error } = useQuery({ queryKey: ['compliance', 'incidents'], queryFn: () => fetchIncidents(), }); const createMutation = useMutation({ mutationFn: (data: IncidentCreate) => createIncident(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['compliance', 'incidents'] }); setShowForm(false); setFormData({ incident_type: 'ai', title: '', description: '', provider: '', measures_taken: '', status: 'open' }); }, }); const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: string; data: { status?: string } }) => updateIncident(id, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['compliance', 'incidents'] }); }, }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); createMutation.mutate(formData); }; if (isLoading) { return

{t('common.loading', 'Laden...')}

; } if (error) { return

{t('common.error', 'Fehler beim Laden')}

; } return (
{showForm && (
setFormData({ ...formData, title: e.target.value })} className="w-full px-3 py-2 border border-secondary-300 rounded text-sm" aria-label={t('compliance.title', 'Titel')} />