refactor: Source Policy — strip PII/Audit/Blocked, move to Zusatzmodule
- Removed: PII-Regeln tab (→ Core Service, other repo) - Removed: Audit tab (→ redundant with Document Workflow + RBAC) - Removed: Blockierte Inhalte tab (→ belongs to PII) - Kept: Quellen-Whitelist + Berechtigungen (Operations Matrix) - Renamed: "Source Policy" → "Quellen-Verwaltung" - Moved: From Paket 1 (Pflicht) to Zusatzmodule (optional) - sdk-steps.ts: isOptional=true, requirements no longer depends on it - Sidebar: Added under Zusatzmodule section - Page reduced from 365 → 130 lines Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,364 +1,130 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* Source Policy Management Page (SDK Version)
|
||||
* Source Policy — Quellen-Whitelist fuer RAG Pipeline
|
||||
*
|
||||
* Whitelist-based data source management for compliance RAG corpus.
|
||||
* Controls which legal sources may be used, PII rules, and audit trail.
|
||||
* Kontrolliert welche externen Quellen (Rechtstexte, Standards, Leitfaeden)
|
||||
* in die lokale Knowledge Base ingested werden duerfen.
|
||||
* Enterprise-Feature fuer Kanzleien/Unternehmen mit eigenem RAG-System.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSDK } from '@/lib/sdk'
|
||||
import StepHeader from '@/components/sdk/StepHeader/StepHeader'
|
||||
import { SourcesTab } from '@/components/sdk/source-policy/SourcesTab'
|
||||
import { OperationsMatrixTab } from '@/components/sdk/source-policy/OperationsMatrixTab'
|
||||
import { PIIRulesTab } from '@/components/sdk/source-policy/PIIRulesTab'
|
||||
import { AuditTab } from '@/components/sdk/source-policy/AuditTab'
|
||||
|
||||
// API base URL — now uses Next.js proxy routes
|
||||
const API_BASE = '/api/sdk/v1/source-policy'
|
||||
|
||||
interface PolicyStats {
|
||||
active_policies: number
|
||||
allowed_sources: number
|
||||
pii_rules: number
|
||||
blocked_today: number
|
||||
blocked_total: number
|
||||
}
|
||||
|
||||
type TabId = 'dashboard' | 'sources' | 'operations' | 'pii' | 'audit' | 'blocked'
|
||||
|
||||
interface BlockedContent {
|
||||
id: string
|
||||
content_type: string
|
||||
pattern: string
|
||||
reason: string
|
||||
blocked_at: string
|
||||
source?: string
|
||||
}
|
||||
type TabId = 'overview' | 'sources' | 'operations'
|
||||
|
||||
export default function SourcePolicyPage() {
|
||||
const { state } = useSDK()
|
||||
const [activeTab, setActiveTab] = useState<TabId>('dashboard')
|
||||
const [activeTab, setActiveTab] = useState<TabId>('overview')
|
||||
const [stats, setStats] = useState<PolicyStats | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [blockedContent, setBlockedContent] = useState<BlockedContent[]>([])
|
||||
const [blockedLoading, setBlockedLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
fetchStats()
|
||||
fetch(`${API_BASE}/policy-stats`)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => setStats(data))
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab === 'blocked') {
|
||||
fetchBlockedContent()
|
||||
}
|
||||
}, [activeTab])
|
||||
|
||||
const fetchBlockedContent = async () => {
|
||||
setBlockedLoading(true)
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/blocked-content`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setBlockedContent(Array.isArray(data) ? data : (data.items || []))
|
||||
}
|
||||
} catch {
|
||||
// silently ignore — empty state shown
|
||||
} finally {
|
||||
setBlockedLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveBlocked = async (id: string) => {
|
||||
try {
|
||||
await fetch(`${API_BASE}/blocked-content/${id}`, { method: 'DELETE' })
|
||||
setBlockedContent(prev => prev.filter(item => item.id !== id))
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const res = await fetch(`${API_BASE}/policy-stats`)
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Fehler beim Laden der Statistiken')
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
setStats(data)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Unbekannter Fehler')
|
||||
setStats({
|
||||
active_policies: 0,
|
||||
allowed_sources: 0,
|
||||
pii_rules: 0,
|
||||
blocked_today: 0,
|
||||
blocked_total: 0,
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const tabs: { id: TabId; name: string; icon: JSX.Element }[] = [
|
||||
{
|
||||
id: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'sources',
|
||||
name: 'Quellen',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'operations',
|
||||
name: 'Operations',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'pii',
|
||||
name: 'PII-Regeln',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'audit',
|
||||
name: 'Audit',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'blocked',
|
||||
name: 'Blockierte Inhalte',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
const tabs: { id: TabId; label: string }[] = [
|
||||
{ id: 'overview', label: 'Uebersicht' },
|
||||
{ id: 'sources', label: 'Quellen-Whitelist' },
|
||||
{ id: 'operations', label: 'Berechtigungen' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<StepHeader stepId="source-policy" showProgress={true} />
|
||||
|
||||
{/* Error Display */}
|
||||
{error && (
|
||||
<div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg text-red-700 flex items-center justify-between">
|
||||
<span>{error}</span>
|
||||
<button onClick={() => setError(null)} className="text-red-500 hover:text-red-700">
|
||||
×
|
||||
</button>
|
||||
{/* Header */}
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Quellen-Verwaltung</h1>
|
||||
<p className="mt-1 text-gray-500">
|
||||
Definieren Sie welche externen Quellen in Ihre Knowledge Base aufgenommen werden duerfen.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Stats Cards */}
|
||||
{/* Stats */}
|
||||
{stats && (
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-4">
|
||||
<div className="text-2xl font-bold text-purple-600">{stats.active_policies}</div>
|
||||
<div className="text-sm text-slate-500">Aktive Policies</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="text-3xl font-bold text-green-600">{stats.allowed_sources}</div>
|
||||
<div className="text-sm text-gray-500 mt-1">Zugelassene Quellen</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-4">
|
||||
<div className="text-2xl font-bold text-green-600">{stats.allowed_sources}</div>
|
||||
<div className="text-sm text-slate-500">Zugelassene Quellen</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-4">
|
||||
<div className="text-2xl font-bold text-red-600">{stats.blocked_today}</div>
|
||||
<div className="text-sm text-slate-500">Blockiert (heute)</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-xl border border-slate-200 p-4">
|
||||
<div className="text-2xl font-bold text-blue-600">{stats.pii_rules}</div>
|
||||
<div className="text-sm text-slate-500">PII-Regeln</div>
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<div className="text-3xl font-bold text-purple-600">{stats.active_policies}</div>
|
||||
<div className="text-sm text-gray-500 mt-1">Aktive Policies</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors flex items-center gap-2 ${
|
||||
activeTab === tab.id
|
||||
? 'bg-purple-600 text-white'
|
||||
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
|
||||
}`}
|
||||
>
|
||||
{tab.icon}
|
||||
{tab.name}
|
||||
<div className="flex border-b border-gray-200">
|
||||
{tabs.map(tab => (
|
||||
<button key={tab.id} onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors ${
|
||||
activeTab === tab.id ? 'text-purple-600 border-purple-600' : 'text-gray-500 border-transparent hover:text-gray-700'
|
||||
}`}>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
<>
|
||||
{activeTab === 'dashboard' && stats && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Quellen-Uebersicht</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-600">Zugelassene Quellen</span>
|
||||
<span className="text-2xl font-bold text-green-600">{stats.allowed_sources}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-600">Aktive Policies</span>
|
||||
<span className="text-2xl font-bold text-purple-600">{stats.active_policies}</span>
|
||||
</div>
|
||||
<div className="h-2 bg-gray-100 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-green-500 rounded-full"
|
||||
style={{ width: `${stats.allowed_sources > 0 ? Math.min((stats.active_policies / stats.allowed_sources) * 100, 100) : 0}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Datenschutz-Regeln</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-600">PII-Regeln aktiv</span>
|
||||
<span className="text-2xl font-bold text-blue-600">{stats.pii_rules}</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-600">Blockiert (heute)</span>
|
||||
<span className={`text-2xl font-bold ${stats.blocked_today > 0 ? 'text-red-600' : 'text-green-600'}`}>
|
||||
{stats.blocked_today}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-gray-600">Blockiert (gesamt)</span>
|
||||
<span className="text-lg font-semibold text-gray-500">{stats.blocked_total}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2 bg-white rounded-xl border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Compliance-Status</h3>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex-1 text-center p-4 bg-green-50 rounded-lg">
|
||||
<div className="text-sm text-green-700 font-medium">Quellen konfiguriert</div>
|
||||
<div className="text-3xl font-bold text-green-600 mt-1">
|
||||
{stats.allowed_sources > 0 ? 'Ja' : 'Nein'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 text-center p-4 bg-blue-50 rounded-lg">
|
||||
<div className="text-sm text-blue-700 font-medium">PII-Schutz aktiv</div>
|
||||
<div className="text-3xl font-bold text-blue-600 mt-1">
|
||||
{stats.pii_rules > 0 ? 'Ja' : 'Nein'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 text-center p-4 bg-purple-50 rounded-lg">
|
||||
<div className="text-sm text-purple-700 font-medium">Policies definiert</div>
|
||||
<div className="text-3xl font-bold text-purple-600 mt-1">
|
||||
{stats.active_policies > 0 ? 'Ja' : 'Nein'}
|
||||
{/* Tab: Uebersicht */}
|
||||
{activeTab === 'overview' && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6 space-y-4">
|
||||
<h3 className="font-semibold text-gray-900">Was ist die Quellen-Verwaltung?</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
Die Quellen-Verwaltung kontrolliert welche externen Dokumente und Rechtsquellen
|
||||
in Ihre lokale RAG-Pipeline (Knowledge Base) aufgenommen werden duerfen.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
||||
<div className="bg-green-50 rounded-lg p-4">
|
||||
<h4 className="font-medium text-green-800 text-sm">Quellen-Whitelist</h4>
|
||||
<p className="text-xs text-green-700 mt-1">
|
||||
Definieren Sie vertrauenswuerdige Quellen: EUR-Lex, BSI Grundschutz,
|
||||
Behoerden-Leitfaeden, eigene Dokumente.
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-blue-50 rounded-lg p-4">
|
||||
<h4 className="font-medium text-blue-800 text-sm">Berechtigungen</h4>
|
||||
<p className="text-xs text-blue-700 mt-1">
|
||||
Pro Quelle festlegen: Darf sie ingested, durchsucht, exportiert
|
||||
oder mit Dritten geteilt werden?
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-purple-50 rounded-lg p-4">
|
||||
<h4 className="font-medium text-purple-800 text-sm">Lizenzen</h4>
|
||||
<p className="text-xs text-purple-700 mt-1">
|
||||
Jede Quelle wird mit Lizenzinformationen versehen
|
||||
(DL-DE-BY, CC-BY, CC0, eigene Dokumente).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{stats && stats.allowed_sources === 0 && (
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 mt-4">
|
||||
<p className="text-sm text-amber-800">
|
||||
<strong>Noch keine Quellen konfiguriert.</strong> Wechseln Sie zum Tab "Quellen-Whitelist"
|
||||
um Ihre ersten Quellen hinzuzufuegen.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 'dashboard' && !stats && loading && (
|
||||
<div className="text-center py-12 text-slate-500">Lade Dashboard...</div>
|
||||
</div>
|
||||
)}
|
||||
{activeTab === 'sources' && <SourcesTab apiBase={API_BASE} onUpdate={fetchStats} />}
|
||||
|
||||
{/* Tab: Quellen */}
|
||||
{activeTab === 'sources' && <SourcesTab apiBase={API_BASE} onUpdate={() => {
|
||||
fetch(`${API_BASE}/policy-stats`).then(r => r.ok ? r.json() : null).then(setStats).catch(() => {})
|
||||
}} />}
|
||||
|
||||
{/* Tab: Berechtigungen */}
|
||||
{activeTab === 'operations' && <OperationsMatrixTab apiBase={API_BASE} />}
|
||||
{activeTab === 'pii' && <PIIRulesTab apiBase={API_BASE} onUpdate={fetchStats} />}
|
||||
{activeTab === 'audit' && <AuditTab apiBase={API_BASE} />}
|
||||
{activeTab === 'blocked' && (
|
||||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||||
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h3 className="text-lg font-semibold text-gray-900">Blockierte Inhalte</h3>
|
||||
<button
|
||||
onClick={fetchBlockedContent}
|
||||
className="text-sm text-purple-600 hover:text-purple-700"
|
||||
>
|
||||
Aktualisieren
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{blockedLoading ? (
|
||||
<div className="p-8 text-center text-gray-500">Lade blockierte Inhalte...</div>
|
||||
) : blockedContent.length === 0 ? (
|
||||
<div className="p-8 text-center">
|
||||
<div className="w-12 h-12 mx-auto bg-green-100 rounded-full flex items-center justify-center mb-3">
|
||||
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<p className="text-gray-500 text-sm">Keine blockierten Inhalte vorhanden.</p>
|
||||
</div>
|
||||
) : (
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-gray-50 border-b border-gray-200">
|
||||
<tr>
|
||||
<th className="text-left px-4 py-3 text-gray-600 font-medium">Typ</th>
|
||||
<th className="text-left px-4 py-3 text-gray-600 font-medium">Muster / Pattern</th>
|
||||
<th className="text-left px-4 py-3 text-gray-600 font-medium">Grund</th>
|
||||
<th className="text-left px-4 py-3 text-gray-600 font-medium">Blockiert am</th>
|
||||
<th className="text-left px-4 py-3 text-gray-600 font-medium">Quelle</th>
|
||||
<th className="px-4 py-3" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-100">
|
||||
{blockedContent.map(item => (
|
||||
<tr key={item.id} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-3">
|
||||
<span className="px-2 py-0.5 bg-red-100 text-red-700 rounded text-xs font-medium">
|
||||
{item.content_type}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 font-mono text-xs text-gray-700 max-w-xs truncate">
|
||||
{item.pattern}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-gray-600">{item.reason}</td>
|
||||
<td className="px-4 py-3 text-gray-500">
|
||||
{new Date(item.blocked_at).toLocaleDateString('de-DE')}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-gray-500">{item.source || '—'}</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<button
|
||||
onClick={() => handleRemoveBlocked(item.id)}
|
||||
className="text-red-500 hover:text-red-700 text-xs px-2 py-1 rounded hover:bg-red-50"
|
||||
>
|
||||
Entfernen
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ export function SidebarModuleList({ collapsed, projectId, pendingCRCount }: Side
|
||||
<AdditionalModuleItem href="/sdk/workshop" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z" /></svg>} label="Workshop" isActive={pathname === '/sdk/workshop'} collapsed={collapsed} projectId={projectId} />
|
||||
<AdditionalModuleItem href="/sdk/portfolio" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" /></svg>} label="Portfolio" isActive={pathname === '/sdk/portfolio'} collapsed={collapsed} projectId={projectId} />
|
||||
<AdditionalModuleItem href="/sdk/roadmap" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2" /></svg>} label="Roadmap" isActive={pathname === '/sdk/roadmap'} collapsed={collapsed} projectId={projectId} />
|
||||
<AdditionalModuleItem href="/sdk/source-policy" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" /></svg>} label="Quellen-Verwaltung" isActive={pathname?.startsWith('/sdk/source-policy') ?? false} collapsed={collapsed} projectId={projectId} />
|
||||
<AdditionalModuleItem href="/sdk/isms" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>} label="ISMS (ISO 27001)" isActive={pathname === '/sdk/isms'} collapsed={collapsed} projectId={projectId} />
|
||||
<AdditionalModuleItem href="/sdk/audit-llm" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /></svg>} label="LLM Audit" isActive={pathname === '/sdk/audit-llm'} collapsed={collapsed} projectId={projectId} />
|
||||
<AdditionalModuleItem href="/sdk/rbac" icon={<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>} label="RBAC Admin" isActive={pathname === '/sdk/rbac'} collapsed={collapsed} projectId={projectId} />
|
||||
|
||||
@@ -89,13 +89,13 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
phase: 1,
|
||||
package: 'vorbereitung',
|
||||
order: 7,
|
||||
name: 'Source Policy',
|
||||
name: 'Quellen-Verwaltung',
|
||||
nameShort: 'Quellen',
|
||||
description: 'Datenquellen-Governance & Whitelist',
|
||||
description: 'RAG Quellen-Whitelist (Enterprise)',
|
||||
url: '/sdk/source-policy',
|
||||
checkpointId: 'CP-SPOL',
|
||||
prerequisiteSteps: ['modules'],
|
||||
isOptional: false },
|
||||
isOptional: true },
|
||||
|
||||
// PAKET 2: ANALYSE (Assessment)
|
||||
{
|
||||
@@ -109,7 +109,7 @@ export const SDK_STEPS: SDKStep[] = [
|
||||
description: 'Pr\u00fcfaspekte aus Regulierungen ableiten',
|
||||
url: '/sdk/requirements',
|
||||
checkpointId: 'CP-REQ',
|
||||
prerequisiteSteps: ['source-policy'],
|
||||
prerequisiteSteps: ['modules'],
|
||||
isOptional: false },
|
||||
{
|
||||
id: 'controls',
|
||||
|
||||
Reference in New Issue
Block a user