Improve concert details and add usage statistics

This commit is contained in:
kai
2026-08-27 18:50:19 +02:00
parent 74e337aaa9
commit c1306f86f6
4 changed files with 70 additions and 0 deletions
+28
View File
@@ -1461,6 +1461,34 @@ def admin_patches_page(request: Request):
return template.render(user=user, patches=patches) return template.render(user=user, patches=patches)
@app.get("/admin/statistics", response_class=HTMLResponse)
def admin_statistics_page(request: Request):
user = require_admin(request)
if not user:
return HTMLResponse("<h1>Nicht erlaubt</h1>", status_code=403)
with get_db_connection() as connection:
with connection.cursor() as cursor:
queries = {
"users": "SELECT COUNT(*) FROM users",
"events": "SELECT COUNT(*) FROM concerts",
"upcoming": "SELECT COUNT(*) FROM concerts WHERE start_datetime >= NOW()",
"comments": "SELECT COUNT(*) FROM concert_comments",
"attendance": "SELECT COUNT(*) FROM concert_attendance",
"friendships": "SELECT COUNT(*) FROM friendships WHERE status = 'accepted'",
"messages": "SELECT COUNT(*) FROM direct_messages",
}
stats = {}
for key, query in queries.items():
try:
cursor.execute(query)
stats[key] = cursor.fetchone()[0]
except Exception:
connection.rollback()
stats[key] = 0
template = templates.get_template("admin_statistics.html")
return template.render(user=user, stats=stats)
@app.post("/admin/invites", response_class=HTMLResponse) @app.post("/admin/invites", response_class=HTMLResponse)
def create_invite(request: Request): def create_invite(request: Request):
user = require_admin(request) user = require_admin(request)
+35
View File
@@ -463,6 +463,41 @@ h1, .page-title h1 {
} }
.empty-state { background: rgba(16,16,16,.86); border-style: solid; } .empty-state { background: rgba(16,16,16,.86); border-style: solid; }
.stats-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:14px; }
.stat-card { display:flex; flex-direction:column; gap:5px; padding:20px; background:linear-gradient(145deg,#171212,#0d0d0d); border:1px solid var(--border); border-top:3px solid var(--accent); border-radius:12px; }
.stat-card strong { color:#fca5a5; font-size:2.2rem; }
.stat-card span { color:var(--muted); }
/* Konzertdetailansicht: konsistente Spalten statt Browser-Standardlayout */
.detail-topbar { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:24px; }
.back-link { color:#fca5a5; font-weight:700; }
.concert-detail { max-width:900px; margin:0 auto; padding:26px; background:rgba(12,12,12,.92); border:1px solid var(--border); border-radius:16px; box-shadow:0 14px 32px rgba(0,0,0,.4); }
.flyer-container { display:flex; flex-direction:column; align-items:center; margin-bottom:26px; }
.concert-flyer { display:block; width:auto; max-width:100%; max-height:560px; object-fit:contain; border-radius:10px; }
.concert-content h1 { margin:10px 0 20px; font-size:clamp(1.8rem,4vw,2.8rem); text-align:center; }
.concert-info { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; margin:20px 0; }
.info-item { display:flex; gap:10px; align-items:flex-start; padding:14px; background:rgba(5,5,5,.7); border:1px solid var(--border); border-radius:10px; }
.info-icon { font-size:1.4rem; }
.description, .attendance-section, .comments-section { margin-top:26px; padding-top:22px; border-top:1px solid var(--border); }
.attendance-actions { display:flex; flex-wrap:wrap; gap:9px; }
.attendance-actions form { margin:0; }
.attendance-option { padding:10px 14px; border:1px solid #7f1d1d; border-radius:9px; background:#241010; color:var(--text); cursor:pointer; }
.attendance-option.is-selected { background:#991b1b; border-color:#ef4444; }
.attendance-list { margin-top:16px; padding:12px 14px; background:#0b0b0b; border:1px solid var(--border); border-radius:10px; }
.attendance-list summary { cursor:pointer; color:#fca5a5; font-weight:700; }
.attendance-groups { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:18px; margin-top:16px; }
.attendance-groups ul { margin:0; padding-left:20px; }
.comment-form { max-width:none; }
.comment-form textarea { width:100%; min-height:100px; resize:vertical; }
.comment-list { display:grid; gap:12px; margin-top:20px; }
.comment { padding:14px; background:rgba(5,5,5,.7); border:1px solid var(--border); border-radius:10px; }
.comment-meta { display:flex; justify-content:space-between; gap:12px; color:var(--muted); font-size:.9rem; }
.comment-meta strong { color:var(--text); }
.comment p { margin:10px 0 0; white-space:pre-wrap; overflow-wrap:anywhere; }
.ticket-button-container, .detail-actions { display:flex; flex-wrap:wrap; justify-content:center; gap:10px; margin-top:22px; }
.ticket-button, .concert-action { display:inline-flex; padding:11px 16px; border:1px solid #b91c1c; border-radius:9px; background:#7f1d1d; color:#fff; font-weight:700; }
.concert-action-delete { background:#450a0a; cursor:pointer; }
@media (max-width:600px) { .detail-topbar { align-items:flex-start; } .concert-detail { padding:16px; } .comment-meta { flex-direction:column; gap:4px; } }
@media (max-width: 600px) { @media (max-width: 600px) {
.logo { max-width: 150px; } .logo { max-width: 150px; }
+1
View File
@@ -2,4 +2,5 @@
<a href="/admin/users" class="{% if admin_section == 'users' %}active{% endif %}">👥 Benutzer</a> <a href="/admin/users" class="{% if admin_section == 'users' %}active{% endif %}">👥 Benutzer</a>
<a href="/admin/venues" class="{% if admin_section == 'venues' %}active{% endif %}">📍 Veranstaltungsorte</a> <a href="/admin/venues" class="{% if admin_section == 'venues' %}active{% endif %}">📍 Veranstaltungsorte</a>
<a href="/admin/patches" class="{% if admin_section == 'patches' %}active{% endif %}">🧵 Patch-Bilder</a> <a href="/admin/patches" class="{% if admin_section == 'patches' %}active{% endif %}">🧵 Patch-Bilder</a>
<a href="/admin/statistics" class="{% if admin_section == 'statistics' %}active{% endif %}">📊 Nutzungsstatistik</a>
</nav> </nav>
+6
View File
@@ -0,0 +1,6 @@
<!DOCTYPE html><html lang="de"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Nutzungsstatistik · MetalCircle</title><link rel="icon" type="image/png" href="/static/images/metalcircle-circle.png"><link rel="stylesheet" href="/static/css/style.css"></head><body>
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_user_menu.html' %}</div></header>
<main><div class="page-title"><h1>📊 Nutzungsstatistik</h1><p>Aktuelle Gesamtzahlen der Plattform.</p></div>{% set admin_section = 'statistics' %}{% include '_admin_nav.html' %}
<section class="stats-grid">
{% for key, label in [('users','Benutzer'),('events','Veranstaltungen'),('upcoming','Bevorstehende Veranstaltungen'),('comments','Kommentare'),('attendance','Teilnahmen'),('friendships','Freundschaften'),('messages','Nachrichten')] %}<div class="stat-card"><strong>{{ stats[key] }}</strong><span>{{ label }}</span></div>{% endfor %}
</section></main></body></html>