Files
breakpilot-compliance/admin-compliance/components/sdk/source-policy/SourceModals.tsx
Sharang Parnerkar b00fe6cb73 refactor(admin): split remaining components
Split 4 oversized component files (all >500 LOC) into sibling modules:
- SDKPipelineSidebar → Icons + Parts siblings (193/264/35 LOC)
- SourcesTab → SourceModals sibling (311/243 LOC)
- ScopeDecisionTab → ScopeDecisionSections sibling (127/444 LOC)
- ComplianceAdvisorWidget → ComplianceAdvisorParts sibling (265/131 LOC)

Zero behavior changes; all logic relocated verbatim.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 12:41:29 +02:00

244 lines
8.8 KiB
TypeScript

'use client'
// =============================================================================
// Source Policy - New/Edit Source Modals
// =============================================================================
interface AllowedSource {
id: string
domain: string
name: string
description?: string
license?: string
legal_basis?: string
trust_boost: number
source_type: string
active: boolean
metadata?: Record<string, unknown>
created_at: string
updated_at?: string
}
const LICENSES = [
{ value: 'DL-DE-BY-2.0', label: 'Datenlizenz Deutschland' },
{ value: 'CC-BY', label: 'Creative Commons BY' },
{ value: 'CC-BY-SA', label: 'Creative Commons BY-SA' },
{ value: 'CC0', label: 'Public Domain' },
{ value: '§5 UrhG', label: 'Amtliche Werke (§5 UrhG)' },
]
// =============================================================================
// NEW SOURCE MODAL
// =============================================================================
interface NewSourceFormState {
domain: string
name: string
license: string
legal_basis: string
trust_boost: number
active: boolean
}
interface NewSourceModalProps {
newSource: NewSourceFormState
saving: boolean
onClose: () => void
onCreate: () => void
onChange: (update: Partial<NewSourceFormState>) => void
}
export function NewSourceModal({ newSource, saving, onClose, onCreate, onChange }: NewSourceModalProps) {
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-xl p-6 w-full max-w-lg mx-4">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Neue Quelle hinzufuegen</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Domain *</label>
<input
type="text"
value={newSource.domain}
onChange={(e) => onChange({ domain: e.target.value })}
placeholder="z.B. nibis.de"
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Name *</label>
<input
type="text"
value={newSource.name}
onChange={(e) => onChange({ name: e.target.value })}
placeholder="z.B. NiBiS Bildungsserver"
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Lizenz *</label>
<select
value={newSource.license}
onChange={(e) => onChange({ license: e.target.value })}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
{LICENSES.map((l) => (
<option key={l.value} value={l.value}>{l.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Rechtsgrundlage</label>
<input
type="text"
value={newSource.legal_basis}
onChange={(e) => onChange({ legal_basis: e.target.value })}
placeholder="z.B. §5 UrhG (Amtliche Werke)"
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Trust Boost</label>
<input
type="range"
min="0"
max="1"
step="0.1"
value={newSource.trust_boost}
onChange={(e) => onChange({ trust_boost: parseFloat(e.target.value) })}
className="w-full"
/>
<div className="text-xs text-slate-500 text-right">
{(newSource.trust_boost * 100).toFixed(0)}%
</div>
</div>
</div>
<div className="flex justify-end gap-3 mt-6 pt-4 border-t border-slate-100">
<button onClick={onClose} className="px-4 py-2 text-slate-600 hover:text-slate-700">
Abbrechen
</button>
<button
onClick={onCreate}
disabled={saving || !newSource.domain || !newSource.name}
className="px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50"
>
{saving ? 'Speichere...' : 'Erstellen'}
</button>
</div>
</div>
</div>
)
}
// =============================================================================
// EDIT SOURCE MODAL
// =============================================================================
interface EditSourceModalProps {
source: AllowedSource
saving: boolean
onClose: () => void
onSave: () => void
onChange: (update: Partial<AllowedSource>) => void
}
export function EditSourceModal({ source, saving, onClose, onSave, onChange }: EditSourceModalProps) {
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-xl p-6 w-full max-w-lg mx-4">
<h3 className="text-lg font-semibold text-slate-900 mb-4">Quelle bearbeiten</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Domain</label>
<input
type="text"
value={source.domain}
disabled
className="w-full px-4 py-2 border border-slate-200 rounded-lg bg-slate-50 text-slate-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Name *</label>
<input
type="text"
value={source.name}
onChange={(e) => onChange({ name: e.target.value })}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Lizenz *</label>
<select
value={source.license}
onChange={(e) => onChange({ license: e.target.value })}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
>
{LICENSES.map((l) => (
<option key={l.value} value={l.value}>{l.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Rechtsgrundlage</label>
<input
type="text"
value={source.legal_basis || ''}
onChange={(e) => onChange({ legal_basis: e.target.value })}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Trust Boost</label>
<input
type="range"
min="0"
max="1"
step="0.1"
value={source.trust_boost}
onChange={(e) => onChange({ trust_boost: parseFloat(e.target.value) })}
className="w-full"
/>
<div className="text-xs text-slate-500 text-right">
{(source.trust_boost * 100).toFixed(0)}%
</div>
</div>
<div className="flex items-center gap-2">
<input
type="checkbox"
id="active"
checked={source.active}
onChange={(e) => onChange({ active: e.target.checked })}
className="w-4 h-4 text-purple-600"
/>
<label htmlFor="active" className="text-sm text-slate-700">Aktiv</label>
</div>
</div>
<div className="flex justify-end gap-3 mt-6 pt-4 border-t border-slate-100">
<button onClick={onClose} className="px-4 py-2 text-slate-600 hover:text-slate-700">
Abbrechen
</button>
<button
onClick={onSave}
disabled={saving}
className="px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 disabled:opacity-50"
>
{saving ? 'Speichere...' : 'Speichern'}
</button>
</div>
</div>
</div>
)
}