290 lines
13 KiB
HTML
Executable File
290 lines
13 KiB
HTML
Executable File
<!--
|
|
Copyright 2025-2026 AIIrondev
|
|
|
|
Licensed under the Inventarsystem EULA (Endbenutzer-Lizenzvertrag).
|
|
See Legal/LICENSE for the full license text.
|
|
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
|
|
For commercial licensing inquiries: https://github.com/AIIrondev
|
|
-->
|
|
{% extends "base.html" %}
|
|
|
|
{% block title %}Benutzer verwalten{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<h1>Benutzer verwalten</h1>
|
|
|
|
<div class="user-management-container">
|
|
<h2>Benutzer</h2>
|
|
|
|
<div class="filter-bar mb-3">
|
|
<div class="row g-2 align-items-end">
|
|
<div class="col-md-3">
|
|
<label for="filter-search" class="form-label">Suche</label>
|
|
<input type="text" class="form-control" id="filter-search" placeholder="Benutzername, Vor- oder Nachname...">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="filter-admin" class="form-label">Rolle</label>
|
|
<select class="form-select" id="filter-admin">
|
|
<option value="">Alle</option>
|
|
<option value="ja">Administrator</option>
|
|
<option value="nein">Standardbenutzer</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="filter-column" class="form-label">Sortieren nach</label>
|
|
<select class="form-select" id="filter-column">
|
|
<option value="0">Benutzername</option>
|
|
<option value="1">Vorname</option>
|
|
<option value="2">Nachname</option>
|
|
<option value="3">Rolle</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label for="filter-direction" class="form-label">Reihenfolge</label>
|
|
<select class="form-select" id="filter-direction">
|
|
<option value="asc">Aufsteigend</option>
|
|
<option value="desc">Absteigend</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button class="btn btn-secondary w-100" onclick="resetFilters()">Filter zurücksetzen</button>
|
|
</div>
|
|
</div>
|
|
<div class="mt-2 text-muted small" id="filter-count"></div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover" id="user-table">
|
|
<thead class="thead-dark">
|
|
<tr>
|
|
<th>Benutzername</th>
|
|
<th>Vorname</th>
|
|
<th>Nachname</th>
|
|
<th>Administrator</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for user in users %}
|
|
<tr>
|
|
<td>{{ user.username }}</td>
|
|
<td>{{ user.name if user.name else user.username }}</td>
|
|
<td>{{ user.last_name if user.last_name else '' }}</td>
|
|
<td>{{ "Ja" if user.admin else "Nein" }}</td>
|
|
<td class="actions">
|
|
<form method="POST" action="{{ url_for('delete_user') }}" class="d-inline">
|
|
<input type="hidden" name="username" value="{{ user.username }}">
|
|
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Sind Sie sicher, dass Sie den Benutzer {{ user.username }} löschen möchten?')">
|
|
Löschen
|
|
</button>
|
|
</form>
|
|
<button type="button" class="btn btn-primary btn-sm"
|
|
data-username="{{ user.username }}"
|
|
data-firstname="{{ user.name if user.name else '' }}"
|
|
data-lastname="{{ user.last_name if user.last_name else '' }}"
|
|
onclick="openEditUserModal(this)">
|
|
Bearbeiten
|
|
</button>
|
|
<button type="button" class="btn btn-warning btn-sm"
|
|
onclick="openResetPasswordModal('{{ user.username }}')">
|
|
Passwort zurücksetzen
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit User Modal -->
|
|
<div class="modal fade" id="editUserModal" tabindex="-1" aria-labelledby="editUserModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="editUserModalLabel">Benutzer bearbeiten</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form method="POST" action="{{ url_for('admin_update_user_name') }}">
|
|
<div class="modal-body">
|
|
<input type="hidden" id="edit-username" name="username">
|
|
<div class="mb-3">
|
|
<label for="edit-username-display" class="form-label">Benutzer:</label>
|
|
<input type="text" class="form-control" id="edit-username-display" disabled>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="edit-name" class="form-label">Vorname:</label>
|
|
<input type="text" class="form-control" id="edit-name" name="name">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="edit-last-name" class="form-label">Nachname:</label>
|
|
<input type="text" class="form-control" id="edit-last-name" name="last_name">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Speichern</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reset Password Modal -->
|
|
<div class="modal fade" id="resetPasswordModal" tabindex="-1" aria-labelledby="resetPasswordModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="resetPasswordModalLabel">Passwort zurücksetzen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form method="POST" action="{{ url_for('admin_reset_user_password') }}">
|
|
<div class="modal-body">
|
|
<input type="hidden" id="reset-username" name="username">
|
|
<div class="mb-3">
|
|
<label for="username-display" class="form-label">Benutzer:</label>
|
|
<input type="text" class="form-control" id="username-display" disabled>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="new-password" class="form-label">Neues Passwort:</label>
|
|
<input type="password" class="form-control" id="new-password" name="new_password" required>
|
|
<div class="password-requirements small text-muted mt-1">
|
|
<p>Das Passwort muss mindestens 6 Zeichen lang sein.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Passwort zurücksetzen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function applyFilters() {
|
|
var search = document.getElementById('filter-search').value.toLowerCase();
|
|
var adminFilter = document.getElementById('filter-admin').value.toLowerCase();
|
|
var colIndex = parseInt(document.getElementById('filter-column').value);
|
|
var direction = document.getElementById('filter-direction').value;
|
|
|
|
var table = document.getElementById('user-table');
|
|
var tbody = table.querySelector('tbody');
|
|
var rows = Array.from(tbody.querySelectorAll('tr'));
|
|
|
|
var visible = rows.filter(function(row) {
|
|
var cells = row.querySelectorAll('td');
|
|
var username = cells[0] ? cells[0].textContent.toLowerCase() : '';
|
|
var firstname = cells[1] ? cells[1].textContent.toLowerCase() : '';
|
|
var lastname = cells[2] ? cells[2].textContent.toLowerCase() : '';
|
|
var admin = cells[3] ? cells[3].textContent.toLowerCase().trim() : '';
|
|
|
|
var matchesSearch = !search ||
|
|
username.includes(search) ||
|
|
firstname.includes(search) ||
|
|
lastname.includes(search);
|
|
|
|
var matchesAdmin = !adminFilter || admin === adminFilter;
|
|
|
|
return matchesSearch && matchesAdmin;
|
|
});
|
|
|
|
var hidden = rows.filter(function(row) { return visible.indexOf(row) === -1; });
|
|
|
|
hidden.forEach(function(row) { row.style.display = 'none'; });
|
|
|
|
visible.sort(function(a, b) {
|
|
var cellA = a.querySelectorAll('td')[colIndex];
|
|
var cellB = b.querySelectorAll('td')[colIndex];
|
|
var valA = cellA ? cellA.textContent.trim().toLowerCase() : '';
|
|
var valB = cellB ? cellB.textContent.trim().toLowerCase() : '';
|
|
if (valA < valB) return direction === 'asc' ? -1 : 1;
|
|
if (valA > valB) return direction === 'asc' ? 1 : -1;
|
|
return 0;
|
|
});
|
|
|
|
visible.forEach(function(row) {
|
|
row.style.display = '';
|
|
tbody.appendChild(row);
|
|
});
|
|
|
|
var countEl = document.getElementById('filter-count');
|
|
countEl.textContent = visible.length + ' von ' + rows.length + ' Benutzer angezeigt';
|
|
}
|
|
|
|
function resetFilters() {
|
|
document.getElementById('filter-search').value = '';
|
|
document.getElementById('filter-admin').value = '';
|
|
document.getElementById('filter-column').value = '0';
|
|
document.getElementById('filter-direction').value = 'asc';
|
|
applyFilters();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
applyFilters();
|
|
document.getElementById('filter-search').addEventListener('input', applyFilters);
|
|
document.getElementById('filter-admin').addEventListener('change', applyFilters);
|
|
document.getElementById('filter-column').addEventListener('change', applyFilters);
|
|
document.getElementById('filter-direction').addEventListener('change', applyFilters);
|
|
});
|
|
|
|
function openEditUserModal(button) {
|
|
var username = button.getAttribute('data-username');
|
|
var name = button.getAttribute('data-firstname');
|
|
var lastName = button.getAttribute('data-lastname');
|
|
|
|
document.getElementById('edit-username').value = username;
|
|
document.getElementById('edit-username-display').value = username;
|
|
document.getElementById('edit-name').value = name;
|
|
document.getElementById('edit-last-name').value = lastName;
|
|
|
|
var modal = new bootstrap.Modal(document.getElementById('editUserModal'));
|
|
modal.show();
|
|
}
|
|
|
|
function openResetPasswordModal(username) {
|
|
document.getElementById('reset-username').value = username;
|
|
document.getElementById('username-display').value = username;
|
|
|
|
// Open modal using Bootstrap
|
|
var modal = new bootstrap.Modal(document.getElementById('resetPasswordModal'));
|
|
modal.show();
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.user-management-container {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.actions {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.actions form {
|
|
display: inline-block;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.filter-bar {
|
|
background-color: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
|
|
.password-requirements {
|
|
background-color: #f8f9fa;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.password-requirements p {
|
|
margin-bottom: 5px;
|
|
}
|
|
</style>
|
|
{% endblock %} |