refactor(admin): split rbac page.tsx into colocated components
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { ModalBase } from './ModalBase'
|
||||
import { apiFetch } from '../_api'
|
||||
|
||||
export function AssignRoleModal({ onClose, onAssigned }: { onClose: () => void; onAssigned: () => void }) {
|
||||
const [form, setForm] = useState({ user_id: '', role_id: '', namespace_id: '', expires_at: '' })
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.user_id || !form.role_id) { setError('User-ID und Rolle sind Pflichtfelder'); return }
|
||||
setSaving(true)
|
||||
try {
|
||||
await apiFetch('user-roles', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
user_id: form.user_id,
|
||||
role_id: form.role_id,
|
||||
namespace_id: form.namespace_id || null,
|
||||
expires_at: form.expires_at || null,
|
||||
}),
|
||||
})
|
||||
onAssigned()
|
||||
} catch (e) { setError(e instanceof Error ? e.message : 'Fehler') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalBase title="Rolle zuweisen" onClose={onClose}>
|
||||
{error && <div className="mb-3 p-2 bg-red-50 text-red-700 rounded text-sm">{error}</div>}
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">User-ID</label>
|
||||
<input type="text" value={form.user_id} onChange={e => setForm(f => ({ ...f, user_id: e.target.value }))}
|
||||
placeholder="UUID" className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm font-mono" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Rollen-ID</label>
|
||||
<input type="text" value={form.role_id} onChange={e => setForm(f => ({ ...f, role_id: e.target.value }))}
|
||||
placeholder="UUID" className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm font-mono" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Namespace-ID (optional)</label>
|
||||
<input type="text" value={form.namespace_id} onChange={e => setForm(f => ({ ...f, namespace_id: e.target.value }))}
|
||||
placeholder="Leer = Global" className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm font-mono" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Ablaufdatum (optional)</label>
|
||||
<input type="date" value={form.expires_at} onChange={e => setForm(f => ({ ...f, expires_at: e.target.value }))}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 mt-5">
|
||||
<button onClick={onClose} className="px-4 py-2 text-sm text-gray-600 hover:text-gray-800">Abbrechen</button>
|
||||
<button onClick={handleSubmit} disabled={saving}
|
||||
className="px-4 py-2 bg-purple-600 text-white rounded-lg text-sm hover:bg-purple-700 disabled:opacity-50">
|
||||
{saving ? 'Speichern...' : 'Zuweisen'}
|
||||
</button>
|
||||
</div>
|
||||
</ModalBase>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user