223 lines
8.4 KiB
TypeScript
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>
|
|
)
|
|
}
|