Files
breakpilot-compliance/admin-compliance/components/sdk/source-policy/PIIRuleModals.tsx

223 lines
8.4 KiB
TypeScript

'use client'
// =============================================================================
// PII RULE MODALS
// NewRuleModal and EditRuleModal extracted from PIIRulesTab for LOC compliance.
// =============================================================================
interface PIIRule {
id: string
name: string
description?: string
pattern?: string
category: string
action: string
active: boolean
created_at: string
}
export const CATEGORIES = [
{ value: 'email', label: 'E-Mail-Adressen' },
{ value: 'phone', label: 'Telefonnummern' },
{ value: 'iban', label: 'IBAN/Bankdaten' },
{ value: 'name', label: 'Personennamen' },
{ value: 'address', label: 'Adressen' },
{ value: 'id_number', label: 'Ausweisnummern' },
{ value: 'health', label: 'Gesundheitsdaten' },
{ value: 'other', label: 'Sonstige' },
]
export const ACTIONS = [
{ value: 'warn', label: 'Warnung', color: 'bg-amber-100 text-amber-700' },
{ value: 'mask', label: 'Maskieren', color: 'bg-orange-100 text-orange-700' },
{ value: 'block', label: 'Blockieren', color: 'bg-red-100 text-red-700' },
]
// ---------------------------------------------------------------------------
// NewRuleModal
// ---------------------------------------------------------------------------
export interface NewRuleState {
name: string
pattern: string
category: string
action: string
active: boolean
}
interface NewRuleModalProps {
newRule: NewRuleState
onChange: (rule: NewRuleState) => void
onSubmit: () => void
onClose: () => void
saving: boolean
}
export function NewRuleModal({ newRule, onChange, onSubmit, onClose, saving }: NewRuleModalProps) {
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 PII-Regel</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Name *</label>
<input
type="text"
value={newRule.name}
onChange={(e) => onChange({ ...newRule, name: e.target.value })}
placeholder="z.B. Deutsche Telefonnummern"
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">Kategorie *</label>
<select
value={newRule.category}
onChange={(e) => onChange({ ...newRule, category: 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"
>
{CATEGORIES.map((c) => (
<option key={c.value} value={c.value}>{c.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Muster (Regex)</label>
<textarea
value={newRule.pattern}
onChange={(e) => onChange({ ...newRule, pattern: e.target.value })}
placeholder={'Regex-Muster, z.B. (?:\\+49|0)[\\s.-]?\\d{2,4}...'}
rows={3}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent font-mono text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Aktion *</label>
<select
value={newRule.action}
onChange={(e) => onChange({ ...newRule, action: 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"
>
{ACTIONS.map((a) => (
<option key={a.value} value={a.value}>{a.label}</option>
))}
</select>
</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={onSubmit}
disabled={saving || !newRule.name || !newRule.pattern}
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>
)
}
// ---------------------------------------------------------------------------
// EditRuleModal
// ---------------------------------------------------------------------------
interface EditRuleModalProps {
editingRule: PIIRule
onChange: (rule: PIIRule) => void
onSubmit: () => void
onClose: () => void
saving: boolean
}
export function EditRuleModal({ editingRule, onChange, onSubmit, onClose, saving }: EditRuleModalProps) {
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">PII-Regel bearbeiten</h3>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Name *</label>
<input
type="text"
value={editingRule.name}
onChange={(e) => onChange({ ...editingRule, 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">Kategorie</label>
<select
value={editingRule.category}
onChange={(e) => onChange({ ...editingRule, category: 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"
>
{CATEGORIES.map((c) => (
<option key={c.value} value={c.value}>{c.label}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Muster (Regex)</label>
<textarea
value={editingRule.pattern || ''}
onChange={(e) => onChange({ ...editingRule, pattern: e.target.value })}
rows={3}
className="w-full px-4 py-2 border border-slate-200 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent font-mono text-sm"
/>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">Aktion</label>
<select
value={editingRule.action}
onChange={(e) => onChange({ ...editingRule, action: 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"
>
{ACTIONS.map((a) => (
<option key={a.value} value={a.value}>{a.label}</option>
))}
</select>
</div>
<div className="flex items-center gap-2">
<input
type="checkbox"
id="edit_active"
checked={editingRule.active}
onChange={(e) => onChange({ ...editingRule, active: e.target.checked })}
className="w-4 h-4 text-purple-600"
/>
<label htmlFor="edit_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={onSubmit}
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>
)
}