feat: 6 Dokumentations-Module auf 100% — VVT Backend, Filter, PDF-Export

Phase 1 — VVT Backend (localStorage → API):
- migrations/006_vvt.sql: Neue Tabellen (vvt_organization, vvt_activities, vvt_audit_log)
- compliance/db/vvt_models.py: SQLAlchemy-Models für alle VVT-Tabellen
- compliance/api/vvt_routes.py: Vollständiger CRUD-Router (10 Endpoints)
- compliance/api/__init__.py: VVT-Router registriert
- compliance/api/schemas.py: VVT Pydantic-Schemas ergänzt
- app/(sdk)/sdk/vvt/page.tsx: API-Client + camelCase↔snake_case Mapping,
  localStorage durch persistente DB-Calls ersetzt (POST/PUT/DELETE/GET)
- tests/test_vvt_routes.py: 18 Tests (alle grün)

Phase 3 — Document Generator PDF-Export:
- document-generator/page.tsx: "Als PDF exportieren"-Button funktioniert jetzt
  via window.print() + Print-Window mit korrektem HTML
- Fallback-Banner wenn Template-Service (breakpilot-core) nicht erreichbar

Phase 4 — Source Policy erweiterte Filter:
- SourcesTab.tsx: source_type-Filter (Rechtlich / Leitlinien / Vorlagen / etc.)
- PIIRulesTab.tsx: category-Filter (E-Mail / Telefon / IBAN / etc.)
- source_policy_router.py: Backend-Endpoints unterstützen jetzt source_type
  und category als Query-Parameter
- requirements.txt: reportlab==4.2.5 ergänzt (fehlende Audit-PDF-Dependency)

Phase 2 — Training (Migration-Skripte):
- scripts/apply_training_migrations.sh: SSH-Skript für Mac Mini
- scripts/apply_vvt_migration.sh: Vollständiges Deploy-Skript für VVT

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-02 17:14:58 +01:00
parent 7cc420bd9e
commit 34fc8dc654
14 changed files with 1332 additions and 65 deletions

View File

@@ -51,6 +51,7 @@ export function SourcesTab({ apiBase, onUpdate }: SourcesTabProps) {
const [searchTerm, setSearchTerm] = useState('')
const [licenseFilter, setLicenseFilter] = useState('')
const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'inactive'>('all')
const [sourceTypeFilter, setSourceTypeFilter] = useState('')
// Edit modal
const [editingSource, setEditingSource] = useState<AllowedSource | null>(null)
@@ -69,7 +70,7 @@ export function SourcesTab({ apiBase, onUpdate }: SourcesTabProps) {
useEffect(() => {
fetchSources()
}, [licenseFilter, statusFilter])
}, [licenseFilter, statusFilter, sourceTypeFilter])
const fetchSources = async () => {
try {
@@ -77,6 +78,7 @@ export function SourcesTab({ apiBase, onUpdate }: SourcesTabProps) {
const params = new URLSearchParams()
if (licenseFilter) params.append('license', licenseFilter)
if (statusFilter !== 'all') params.append('active_only', statusFilter === 'active' ? 'true' : 'false')
if (sourceTypeFilter) params.append('source_type', sourceTypeFilter)
const res = await fetch(`${apiBase}/sources?${params}`)
if (!res.ok) throw new Error('Fehler beim Laden')
@@ -230,6 +232,18 @@ export function SourcesTab({ apiBase, onUpdate }: SourcesTabProps) {
<option value="active">Aktiv</option>
<option value="inactive">Inaktiv</option>
</select>
<select
value={sourceTypeFilter}
onChange={(e) => setSourceTypeFilter(e.target.value)}
className="px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
<option value="">Alle Typen</option>
<option value="legal">Rechtlich</option>
<option value="guidance">Leitlinien</option>
<option value="template">Vorlagen</option>
<option value="technical">Technisch</option>
<option value="other">Sonstige</option>
</select>
<button
onClick={() => setIsNewSource(true)}
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 flex items-center gap-2"