AstraOS-Panel/AstraOS/Frontend/HTML/user_management.html
elias 4c3709b64e
All checks were successful
Deploy AstraOS Panel / deploy (push) Successful in 16s
AstraOS Ver. 1.43
Deaktivierung von Account und Notfication System hinzugefügt
2026-08-18 18:47:46 +02:00

199 lines
14 KiB
HTML

{% extends "sidebar.html" %}
{% block title %}AstraOS — User Management{% endblock %}
{% block head %}
{% endblock %}
{% block content %}
<div x-data="{
showAddUserModal: false,
showDeleteModal: false,
showDeactivateModal: false,
userToDelete: null,
userToEdit: null,
userToToggle: null,
usersData: {},
init() {
this.usersData = JSON.parse(document.getElementById('users-data').textContent);
},
openEditModal(userId) {
this.userToEdit = this.usersData[userId];
this.showAddUserModal = true;
},
openDeactivateModal(userId, username, isActive) {
this.userToToggle = { id: userId, username: username, is_active: isActive };
if (isActive) {
this.showDeactivateModal = true;
} else {
// Direct activate — no reason needed
document.getElementById('toggle-form-' + userId).submit();
}
}
}">
<div class="space-y-10">
<header class="flex justify-between items-center">
<h2 class="text-4xl font-bold tracking-wide {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}">User Management</h2>
<button @click="userToEdit = null; showAddUserModal = true" class="font-bold py-2 px-4 rounded-lg transition text-white
{% if maintenance_active %}bg-yellow-600 hover:bg-yellow-700{% else %}bg-purple-600 hover:bg-purple-700{% endif %}">
+ Neuen Benutzer hinzufügen
</button>
</header>
<div class="bg-black/40 rounded-xl backdrop-blur-lg overflow-hidden
{% if maintenance_active %}border border-yellow-500/30{% else %}border border-purple-500/20{% endif %}">
<table class="min-w-full divide-y {% if maintenance_active %}divide-yellow-500/30{% else %}divide-purple-500/20{% endif %}">
<thead class="bg-black/20">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Benutzername</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Rolle</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Discord</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Status</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Erstellt am</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">Aktionen</span>
</th>
</tr>
</thead>
<tbody class="divide-y {% if maintenance_active %}divide-yellow-500/30{% else %}divide-purple-500/30{% endif %}">
{% for user_table_item in users_for_table %}
<tr class="transition {% if maintenance_active %}hover:bg-yellow-500/5{% else %}hover:bg-purple-500/5{% endif %}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-white">{{ user_table_item.username }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ user_table_item.role_name }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
{% if user_table_item.discord_linked %}
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Verknüpft</span>
{% else %}
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Nicht verknüpft</span>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
{% if user_table_item.get('is_active', True) %}
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-500/20 text-green-300">Aktiv</span>
{% else %}
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-500/20 text-red-300">Deaktiviert</span>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">{{ user_table_item.creation_date.strftime('%d.%m.%Y') if user_table_item.creation_date else 'N/A' }}</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button @click="openEditModal('{{ user_table_item._id | string }}')" class="mr-3 {% if maintenance_active %}text-yellow-400 hover:text-yellow-300{% else %}text-indigo-400 hover:text-indigo-300{% endif %}">Bearbeiten</button>
{% if session.get('user_id') != user_table_item._id | string %}
{% if user_table_item.get('is_active', True) %}
<button @click="openDeactivateModal('{{ user_table_item._id }}', '{{ user_table_item.username }}', true)" class="mr-3 text-orange-400 hover:text-orange-300">Deaktivieren</button>
{% else %}
<form id="toggle-form-{{ user_table_item._id }}" action="/toggle_user_status/{{ user_table_item._id }}" method="POST" class="inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
</form>
<button @click="openDeactivateModal('{{ user_table_item._id }}', '{{ user_table_item.username }}', false)" class="mr-3 text-green-400 hover:text-green-300">Aktivieren</button>
{% endif %}
{% endif %}
<button @click="userToDelete = '{{ user_table_item._id }}'; showDeleteModal = true" class="text-red-400 hover:text-red-300">Löschen</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div x-show="showAddUserModal" @keydown.escape.window="showAddUserModal = false" class="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center justify-center z-50" style="display: none;">
<div @click.away="showAddUserModal = false" class="bg-gray-900 rounded-xl shadow-2xl p-8 w-full max-w-md
{% if maintenance_active %}border border-yellow-500/50{% else %}border border-purple-500/50{% endif %}">
<h3 class="text-2xl font-bold mb-6 {% if maintenance_active %}text-yellow-300{% else %}text-purple-300{% endif %}" x-text="userToEdit ? 'Benutzer bearbeiten' : 'Neuen Benutzer erstellen'"></h3>
<form :action="userToEdit ? '/edit_user/' + userToEdit._id : '/add_user'" method="POST" class="space-y-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div>
<label for="username" class="block text-sm font-medium text-gray-300">Benutzername</label>
<input type="text" name="username" id="username" required :value="userToEdit ? userToEdit.username : ''" class="mt-1 w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white focus:outline-none focus:ring-2 {% if maintenance_active %}focus:ring-yellow-500{% else %}focus:ring-purple-500{% endif %}">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-300">Neues Passwort</label>
<input type="password" name="password" id="password" :required="!userToEdit" :placeholder="userToEdit ? 'Leer lassen, um nicht zu ändern' : ''" class="mt-1 w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white focus:outline-none focus:ring-2 {% if maintenance_active %}focus:ring-yellow-500{% else %}focus:ring-purple-500{% endif %}">
</div>
<div x-data='{
open: false,
roles: {{ roles | tojson }},
selectedRole: null,
init() {
if (this.userToEdit && this.userToEdit.role_id) {
this.selectedRole = this.roles.find(r => r._id === this.userToEdit.role_id);
} else {
this.selectedRole = this.roles.length > 0 ? this.roles[0] : null;
}
}
}' @click.away="open = false" class="relative">
<label class="block text-sm font-medium text-gray-300">Rolle</label>
<input type="hidden" name="role" :value="selectedRole ? selectedRole._id : ''">
<button type="button" @click="open = !open" class="mt-1 w-full flex justify-between items-center px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white focus:outline-none focus:ring-2 {% if maintenance_active %}focus:ring-yellow-500{% else %}focus:ring-purple-500{% endif %}">
<span x-text="selectedRole ? selectedRole.name : 'Rolle auswählen'"></span>
<svg class="w-5 h-5 text-gray-400 transform transition-transform" :class="{ 'rotate-180': open }" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<div x-show="open" x-transition
class="absolute z-10 mt-1 w-full bg-gray-800 border border-gray-700 rounded-lg shadow-lg max-h-60 overflow-y-auto" style="display: none;">
<template x-for="role in roles" :key="role._id">
<div @click="selectedRole = role; open = false"
class="px-3 py-2 text-white cursor-pointer hover:bg-gray-700"
:class="{ '{% if maintenance_active %}bg-yellow-600/20{% else %}bg-purple-600/20{% endif %}': selectedRole && selectedRole._id === role._id }"
x-text="role.name">
</div>
</template>
</div>
</div>
<div class="flex justify-end gap-4 pt-4">
<button type="button" @click="showAddUserModal = false; userToEdit = null" class="px-4 py-2 rounded-lg text-gray-300 hover:bg-gray-700 transition">Abbrechen</button>
<button type="submit" class="px-4 py-2 font-semibold rounded-lg transition text-white
{% if maintenance_active %}bg-yellow-600 hover:bg-yellow-700{% else %}bg-purple-600 hover:bg-purple-700{% endif %}"
x-text="userToEdit ? 'Änderungen speichern' : 'Benutzer erstellen'"></button>
</div>
</form>
</div>
</div>
<!-- Deactivate Confirm Modal -->
<div x-show="showDeactivateModal" @keydown.escape.window="showDeactivateModal = false"
class="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center justify-center z-50"
style="display: none;">
<div @click.away="showDeactivateModal = false"
class="bg-gray-900 border border-orange-500/50 rounded-xl shadow-2xl p-8 w-full max-w-md">
<h3 class="text-2xl font-bold text-orange-400 mb-2">Benutzer deaktivieren</h3>
<p class="text-gray-300 mb-5">Benutzer <span class="font-semibold text-white" x-text="userToToggle ? userToToggle.username : ''"></span> wird deaktiviert. Der Account kann sich nicht mehr anmelden.</p>
<form :action="userToToggle ? '/toggle_user_status/' + userToToggle.id : '#'" method="POST" class="space-y-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div>
<label class="block text-sm font-medium text-gray-300 mb-1">Grund (optional)</label>
<input type="text" name="deactivation_reason" placeholder="z.B. Verstos gegen Regeln..."
class="w-full px-3 py-2 bg-gray-800 border border-gray-700 rounded-lg text-white text-sm focus:outline-none focus:ring-2 focus:ring-orange-500">
</div>
<div class="flex justify-end gap-4">
<button type="button" @click="showDeactivateModal = false"
class="px-4 py-2 rounded-lg text-gray-300 hover:bg-gray-700 transition">Abbrechen</button>
<button type="submit"
class="px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white font-semibold rounded-lg transition">Deaktivieren</button>
</div>
</form>
</div>
</div>
<div x-show="showDeleteModal" @keydown.escape.window="showDeleteModal = false" class="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center justify-center z-50" style="display: none;">
<div @click.away="showDeleteModal = false" class="bg-gray-900 border border-red-500/50 rounded-xl shadow-2xl p-8 w-full max-w-md">
<h3 class="text-2xl font-bold text-red-400 mb-4">Benutzer löschen</h3>
<p class="text-gray-300 mb-6">Möchten Sie diesen Benutzer wirklich endgültig löschen? Diese Aktion kann nicht rückgängig gemacht werden.</p>
<form :action="'/delete_user/' + userToDelete" method="POST" class="flex justify-end gap-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="button" @click="showDeleteModal = false" class="px-4 py-2 rounded-lg text-gray-300 hover:bg-gray-700 transition">Abbrechen</button>
<button type="submit" class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-semibold rounded-lg transition">Löschen</button>
</form>
</div>
</div>
<script id="users-data" type="application/json">
{{ users_for_modal | tojson | safe }}
</script>
</div>
{% endblock %}
{% block loading %}
{% endblock %}