This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/admin-v2/app/(sdk)/sdk/ai-act/page.tsx
BreakPilot Dev 660295e218 fix(admin-v2): Restore complete admin-v2 application
The admin-v2 application was incomplete in the repository. This commit
restores all missing components:

- Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education,
  infrastructure, communication, development, onboarding, rbac
- SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen,
  vendor-compliance, tom-generator, dsr, and more
- Developer portal (25 pages): API docs, SDK guides, frameworks
- All components, lib files, hooks, and types
- Updated package.json with all dependencies

The issue was caused by incomplete initial repository state - the full
admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2
but was never fully synced to the main admin-v2 directory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 23:40:15 -08:00

296 lines
12 KiB
TypeScript

'use client'
import React, { useState } from 'react'
import { useSDK } from '@/lib/sdk'
import { StepHeader, STEP_EXPLANATIONS } from '@/components/sdk/StepHeader'
// =============================================================================
// TYPES
// =============================================================================
interface AISystem {
id: string
name: string
description: string
classification: 'prohibited' | 'high-risk' | 'limited-risk' | 'minimal-risk' | 'unclassified'
purpose: string
sector: string
status: 'draft' | 'classified' | 'compliant' | 'non-compliant'
obligations: string[]
assessmentDate: Date | null
}
// =============================================================================
// MOCK DATA
// =============================================================================
const mockAISystems: AISystem[] = [
{
id: 'ai-1',
name: 'Kundenservice Chatbot',
description: 'KI-gestuetzter Chatbot fuer Kundenanfragen',
classification: 'limited-risk',
purpose: 'Automatisierte Beantwortung von Kundenanfragen',
sector: 'Kundenservice',
status: 'classified',
obligations: ['Transparenzpflicht', 'Kennzeichnung als KI-System'],
assessmentDate: new Date('2024-01-15'),
},
{
id: 'ai-2',
name: 'Bewerber-Screening',
description: 'KI-System zur Vorauswahl von Bewerbungen',
classification: 'high-risk',
purpose: 'Automatisierte Bewertung von Bewerbungsunterlagen',
sector: 'Personal',
status: 'non-compliant',
obligations: ['Risikomanagementsystem', 'Datenlenkung', 'Technische Dokumentation', 'Menschliche Aufsicht', 'Transparenz'],
assessmentDate: new Date('2024-01-10'),
},
{
id: 'ai-3',
name: 'Empfehlungsalgorithmus',
description: 'Personalisierte Produktempfehlungen',
classification: 'minimal-risk',
purpose: 'Verbesserung der Kundenerfahrung durch personalisierte Empfehlungen',
sector: 'E-Commerce',
status: 'compliant',
obligations: [],
assessmentDate: new Date('2024-01-05'),
},
{
id: 'ai-4',
name: 'Neue KI-Anwendung',
description: 'Noch nicht klassifiziertes System',
classification: 'unclassified',
purpose: 'In Evaluierung',
sector: 'Unbestimmt',
status: 'draft',
obligations: [],
assessmentDate: null,
},
]
// =============================================================================
// COMPONENTS
// =============================================================================
function RiskPyramid({ systems }: { systems: AISystem[] }) {
const counts = {
prohibited: systems.filter(s => s.classification === 'prohibited').length,
'high-risk': systems.filter(s => s.classification === 'high-risk').length,
'limited-risk': systems.filter(s => s.classification === 'limited-risk').length,
'minimal-risk': systems.filter(s => s.classification === 'minimal-risk').length,
}
return (
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">AI Act Risikopyramide</h3>
<div className="flex flex-col items-center space-y-1">
<div className="w-24 h-12 bg-red-500 text-white flex items-center justify-center rounded-t-lg text-sm font-medium">
Verboten ({counts.prohibited})
</div>
<div className="w-40 h-12 bg-orange-500 text-white flex items-center justify-center text-sm font-medium">
Hochrisiko ({counts['high-risk']})
</div>
<div className="w-56 h-12 bg-yellow-500 text-white flex items-center justify-center text-sm font-medium">
Begrenztes Risiko ({counts['limited-risk']})
</div>
<div className="w-72 h-12 bg-green-500 text-white flex items-center justify-center rounded-b-lg text-sm font-medium">
Minimales Risiko ({counts['minimal-risk']})
</div>
</div>
<div className="mt-4 text-center text-sm text-gray-500">
{systems.filter(s => s.classification === 'unclassified').length} System(e) noch nicht klassifiziert
</div>
</div>
)
}
function AISystemCard({ system }: { system: AISystem }) {
const classificationColors = {
prohibited: 'bg-red-100 text-red-700 border-red-200',
'high-risk': 'bg-orange-100 text-orange-700 border-orange-200',
'limited-risk': 'bg-yellow-100 text-yellow-700 border-yellow-200',
'minimal-risk': 'bg-green-100 text-green-700 border-green-200',
unclassified: 'bg-gray-100 text-gray-500 border-gray-200',
}
const classificationLabels = {
prohibited: 'Verboten',
'high-risk': 'Hochrisiko',
'limited-risk': 'Begrenztes Risiko',
'minimal-risk': 'Minimales Risiko',
unclassified: 'Nicht klassifiziert',
}
const statusColors = {
draft: 'bg-gray-100 text-gray-500',
classified: 'bg-blue-100 text-blue-700',
compliant: 'bg-green-100 text-green-700',
'non-compliant': 'bg-red-100 text-red-700',
}
const statusLabels = {
draft: 'Entwurf',
classified: 'Klassifiziert',
compliant: 'Konform',
'non-compliant': 'Nicht konform',
}
return (
<div className={`bg-white rounded-xl border-2 p-6 ${
system.classification === 'high-risk' ? 'border-orange-200' :
system.classification === 'prohibited' ? 'border-red-200' : 'border-gray-200'
}`}>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<span className={`px-2 py-1 text-xs rounded-full ${classificationColors[system.classification]}`}>
{classificationLabels[system.classification]}
</span>
<span className={`px-2 py-1 text-xs rounded-full ${statusColors[system.status]}`}>
{statusLabels[system.status]}
</span>
</div>
<h3 className="text-lg font-semibold text-gray-900">{system.name}</h3>
<p className="text-sm text-gray-500 mt-1">{system.description}</p>
<div className="mt-2 text-sm text-gray-500">
<span>Sektor: {system.sector}</span>
{system.assessmentDate && (
<span className="ml-4">Klassifiziert: {system.assessmentDate.toLocaleDateString('de-DE')}</span>
)}
</div>
</div>
</div>
{system.obligations.length > 0 && (
<div className="mt-4 pt-4 border-t border-gray-100">
<p className="text-sm font-medium text-gray-700 mb-2">Pflichten nach AI Act:</p>
<div className="flex flex-wrap gap-2">
{system.obligations.map(obl => (
<span key={obl} className="px-2 py-1 text-xs bg-purple-50 text-purple-700 rounded">
{obl}
</span>
))}
</div>
</div>
)}
<div className="mt-4 flex items-center gap-2">
<button className="flex-1 px-4 py-2 text-sm bg-purple-50 text-purple-700 rounded-lg hover:bg-purple-100 transition-colors">
{system.classification === 'unclassified' ? 'Klassifizierung starten' : 'Details anzeigen'}
</button>
<button className="px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">
Bearbeiten
</button>
</div>
</div>
)
}
// =============================================================================
// MAIN PAGE
// =============================================================================
export default function AIActPage() {
const { state } = useSDK()
const [systems] = useState<AISystem[]>(mockAISystems)
const [filter, setFilter] = useState<string>('all')
const filteredSystems = filter === 'all'
? systems
: systems.filter(s => s.classification === filter || s.status === filter)
const highRiskCount = systems.filter(s => s.classification === 'high-risk').length
const compliantCount = systems.filter(s => s.status === 'compliant').length
const unclassifiedCount = systems.filter(s => s.classification === 'unclassified').length
const stepInfo = STEP_EXPLANATIONS['ai-act']
return (
<div className="space-y-6">
{/* Step Header */}
<StepHeader
stepId="ai-act"
title={stepInfo.title}
description={stepInfo.description}
explanation={stepInfo.explanation}
tips={stepInfo.tips}
>
<button className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
KI-System registrieren
</button>
</StepHeader>
{/* Stats */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="bg-white rounded-xl border border-gray-200 p-6">
<div className="text-sm text-gray-500">KI-Systeme gesamt</div>
<div className="text-3xl font-bold text-gray-900">{systems.length}</div>
</div>
<div className="bg-white rounded-xl border border-orange-200 p-6">
<div className="text-sm text-orange-600">Hochrisiko</div>
<div className="text-3xl font-bold text-orange-600">{highRiskCount}</div>
</div>
<div className="bg-white rounded-xl border border-green-200 p-6">
<div className="text-sm text-green-600">Konform</div>
<div className="text-3xl font-bold text-green-600">{compliantCount}</div>
</div>
<div className="bg-white rounded-xl border border-gray-200 p-6">
<div className="text-sm text-gray-500">Nicht klassifiziert</div>
<div className="text-3xl font-bold text-gray-500">{unclassifiedCount}</div>
</div>
</div>
{/* Risk Pyramid */}
<RiskPyramid systems={systems} />
{/* Filter */}
<div className="flex items-center gap-2 flex-wrap">
<span className="text-sm text-gray-500">Filter:</span>
{['all', 'high-risk', 'limited-risk', 'minimal-risk', 'unclassified', 'compliant', 'non-compliant'].map(f => (
<button
key={f}
onClick={() => setFilter(f)}
className={`px-3 py-1 text-sm rounded-full transition-colors ${
filter === f
? 'bg-purple-600 text-white'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
}`}
>
{f === 'all' ? 'Alle' :
f === 'high-risk' ? 'Hochrisiko' :
f === 'limited-risk' ? 'Begrenztes Risiko' :
f === 'minimal-risk' ? 'Minimales Risiko' :
f === 'unclassified' ? 'Nicht klassifiziert' :
f === 'compliant' ? 'Konform' : 'Nicht konform'}
</button>
))}
</div>
{/* AI Systems List */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{filteredSystems.map(system => (
<AISystemCard key={system.id} system={system} />
))}
</div>
{filteredSystems.length === 0 && (
<div className="bg-white rounded-xl border border-gray-200 p-12 text-center">
<div className="w-16 h-16 mx-auto bg-purple-100 rounded-full flex items-center justify-center mb-4">
<svg className="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<h3 className="text-lg font-semibold text-gray-900">Keine KI-Systeme gefunden</h3>
<p className="mt-2 text-gray-500">Passen Sie den Filter an oder registrieren Sie ein neues KI-System.</p>
</div>
)}
</div>
)
}