optimized admin protection
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { usePermissions } from '../hooks/usePermissions'
|
||||
|
||||
const Users = () => {
|
||||
const { authFetch } = useAuth()
|
||||
const { refreshPermissions } = usePermissions()
|
||||
const [users, setUsers] = useState([])
|
||||
const [groups, setGroups] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -12,14 +14,20 @@ const Users = () => {
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const [userToDelete, setUserToDelete] = useState(null)
|
||||
const [confirmChecked, setConfirmChecked] = useState(false)
|
||||
const [showToggleModal, setShowToggleModal] = useState(false)
|
||||
const [userToToggle, setUserToToggle] = useState(null)
|
||||
const [confirmToggleChecked, setConfirmToggleChecked] = useState(false)
|
||||
const [formData, setFormData] = useState({
|
||||
username: '',
|
||||
email: '',
|
||||
oldPassword: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
isAdmin: false,
|
||||
enabled: true,
|
||||
groupIds: []
|
||||
})
|
||||
const [showAdminWarning, setShowAdminWarning] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsers()
|
||||
@@ -81,18 +89,24 @@ const Users = () => {
|
||||
|
||||
const body = editingUser
|
||||
? {
|
||||
...(formData.username && { username: formData.username }),
|
||||
...(formData.email && { email: formData.email }),
|
||||
// Username/Email nur setzen wenn nicht der spezielle Admin-User mit UID 'admin'
|
||||
...(formData.username && editingUser.id !== 'admin' && { username: formData.username }),
|
||||
...(formData.email && editingUser.id !== 'admin' && { email: formData.email }),
|
||||
...(formData.password && {
|
||||
password: formData.password,
|
||||
oldPassword: formData.oldPassword
|
||||
}),
|
||||
// isAdmin nur setzen wenn nicht UID 'admin' (UID 'admin' ist immer Admin)
|
||||
...(formData.isAdmin !== undefined && editingUser.id !== 'admin' && { isAdmin: formData.isAdmin }),
|
||||
// enabled wird nicht über das Bearbeitungsformular geändert, nur über den Button in der Liste
|
||||
...(formData.groupIds !== undefined && { groupIds: formData.groupIds })
|
||||
}
|
||||
: {
|
||||
username: formData.username,
|
||||
email: formData.email,
|
||||
password: formData.password,
|
||||
isAdmin: formData.isAdmin || false,
|
||||
enabled: true, // Neue User sind immer aktiviert, enabled kann nur für UID 'admin' geändert werden
|
||||
groupIds: formData.groupIds || []
|
||||
}
|
||||
|
||||
@@ -106,9 +120,12 @@ const Users = () => {
|
||||
|
||||
if (response.ok) {
|
||||
await fetchUsers()
|
||||
setFormData({ username: '', email: '', oldPassword: '', password: '', confirmPassword: '', groupIds: [] })
|
||||
setFormData({ username: '', email: '', oldPassword: '', password: '', confirmPassword: '', isAdmin: false, enabled: true, groupIds: [] })
|
||||
setShowForm(false)
|
||||
setEditingUser(null)
|
||||
setShowAdminWarning(false)
|
||||
// Aktualisiere Berechtigungen nach Änderung an Benutzern (Gruppen-Zuweisungen könnten sich geändert haben)
|
||||
refreshPermissions()
|
||||
} else {
|
||||
const errorData = await response.json()
|
||||
setError(errorData.error || 'Fehler beim Speichern des Benutzers')
|
||||
@@ -129,6 +146,8 @@ const Users = () => {
|
||||
oldPassword: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
isAdmin: user.isAdmin || false,
|
||||
enabled: user.enabled !== undefined ? user.enabled : true, // Wird nicht im Formular angezeigt, nur für internen Zustand
|
||||
groupIds: user.groupIds || []
|
||||
})
|
||||
setShowForm(true)
|
||||
@@ -140,6 +159,67 @@ const Users = () => {
|
||||
setConfirmChecked(false)
|
||||
}
|
||||
|
||||
const handleToggleEnabled = (user) => {
|
||||
if (user.id !== 'admin') {
|
||||
return
|
||||
}
|
||||
setUserToToggle(user)
|
||||
setShowToggleModal(true)
|
||||
setConfirmToggleChecked(false)
|
||||
}
|
||||
|
||||
const confirmToggle = async () => {
|
||||
if (!confirmToggleChecked || !userToToggle) {
|
||||
return
|
||||
}
|
||||
|
||||
const newEnabled = !userToToggle.enabled
|
||||
const action = newEnabled ? 'aktivieren' : 'deaktivieren'
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const response = await authFetch(`/api/users/${userToToggle.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
enabled: newEnabled
|
||||
}),
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
await fetchUsers()
|
||||
setShowToggleModal(false)
|
||||
setUserToToggle(null)
|
||||
setConfirmToggleChecked(false)
|
||||
// Aktualisiere Berechtigungen nach Änderung
|
||||
refreshPermissions()
|
||||
} else {
|
||||
const errorData = await response.json().catch(() => ({ error: `Fehler beim ${action}` }))
|
||||
const errorMessage = errorData.error || `Fehler beim ${action} des Admin-Users`
|
||||
setError(errorMessage)
|
||||
// Schließe Modal bei Fehler
|
||||
if (response.status === 403) {
|
||||
setShowToggleModal(false)
|
||||
setUserToToggle(null)
|
||||
setConfirmToggleChecked(false)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error toggling enabled for admin user:`, err)
|
||||
setError(`Fehler beim ${action} des Admin-Users`)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const cancelToggle = () => {
|
||||
setShowToggleModal(false)
|
||||
setUserToToggle(null)
|
||||
setConfirmToggleChecked(false)
|
||||
}
|
||||
|
||||
const confirmDelete = async () => {
|
||||
if (!confirmChecked || !userToDelete) {
|
||||
return
|
||||
@@ -155,13 +235,23 @@ const Users = () => {
|
||||
setShowDeleteModal(false)
|
||||
setUserToDelete(null)
|
||||
setConfirmChecked(false)
|
||||
// Aktualisiere Berechtigungen nach Löschen eines Benutzers
|
||||
refreshPermissions()
|
||||
} else {
|
||||
const errorData = await response.json().catch(() => ({ error: 'Fehler beim Löschen' }))
|
||||
alert(errorData.error || 'Fehler beim Löschen des Benutzers')
|
||||
const errorMessage = errorData.error || 'Fehler beim Löschen des Benutzers'
|
||||
// Zeige Fehlermeldung
|
||||
setError(errorMessage)
|
||||
// Wenn Admin-Löschung verhindert wurde, schließe Modal
|
||||
if (response.status === 403) {
|
||||
setShowDeleteModal(false)
|
||||
setUserToDelete(null)
|
||||
setConfirmChecked(false)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error deleting user:', err)
|
||||
alert('Fehler beim Löschen des Benutzers')
|
||||
setError('Fehler beim Löschen des Benutzers')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +279,20 @@ const Users = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const handleAdminToggle = (e) => {
|
||||
const isAdmin = e.target.checked
|
||||
if (isAdmin && !showAdminWarning) {
|
||||
setShowAdminWarning(true)
|
||||
}
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
isAdmin,
|
||||
// Wenn Admin aktiviert wird, entferne alle Gruppen und stelle sicher dass enabled=true
|
||||
groupIds: isAdmin ? [] : prev.groupIds,
|
||||
enabled: isAdmin ? true : (prev.enabled !== undefined ? prev.enabled : true) // Admin muss immer enabled sein
|
||||
}))
|
||||
}
|
||||
|
||||
const getPermissionLabel = (permission) => {
|
||||
switch (permission) {
|
||||
case 'READ':
|
||||
@@ -216,7 +320,8 @@ const Users = () => {
|
||||
onClick={() => {
|
||||
setShowForm(!showForm)
|
||||
setEditingUser(null)
|
||||
setFormData({ username: '', email: '', oldPassword: '', password: '', confirmPassword: '', groupIds: [] })
|
||||
setFormData({ username: '', email: '', oldPassword: '', password: '', confirmPassword: '', isAdmin: false, enabled: true, groupIds: [] })
|
||||
setShowAdminWarning(false)
|
||||
}}
|
||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
>
|
||||
@@ -242,9 +347,15 @@ const Users = () => {
|
||||
value={formData.username}
|
||||
onChange={handleChange}
|
||||
required={!editingUser}
|
||||
className="w-full px-4 py-2 bg-slate-700/50 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
disabled={editingUser && editingUser.id === 'admin'}
|
||||
className={`w-full px-4 py-2 bg-slate-700/50 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
editingUser && editingUser.id === 'admin' ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
placeholder="Geben Sie einen Benutzernamen ein"
|
||||
/>
|
||||
{editingUser && editingUser.id === 'admin' && (
|
||||
<p className="mt-1 text-xs text-slate-400">Der Benutzername des Admin-Users mit UID 'admin' kann nicht geändert werden</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-slate-200 mb-2">
|
||||
@@ -257,9 +368,15 @@ const Users = () => {
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
required={!editingUser}
|
||||
className="w-full px-4 py-2 bg-slate-700/50 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
disabled={editingUser && editingUser.id === 'admin'}
|
||||
className={`w-full px-4 py-2 bg-slate-700/50 border border-slate-600 rounded-lg text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
|
||||
editingUser && editingUser.id === 'admin' ? 'opacity-50 cursor-not-allowed' : ''
|
||||
}`}
|
||||
placeholder="Geben Sie eine E-Mail-Adresse ein"
|
||||
/>
|
||||
{editingUser && editingUser.id === 'admin' && (
|
||||
<p className="mt-1 text-xs text-slate-400">Die E-Mail-Adresse des Admin-Users mit UID 'admin' kann nicht geändert werden</p>
|
||||
)}
|
||||
</div>
|
||||
{editingUser && (
|
||||
<div>
|
||||
@@ -344,21 +461,56 @@ const Users = () => {
|
||||
<p className="mt-1 text-xs text-green-400">✓ Passwörter stimmen überein</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Admin Checkbox - nicht für UID 'admin' */}
|
||||
{(!editingUser || editingUser.id !== 'admin') && (
|
||||
<div className="bg-slate-700/30 border border-slate-600/50 rounded-lg p-4">
|
||||
<label className="flex items-start cursor-pointer group">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={formData.isAdmin || false}
|
||||
onChange={handleAdminToggle}
|
||||
disabled={editingUser && editingUser.username === 'admin'} // Admin user kann seinen Status nicht ändern
|
||||
className="mt-1 w-5 h-5 text-blue-600 bg-slate-700 border-slate-600 rounded focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-slate-800 cursor-pointer"
|
||||
/>
|
||||
<div className="ml-3 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-slate-200 font-semibold">Administrator</span>
|
||||
<span className="px-2 py-0.5 bg-red-600/20 text-red-300 rounded text-xs font-medium">
|
||||
VOLLZUGRIFF
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-slate-400 mt-1">
|
||||
Ein Administrator hat vollständigen Zugriff auf alle Funktionen und kann alle Einstellungen verwalten.
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Berechtigungsgruppen - ausgegraut wenn Admin oder UID 'admin' */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-200 mb-2">
|
||||
Berechtigungsgruppen
|
||||
{(formData.isAdmin || (editingUser && editingUser.id === 'admin')) && <span className="text-xs text-slate-400 ml-2">(nicht verfügbar für Administratoren)</span>}
|
||||
</label>
|
||||
<div className="bg-slate-700/50 border border-slate-600 rounded-lg p-4 max-h-60 overflow-y-auto">
|
||||
<div className={`bg-slate-700/50 border border-slate-600 rounded-lg p-4 max-h-60 overflow-y-auto ${
|
||||
formData.isAdmin || (editingUser && editingUser.id === 'admin') ? 'opacity-50 pointer-events-none' : ''
|
||||
}`}>
|
||||
{groups.length === 0 ? (
|
||||
<p className="text-slate-400 text-sm">Keine Berechtigungsgruppen vorhanden</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{groups.map(group => (
|
||||
<label key={group.id} className="flex items-start cursor-pointer hover:bg-slate-600/50 p-2 rounded">
|
||||
<label key={group.id} className={`flex items-start p-2 rounded ${
|
||||
formData.isAdmin || (editingUser && editingUser.id === 'admin') ? 'cursor-not-allowed' : 'cursor-pointer hover:bg-slate-600/50'
|
||||
}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={formData.groupIds?.includes(group.id) || false}
|
||||
onChange={() => handleGroupToggle(group.id)}
|
||||
disabled={formData.isAdmin || (editingUser && editingUser.id === 'admin')}
|
||||
className="w-4 h-4 text-blue-600 bg-slate-600 border-slate-500 rounded focus:ring-blue-500 mt-1"
|
||||
/>
|
||||
<div className="ml-3 flex-1">
|
||||
@@ -396,7 +548,8 @@ const Users = () => {
|
||||
onClick={() => {
|
||||
setShowForm(false)
|
||||
setEditingUser(null)
|
||||
setFormData({ username: '', email: '', oldPassword: '', password: '', confirmPassword: '', groupIds: [] })
|
||||
setFormData({ username: '', email: '', oldPassword: '', password: '', confirmPassword: '', isAdmin: false, enabled: true, groupIds: [] })
|
||||
setShowAdminWarning(false)
|
||||
setError('')
|
||||
}}
|
||||
className="px-6 py-2 bg-slate-600 hover:bg-slate-700 text-white font-semibold rounded-lg transition-colors duration-200"
|
||||
@@ -437,11 +590,23 @@ const Users = () => {
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-xl font-semibold text-white mb-2">
|
||||
{user.username}
|
||||
</h3>
|
||||
<p className="text-slate-300 mb-2">{user.email}</p>
|
||||
{user.groupIds && user.groupIds.length > 0 && (
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<h3 className="text-xl font-semibold text-white">
|
||||
{user.username}
|
||||
</h3>
|
||||
{user.isAdmin && (
|
||||
<span className="px-2 py-0.5 bg-red-600/20 text-red-300 rounded text-xs font-medium border border-red-500/30">
|
||||
ADMIN
|
||||
</span>
|
||||
)}
|
||||
{user.id === 'admin' && user.enabled === false && (
|
||||
<span className="px-2 py-0.5 bg-red-600/20 text-red-300 rounded text-xs font-medium border border-red-500/30">
|
||||
DEAKTIVIERT
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-slate-300 mb-2">{user.email}</p>
|
||||
{!user.isAdmin && user.groupIds && user.groupIds.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<p className="text-sm text-slate-300 mb-1">Berechtigungsgruppen:</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
@@ -472,12 +637,26 @@ const Users = () => {
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(user)}
|
||||
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm rounded-lg transition-colors"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
{user.id === 'admin' ? (
|
||||
<button
|
||||
onClick={() => handleToggleEnabled(user)}
|
||||
className={`px-4 py-2 text-white text-sm rounded-lg transition-colors ${
|
||||
user.enabled
|
||||
? 'bg-yellow-600 hover:bg-yellow-700'
|
||||
: 'bg-green-600 hover:bg-green-700'
|
||||
}`}
|
||||
title={user.enabled ? "Admin-User deaktivieren" : "Admin-User aktivieren"}
|
||||
>
|
||||
{user.enabled ? 'Deaktivieren' : 'Aktivieren'}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => handleDelete(user)}
|
||||
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white text-sm rounded-lg transition-colors"
|
||||
>
|
||||
Löschen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -486,6 +665,66 @@ const Users = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Admin Warning Modal */}
|
||||
{showAdminWarning && (
|
||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="bg-slate-800 rounded-xl shadow-2xl border border-red-600/50 max-w-md w-full p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="flex-shrink-0 w-12 h-12 bg-red-500/20 rounded-full flex items-center justify-center mr-4">
|
||||
<svg
|
||||
className="w-6 h-6 text-red-400"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-white">
|
||||
Administrator-Berechtigung
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<p className="text-slate-300 mb-3">
|
||||
Sie sind dabei, diesem Benutzer <span className="font-semibold text-red-400">Administrator-Rechte</span> zu gewähren.
|
||||
</p>
|
||||
<div className="bg-red-500/10 border border-red-500/30 rounded-lg p-3 mb-4">
|
||||
<p className="text-sm font-semibold text-red-300 mb-2">⚠️ Mögliche Gefahren:</p>
|
||||
<ul className="text-xs text-slate-300 space-y-1 list-disc list-inside">
|
||||
<li>Vollständiger Zugriff auf alle Funktionen und Einstellungen</li>
|
||||
<li>Möglichkeit, andere Benutzer zu erstellen, zu bearbeiten oder zu löschen</li>
|
||||
<li>Zugriff auf alle Spaces, FQDNs und Zertifikate</li>
|
||||
<li>Möglichkeit, Berechtigungsgruppen zu verwalten</li>
|
||||
<li>Keine Einschränkungen durch Berechtigungsgruppen</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p className="text-sm text-slate-400">
|
||||
Möchten Sie wirklich fortfahren?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => setShowAdminWarning(false)}
|
||||
className="flex-1 px-4 py-2 bg-slate-600 hover:bg-slate-700 text-white font-semibold rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowAdminWarning(false)}
|
||||
className="flex-1 px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-semibold rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Verstanden, fortfahren
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Delete Confirmation Modal */}
|
||||
{showDeleteModal && userToDelete && (
|
||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
@@ -548,6 +787,99 @@ const Users = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Toggle Enabled Confirmation Modal */}
|
||||
{showToggleModal && userToToggle && (
|
||||
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className={`bg-slate-800 rounded-xl shadow-2xl border max-w-md w-full p-6 ${
|
||||
userToToggle.enabled ? 'border-yellow-600/50' : 'border-green-600/50'
|
||||
}`}>
|
||||
<div className="flex items-center mb-4">
|
||||
<div className={`flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center mr-4 ${
|
||||
userToToggle.enabled ? 'bg-yellow-500/20' : 'bg-green-500/20'
|
||||
}`}>
|
||||
{userToToggle.enabled ? (
|
||||
<svg
|
||||
className="w-6 h-6 text-yellow-400"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="w-6 h-6 text-green-400"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-white">
|
||||
Admin-User {userToToggle.enabled ? 'deaktivieren' : 'aktivieren'}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<p className="text-slate-300 mb-4">
|
||||
Möchten Sie den Admin-User <span className="font-semibold text-white">{userToToggle.username}</span> (UID: {userToToggle.id}) wirklich {userToToggle.enabled ? 'deaktivieren' : 'aktivieren'}?
|
||||
</p>
|
||||
<p className={`text-sm mb-4 ${
|
||||
userToToggle.enabled ? 'text-yellow-400' : 'text-green-400'
|
||||
}`}>
|
||||
{userToToggle.enabled
|
||||
? 'Der Admin-User kann sich nach der Deaktivierung nicht mehr anmelden und keine API-Calls durchführen.'
|
||||
: 'Der Admin-User kann sich nach der Aktivierung wieder anmelden und API-Calls durchführen.'}
|
||||
</p>
|
||||
|
||||
<label className="flex items-start cursor-pointer group">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={confirmToggleChecked}
|
||||
onChange={(e) => setConfirmToggleChecked(e.target.checked)}
|
||||
className={`mt-1 w-5 h-5 bg-slate-700 border-slate-600 rounded focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-800 cursor-pointer ${
|
||||
userToToggle.enabled
|
||||
? 'text-yellow-600 focus:ring-yellow-500'
|
||||
: 'text-green-600 focus:ring-green-500'
|
||||
}`}
|
||||
/>
|
||||
<span className="ml-3 text-sm text-slate-300 group-hover:text-white transition-colors">
|
||||
Ich bestätige, dass ich den Admin-User {userToToggle.enabled ? 'deaktivieren' : 'aktivieren'} möchte
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={confirmToggle}
|
||||
disabled={!confirmToggleChecked}
|
||||
className={`flex-1 px-4 py-2 disabled:bg-slate-700 disabled:text-slate-500 disabled:cursor-not-allowed text-white font-semibold rounded-lg transition-colors duration-200 ${
|
||||
userToToggle.enabled
|
||||
? 'bg-yellow-600 hover:bg-yellow-700'
|
||||
: 'bg-green-600 hover:bg-green-700'
|
||||
}`}
|
||||
>
|
||||
{userToToggle.enabled ? 'Deaktivieren' : 'Aktivieren'}
|
||||
</button>
|
||||
<button
|
||||
onClick={cancelToggle}
|
||||
className="flex-1 px-4 py-2 bg-slate-600 hover:bg-slate-700 text-white font-semibold rounded-lg transition-colors duration-200"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user