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>
244 lines
8.8 KiB
TypeScript
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>
|
|
)
|
|
}
|