Dashboard mit Statistiken

Dieser Commit ist enthalten in:
2025-06-07 13:59:32 +02:00
Ursprung 91a193e0fb
Commit 68d6cc346b
8 geänderte Dateien mit 525 neuen und 5 gelöschten Zeilen

Datei anzeigen

@@ -64,9 +64,101 @@ def logout():
session.pop('username', None)
return redirect(url_for('login'))
@app.route("/", methods=["GET", "POST"])
@app.route("/")
@login_required
def dashboard():
conn = get_connection()
cur = conn.cursor()
# Statistiken abrufen
# Gesamtanzahl Kunden
cur.execute("SELECT COUNT(*) FROM customers")
total_customers = cur.fetchone()[0]
# Gesamtanzahl Lizenzen
cur.execute("SELECT COUNT(*) FROM licenses")
total_licenses = cur.fetchone()[0]
# Aktive Lizenzen (nicht abgelaufen und is_active = true)
cur.execute("""
SELECT COUNT(*) FROM licenses
WHERE valid_until >= CURRENT_DATE AND is_active = TRUE
""")
active_licenses = cur.fetchone()[0]
# Abgelaufene Lizenzen
cur.execute("""
SELECT COUNT(*) FROM licenses
WHERE valid_until < CURRENT_DATE
""")
expired_licenses = cur.fetchone()[0]
# Lizenzen die in den nächsten 30 Tagen ablaufen
cur.execute("""
SELECT COUNT(*) FROM licenses
WHERE valid_until >= CURRENT_DATE
AND valid_until < CURRENT_DATE + INTERVAL '30 days'
AND is_active = TRUE
""")
expiring_soon = cur.fetchone()[0]
# Testlizenzen vs Vollversionen
cur.execute("""
SELECT license_type, COUNT(*)
FROM licenses
GROUP BY license_type
""")
license_types = dict(cur.fetchall())
# Letzte 5 erstellten Lizenzen
cur.execute("""
SELECT l.id, l.license_key, c.name, l.valid_until,
CASE
WHEN l.valid_until < CURRENT_DATE THEN 'abgelaufen'
WHEN l.valid_until < CURRENT_DATE + INTERVAL '30 days' THEN 'läuft bald ab'
ELSE 'aktiv'
END as status
FROM licenses l
JOIN customers c ON l.customer_id = c.id
ORDER BY l.id DESC
LIMIT 5
""")
recent_licenses = cur.fetchall()
# Bald ablaufende Lizenzen (nächste 30 Tage)
cur.execute("""
SELECT l.id, l.license_key, c.name, l.valid_until,
l.valid_until - CURRENT_DATE as days_left
FROM licenses l
JOIN customers c ON l.customer_id = c.id
WHERE l.valid_until >= CURRENT_DATE
AND l.valid_until < CURRENT_DATE + INTERVAL '30 days'
AND l.is_active = TRUE
ORDER BY l.valid_until
LIMIT 10
""")
expiring_licenses = cur.fetchall()
cur.close()
conn.close()
stats = {
'total_customers': total_customers,
'total_licenses': total_licenses,
'active_licenses': active_licenses,
'expired_licenses': expired_licenses,
'expiring_soon': expiring_soon,
'full_licenses': license_types.get('full', 0),
'test_licenses': license_types.get('test', 0),
'recent_licenses': recent_licenses,
'expiring_licenses': expiring_licenses
}
return render_template("dashboard.html", stats=stats, username=session.get('username'))
@app.route("/create", methods=["GET", "POST"])
@login_required
def create_license():
if request.method == "POST":
name = request.form["customer_name"]
email = request.form["email"]
@@ -96,7 +188,7 @@ def dashboard():
cur.close()
conn.close()
return redirect("/")
return redirect("/create")
return render_template("index.html", username=session.get('username'))

Datei anzeigen

@@ -20,7 +20,8 @@
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Kundenverwaltung</h2>
<div>
<a href="/" class="btn btn-primary"> Neue Lizenz</a>
<a href="/" class="btn btn-secondary">📊 Dashboard</a>
<a href="/create" class="btn btn-primary"> Neue Lizenz</a>
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
</div>
</div>

Datei anzeigen

@@ -0,0 +1,199 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Dashboard - Admin Panel</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.stat-card {
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.status-aktiv { color: #28a745; }
.status-ablaufend { color: #ffc107; }
.status-abgelaufen { color: #dc3545; }
</style>
</head>
<body class="bg-light">
<nav class="navbar navbar-dark bg-dark">
<div class="container">
<span class="navbar-brand">🎛️ Lizenzverwaltung</span>
<div class="d-flex align-items-center">
<span class="text-white me-3">Angemeldet als: {{ username }}</span>
<a href="/logout" class="btn btn-outline-light btn-sm">Abmelden</a>
</div>
</div>
</nav>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Dashboard</h1>
<div>
<a href="/create" class="btn btn-primary"> Neue Lizenz</a>
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
</div>
</div>
<!-- Statistik-Karten -->
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="card stat-card h-100">
<div class="card-body text-center">
<h5 class="card-title">👥 Kunden</h5>
<h2 class="text-primary">{{ stats.total_customers }}</h2>
<p class="text-muted mb-0">Gesamt</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card h-100">
<div class="card-body text-center">
<h5 class="card-title">📋 Lizenzen</h5>
<h2 class="text-info">{{ stats.total_licenses }}</h2>
<p class="text-muted mb-0">Gesamt</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card h-100">
<div class="card-body text-center">
<h5 class="card-title">✅ Aktiv</h5>
<h2 class="text-success">{{ stats.active_licenses }}</h2>
<p class="text-muted mb-0">Gültige Lizenzen</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card h-100">
<div class="card-body text-center">
<h5 class="card-title">⚠️ Ablaufend</h5>
<h2 class="text-warning">{{ stats.expiring_soon }}</h2>
<p class="text-muted mb-0">Nächste 30 Tage</p>
</div>
</div>
</div>
</div>
<!-- Lizenztypen -->
<div class="row g-3 mb-4">
<div class="col-md-6">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Lizenztypen</h5>
<div class="row">
<div class="col-6 text-center">
<h3 class="text-success">{{ stats.full_licenses }}</h3>
<p class="text-muted">Vollversionen</p>
</div>
<div class="col-6 text-center">
<h3 class="text-warning">{{ stats.test_licenses }}</h3>
<p class="text-muted">Testversionen</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">Lizenzstatus</h5>
<div class="row">
<div class="col-6 text-center">
<h3 class="text-success">{{ stats.active_licenses }}</h3>
<p class="text-muted">Aktiv</p>
</div>
<div class="col-6 text-center">
<h3 class="text-danger">{{ stats.expired_licenses }}</h3>
<p class="text-muted">Abgelaufen</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3">
<!-- Bald ablaufende Lizenzen -->
<div class="col-md-6">
<div class="card">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0">⏰ Bald ablaufende Lizenzen</h5>
</div>
<div class="card-body">
{% if stats.expiring_licenses %}
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Kunde</th>
<th>Lizenz</th>
<th>Tage</th>
</tr>
</thead>
<tbody>
{% for license in stats.expiring_licenses %}
<tr>
<td>{{ license[2] }}</td>
<td><small><code>{{ license[1][:8] }}...</code></small></td>
<td><span class="badge bg-warning">{{ license[4] }} Tage</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">Keine Lizenzen laufen in den nächsten 30 Tagen ab.</p>
{% endif %}
</div>
</div>
</div>
<!-- Letzte Lizenzen -->
<div class="col-md-6">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="mb-0">🆕 Zuletzt erstellte Lizenzen</h5>
</div>
<div class="card-body">
{% if stats.recent_licenses %}
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Kunde</th>
<th>Lizenz</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for license in stats.recent_licenses %}
<tr>
<td>{{ license[2] }}</td>
<td><small><code>{{ license[1][:8] }}...</code></small></td>
<td>
{% if license[4] == 'abgelaufen' %}
<span class="status-abgelaufen">⚠️ Abgelaufen</span>
{% elif license[4] == 'läuft bald ab' %}
<span class="status-ablaufend">⏰ Läuft bald ab</span>
{% else %}
<span class="status-aktiv">✅ Aktiv</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">Noch keine Lizenzen erstellt.</p>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</body>
</html>

Datei anzeigen

@@ -20,6 +20,7 @@
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Neue Lizenz erstellen</h2>
<div>
<a href="/" class="btn btn-secondary">📊 Dashboard</a>
<a href="/licenses" class="btn btn-secondary">📋 Lizenzen</a>
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
</div>

Datei anzeigen

@@ -25,7 +25,8 @@
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Lizenzübersicht</h2>
<div>
<a href="/" class="btn btn-primary"> Neue Lizenz</a>
<a href="/" class="btn btn-secondary">📊 Dashboard</a>
<a href="/create" class="btn btn-primary"> Neue Lizenz</a>
<a href="/customers" class="btn btn-secondary">👥 Kunden</a>
</div>
</div>