Dateien
Hetzner-Backup/v2_adminpanel/templates/customers.html

193 Zeilen
8.9 KiB
HTML
Originalformat Blame Verlauf

Diese Datei enthält mehrdeutige Unicode-Zeichen
Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.
{% extends "base.html" %}
{% block title %}Kundenverwaltung{% endblock %}
{% macro sortable_header(label, field, current_sort, current_order) %}
<th>
{% if current_sort == field %}
<a href="{{ url_for('customers', sort=field, order='desc' if current_order == 'asc' else 'asc', search=search, page=1) }}"
class="server-sortable">
{% else %}
<a href="{{ url_for('customers', sort=field, order='asc', search=search, page=1) }}"
class="server-sortable">
{% endif %}
{{ label }}
<span class="sort-indicator{% if current_sort == field %} active{% endif %}">
{% if current_sort == field %}
{% if current_order == 'asc' %}↑{% else %}↓{% endif %}
{% else %}
{% endif %}
</span>
</a>
</th>
{% endmacro %}
{% block content %}
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Kundenverwaltung</h2>
<div>
<a href="/customers-licenses" class="btn btn-success">
<i class="bi bi-layout-split"></i> Kombinierte Ansicht
</a>
<a href="/customer/create" class="btn btn-primary">👤 Neuer Kunde</a>
<a href="/create" class="btn btn-primary"> Neue Lizenz</a>
<a href="/batch" class="btn btn-primary">🔑 Batch-Lizenzen</a>
<div class="btn-group">
<button type="button" class="btn btn-info dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
📥 Export
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/export/customers?format=excel">📊 Excel (.xlsx)</a></li>
<li><a class="dropdown-item" href="/export/customers?format=csv">📄 CSV (.csv)</a></li>
</ul>
</div>
</div>
</div>
<!-- Suchformular -->
<div class="card mb-3">
<div class="card-body">
<form method="get" action="/customers" id="customerSearchForm" class="row g-3 align-items-end">
<div class="col-md-10">
<label for="search" class="form-label">🔍 Suchen</label>
<input type="text" class="form-control" id="search" name="search"
placeholder="Kundenname oder E-Mail..."
value="{{ search }}" autofocus>
</div>
<div class="col-md-2">
<a href="/customers" class="btn btn-outline-secondary w-100">Zurücksetzen</a>
</div>
</form>
{% if search %}
<div class="mt-2">
<small class="text-muted">Suchergebnisse für: <strong>{{ search }}</strong></small>
<a href="/customers" class="btn btn-sm btn-outline-secondary ms-2">✖ Suche zurücksetzen</a>
</div>
{% endif %}
</div>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
{{ sortable_header('ID', 'id', sort, order) }}
{{ sortable_header('Name', 'name', sort, order) }}
{{ sortable_header('E-Mail', 'email', sort, order) }}
{{ sortable_header('Erstellt am', 'created_at', sort, order) }}
{{ sortable_header('Lizenzen (Aktiv/Gesamt)', 'licenses', sort, order) }}
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for customer in customers %}
<tr>
<td>{{ customer[0] }}</td>
<td>
{{ customer[1] }}
{% if customer[4] %}
<span class="badge bg-secondary ms-1" title="Testdaten">🧪</span>
{% endif %}
</td>
<td>{{ customer[2] or '-' }}</td>
<td>{{ customer[3].strftime('%d.%m.%Y %H:%M') }}</td>
<td>
<span class="badge bg-info">{{ customer[6] }}/{{ customer[5] }}</span>
</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<a href="/customer/edit/{{ customer[0] }}" class="btn btn-outline-primary">✏️ Bearbeiten</a>
{% if customer[5] == 0 %}
<form method="post" action="/customer/delete/{{ customer[0] }}" style="display: inline;" onsubmit="return confirm('Kunde wirklich löschen?');">
<button type="submit" class="btn btn-outline-danger">🗑️ Löschen</button>
</form>
{% else %}
<button class="btn btn-outline-danger" disabled title="Kunde hat Lizenzen">🗑️ Löschen</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not customers %}
<div class="text-center py-5">
{% if search %}
<p class="text-muted">Keine Kunden gefunden für: <strong>{{ search }}</strong></p>
<a href="/customers" class="btn btn-secondary">Alle Kunden anzeigen</a>
{% else %}
<p class="text-muted">Noch keine Kunden vorhanden.</p>
<a href="/create" class="btn btn-primary">Erste Lizenz erstellen</a>
{% endif %}
</div>
{% endif %}
</div>
<!-- Pagination -->
{% if total_pages > 1 %}
<nav aria-label="Seitennavigation" class="mt-3">
<ul class="pagination justify-content-center">
<!-- Erste Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers', page=1, search=search, sort=sort, order=order) }}">Erste</a>
</li>
<!-- Vorherige Seite -->
<li class="page-item {% if page == 1 %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers', page=page-1, search=search, sort=sort, order=order) }}"></a>
</li>
<!-- Seitenzahlen -->
{% for p in range(1, total_pages + 1) %}
{% if p >= page - 2 and p <= page + 2 %}
<li class="page-item {% if p == page %}active{% endif %}">
<a class="page-link" href="{{ url_for('customers', page=p, search=search, sort=sort, order=order) }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
<!-- Nächste Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers', page=page+1, search=search, sort=sort, order=order) }}"></a>
</li>
<!-- Letzte Seite -->
<li class="page-item {% if page == total_pages %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('customers', page=total_pages, search=search, sort=sort, order=order) }}">Letzte</a>
</li>
</ul>
<p class="text-center text-muted">
Seite {{ page }} von {{ total_pages }} | Gesamt: {{ total }} Kunden
</p>
</nav>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Live Search für Kunden
document.addEventListener('DOMContentLoaded', function() {
const searchForm = document.getElementById('customerSearchForm');
const searchInput = document.getElementById('search');
// Debounce timer für Suchfeld
let searchTimeout;
// Live-Suche mit 300ms Verzögerung
searchInput.addEventListener('input', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
searchForm.submit();
}, 300);
});
});
</script>
{% endblock %}