Compact chat view and unify header controls

This commit is contained in:
2026-09-15 12:07:00 +02:00
parent 59e9299883
commit 50bf79965f
23 changed files with 78 additions and 52 deletions
+4
View File
@@ -0,0 +1,4 @@
<div class="header-controls">
{% include '_language_switch.html' %}
{% include '_user_menu.html' %}
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
<nav class="language-switch" aria-label="{{ _('Sprache') }}">
{% set target_language = 'en' if language() == 'de' else 'de' %}
<a class="active" href="{{ language_url(target_language) }}" lang="{{ target_language }}" hreflang="{{ target_language }}" aria-label="{{ _('Zu Englisch wechseln') if target_language == 'en' else _('Zu Deutsch wechseln') }}" title="{{ _('Zu Englisch wechseln') if target_language == 'en' else _('Zu Deutsch wechseln') }}">{{ target_language | upper }}</a>
<a class="active" href="{{ language_url(target_language) }}" lang="{{ target_language }}" hreflang="{{ target_language }}" aria-label="{{ _('Zu Englisch wechseln') if target_language == 'en' else _('Zu Deutsch wechseln') }}" title="{{ _('Zu Englisch wechseln') if target_language == 'en' else _('Zu Deutsch wechseln') }}">{{ '🇬🇧' if target_language == 'en' else '🇩🇪' }}</a>
</nav>
+1 -1
View File
@@ -1,6 +1,6 @@
<!DOCTYPE html><html lang="{{ language() }}"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{{ _('Account-Link · MetalCircle') }}</title><link rel="icon" type="image/png" href="/static/images/metalcircle-circle.png"><link rel="stylesheet" href="/static/css/style.css"><link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.html' %}</div>
</header>
<main><div class="page-title"><h1>{{ _('Passwort zurücksetzen') }}</h1><p>{{ _('Reset-Link für @') }}{{ target_username }} · {{ target_email }}</p></div>
<section class="admin-section"><p>{{ _('Diesen persönlichen Link sicher an den User senden. Er ist zwei Stunden gültig und nur einmal verwendbar.') }}</p><label class="invite-link" style="display:block;overflow-wrap:anywhere">{{ account_url }}</label><p><a class="button button-secondary" href="/admin/users">{{ _('Zurück') }}</a></p></section></main></body></html>
+1 -1
View File
@@ -21,7 +21,7 @@
<link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.html' %}</div>
</header>
<main>
<div class="page-title"><h1>{{ _('🧵 Patch-Bilder') }}</h1><p>{{ _('Grafiken für die Patches auf der virtuellen Kutte verwalten.') }}</p></div>
+1 -1
View File
@@ -1,6 +1,6 @@
<!DOCTYPE html><html lang="{{ language() }}"><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"><link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.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">
+1 -1
View File
@@ -19,7 +19,7 @@
<link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.html' %}</div>
</header>
<main>
<div class="page-title"><h1>{{ _('⚙️ Verwaltung') }}</h1><p>{{ _('Benutzerkonten und Einladungen verwalten.') }}</p></div>
+1 -1
View File
@@ -31,7 +31,7 @@
<link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.html' %}</div>
</header>
<main>
<div class="page-title"><h1>{{ _('📍 Veranstaltungsorte') }}</h1><p>{{ _('Orte korrigieren, bestätigen, zusammenführen oder entfernen.') }}</p></div>
+2 -2
View File
@@ -11,8 +11,8 @@
<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>{% include '_language_switch.html' %}</header>
{% include '_header_controls.html' %}
</div></header>
<main>
<div class="page-title"><h1>{{ _('🐛 Bug melden') }}</h1><p>{{ _('Hilf uns, MetalCircle zu verbessern.') }}</p></div>
<section class="admin-section bug-report-section">
+1 -2
View File
@@ -44,8 +44,7 @@
<div class="detail-topbar">
<a href="/" class="back-link">{{ _('← Zurück zum Kalender') }}</a>
{% include '_language_switch.html' %}
{% include '_user_menu.html' %}
{% include '_header_controls.html' %}
</div>
<article class="concert-detail">
+1 -1
View File
@@ -3,7 +3,7 @@
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{ _('Datenschutz · MetalCircle') }}</title><link rel="icon" type="image/png" href="/static/images/metalcircle-circle.png"><link rel="stylesheet" href="/static/css/style.css"><link rel="stylesheet" href="/static/css/language.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></div> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_language_switch.html' %}</div>
</header>
<main>
<div class="page-title"><h1>{{ _('Datenschutz') }}</h1><p>{{ _('Hinweise zur Verarbeitung deiner Daten') }}</p></div>
+1 -1
View File
@@ -11,7 +11,7 @@
<link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.html' %}</div>
</header>
<main>
<div class="page-title"><h1>{{ _('📓 Konzerttagebuch') }}</h1><p>{{ _('Besuchte Konzerte werden automatisch eingetragen. Deine Erinnerungen bleiben privat.') }}</p></div>
+1 -2
View File
@@ -12,9 +12,8 @@
<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' %}
{% include '_header_controls.html' %}
</div>
{% include '_language_switch.html' %}
</header>
<main>
<div class="page-title">
+1 -1
View File
@@ -1,7 +1,7 @@
<!DOCTYPE html><html lang="{{ language() }}"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{{ _('Gefolgt · MetalCircle') }}</title><link rel="stylesheet" href="/static/css/style.css"><style>
.follow-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px}.follow-card{padding:18px;background:var(--surface);border:1px solid var(--border);border-radius:12px}.follow-list{display:flex;flex-wrap:wrap;gap:8px;padding:0;list-style:none}.follow-list li{display:flex;align-items:center;gap:6px;padding:7px 10px;background:#171212;border:1px solid var(--border);border-radius:999px}.follow-list form{margin:0}.follow-remove{padding:0;color:#fca5a5;background:none;border:0;cursor:pointer;font-size:1rem}.follow-events{display:grid;gap:10px}.follow-event{display:block;padding:14px;background:#0b0b0b;border:1px solid var(--border);border-radius:10px}.follow-event:hover{border-color:#ef4444}.follow-reason{color:var(--muted);font-size:.82rem}
</style><link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
</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 '_header_controls.html' %}</div>
</header><main>
<div class="page-title"><h1>{{ _('⭐ Gefolgt') }}</h1><p>{{ _('Bands, Locations, Personen und passende kommende Veranstaltungen.') }}</p></div>
<div class="follow-grid"><section class="follow-card"><h2>Bands</h2><ul class="follow-list">{% for band in followed_bands %}<li>{{ band.name }}<form method="post" action="/following/bands/remove"><input type="hidden" name="band_key" value="{{ band.key }}"><button class="follow-remove" type="submit" title="{{ _('Nicht mehr folgen') }}" aria-label="{{ band.name }} {{ _('nicht mehr folgen') }}">×</button></form></li>{% else %}<li>{{ _('Noch keine Band gefolgt.') }}</li>{% endfor %}</ul></section>
+1 -1
View File
@@ -3,7 +3,7 @@
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{ _('Impressum · MetalCircle') }}</title><link rel="icon" type="image/png" href="/static/images/metalcircle-circle.png"><link rel="stylesheet" href="/static/css/style.css"><link rel="stylesheet" href="/static/css/language.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></div> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_language_switch.html' %}</div>
</header>
<main>
<div class="page-title"><h1>{{ _('Impressum') }}</h1><p>{{ _('Anbieterkennzeichnung') }}</p></div>
+1 -2
View File
@@ -257,8 +257,7 @@
{{ _('+ Veranstaltung hinzufügen') }}
</a>
{% include '_language_switch.html' %}
{% include '_user_menu.html' %}
{% include '_header_controls.html' %}
</div>
+1 -1
View File
@@ -12,8 +12,8 @@
<header>
<div class="header-inner">
<a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>
{% include '_language_switch.html' %}
</div>
{% include '_language_switch.html' %}
</header>
<main>
<div class="page-title">
+29 -11
View File
@@ -15,14 +15,22 @@
.conversation-copy strong, .conversation-copy span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conversation-copy span { margin-top: 3px; color: var(--muted); font-size: .9rem; }
.unread-badge { min-width: 26px; padding: 3px 7px; color: #fff; background: #b91c1c; border-radius: 999px; text-align: center; font-weight: 800; }
.chat-header { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; }
.chat-header h1 { margin: 0; }
.chat-log { display: flex; flex-direction: column; gap: 9px; min-height: 260px; padding: 18px; background: #090909; border: 1px solid var(--border); border-radius: 15px; }
body.chat-page { display: flex; flex-direction: column; width: 100%; height: 100vh; height: 100dvh; min-height: 0; overflow: hidden; }
body.chat-page > header { position: relative; flex: 0 0 auto; }
.chat-main { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; gap: 8px; flex: 1 1 auto; width: 100%; min-height: 0; overflow: hidden; padding: 8px 20px max(8px, env(safe-area-inset-bottom)); }
.chat-header { display: flex; align-items: center; min-width: 0; gap: 9px; margin: 0; }
.chat-header h1 { overflow: hidden; margin: 0; font-size: 1.05rem; text-overflow: ellipsis; white-space: nowrap; }
.chat-partner-copy { min-width: 0; flex: 1 1 auto; }
.chat-partner-copy a { display: block; overflow: hidden; color: var(--muted); font-size: .8rem; text-overflow: ellipsis; white-space: nowrap; }
.chat-header .chat-avatar { width: 38px; height: 38px; flex-basis: 38px; }
.chat-back { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 36px; height: 36px; padding: 0; }
.chat-log { display: flex; flex-direction: column; gap: 9px; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 12px; background: #090909; border: 1px solid var(--border); border-radius: 15px; }
.chat-bubble { align-self: flex-start; max-width: min(76%, 650px); padding: 10px 13px; background: #262020; border-radius: 4px 13px 13px 13px; white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-bubble.mine { align-self: flex-end; background: #5f1515; border-radius: 13px 4px 13px 13px; }
.chat-time { display: block; margin-top: 5px; color: #a8a29e; font-size: .72rem; }
.chat-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; margin-top: 12px; max-width: none; }
.chat-form textarea { min-height: 76px; margin: 0; resize: vertical; }
.chat-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 8px; margin: 0; max-width: none; }
.chat-form textarea { height: 46px; min-height: 46px; max-height: 96px; margin: 0; overflow-y: auto; resize: none; }
.chat-form .button { min-height: 44px; }
.empty-chat { color: var(--muted); text-align: center; margin: auto; }
.message-notifications { margin-bottom: 24px; padding: 16px; background: rgba(69,10,10,.45); border: 1px solid #7f1d1d; border-radius: 14px; }
.message-notifications h2, .conversation-heading { margin: 0 0 12px; font-size: 1.15rem; }
@@ -36,18 +44,25 @@
.notification-actions .button { padding: 7px 10px; font-size: .82rem; }
.notification-link { color: #fca5a5; white-space: nowrap; }
.conversation-heading { margin-top: 20px; }
@media (max-width: 600px) { .chat-form { grid-template-columns: 1fr; } .chat-bubble { max-width: 90%; } }
@media (max-width: 600px) {
.chat-main { padding: 6px 12px max(6px, env(safe-area-inset-bottom)); }
.chat-log { padding: 10px; }
.chat-form { grid-template-columns: minmax(0, 1fr) auto; }
.chat-form textarea { width: 100%; }
.chat-bubble { max-width: 90%; }
}
</style>
<link rel="stylesheet" href="/static/css/language.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> {% include '_language_switch.html' %}
<body class="{% if partner %}chat-page{% endif %}">
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_header_controls.html' %}</div>
</header>
<main>
<main class="{% if partner %}chat-main{% endif %}">
{% if partner %}
<div class="chat-header">
{% if partner.avatar_path %}<img class="chat-avatar" src="{{ partner.avatar_path }}" alt="">{% else %}<span class="chat-avatar">{{ partner.display_name[:1] }}</span>{% endif %}
<div><h1>{{ partner.display_name }}</h1><a href="/users/{{ partner.username }}">@{{ partner.username }}</a></div>
<div class="chat-partner-copy"><h1>{{ partner.display_name }}</h1><a href="/users/{{ partner.username }}">@{{ partner.username }}</a></div>
<a class="button button-secondary chat-back" href="/messages" aria-label="{{ _('← Alle Nachrichten') }}" title="{{ _('← Alle Nachrichten') }}"></a>
</div>
<div class="chat-log">
{% for message in messages %}<div class="chat-bubble{% if message.from_me %} mine{% endif %}"{% if loop.last %} id="latest"{% endif %}>{{ message.body }}<span class="chat-time">{{ message.created_at }}</span></div>
@@ -57,7 +72,6 @@
<textarea name="body" maxlength="2000" required placeholder="{{ _('Nachricht schreiben …') }}" aria-label="{{ _('Nachricht') }}"></textarea>
<button class="button" type="submit">{{ _('Senden') }}</button>
</form>
<p><a class="button button-secondary" href="/messages">{{ _('← Alle Nachrichten') }}</a></p>
{% else %}
<div class="page-title"><h1>{{ _('🤘 Nachrichten') }}</h1><p>{{ _('Private Chats mit deinen Freunden.') }}</p></div>
{% if friend_requests or event_invitations %}
@@ -91,5 +105,9 @@
</div>
{% endif %}
</main>
{% if partner %}<script>
const chatLog = document.querySelector('.chat-log');
if (chatLog) chatLog.scrollTop = chatLog.scrollHeight;
</script>{% endif %}
</body>
</html>
+1 -2
View File
@@ -85,11 +85,10 @@
<img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle">
</a>
{% include '_user_menu.html' %}
{% include '_header_controls.html' %}
</div>
{% include '_language_switch.html' %}
</header>
+1 -1
View File
@@ -1,6 +1,6 @@
<!DOCTYPE html><html lang="{{ language() }}"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{{ _('Passwort zurücksetzen · MetalCircle') }}</title><link rel="icon" type="image/png" href="/static/images/metalcircle-circle.png"><link rel="stylesheet" href="/static/css/style.css"><link rel="stylesheet" href="/static/css/language.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></div> {% include '_language_switch.html' %}
<header><div class="header-inner"><a href="/" class="logo"><img class="brand-logo" src="/static/images/metalcircle-full.png" alt="MetalCircle"></a>{% include '_language_switch.html' %}</div>
</header><main><div class="page-title"><h1>{{ _('Passwort zurücksetzen') }}</h1></div><section class="empty">
{% if error %}<p role="alert">{{ error | t }}</p>{% endif %}<form method="post" action="/password-reset/{{ token }}"><label>{{ _('Neues Passwort') }}<br><input type="password" name="password" minlength="10" required autocomplete="new-password"></label><br><label>{{ _('Passwort wiederholen') }}<br><input type="password" name="password_repeat" minlength="10" required autocomplete="new-password"></label><br><button class="button" type="submit">{{ _('Passwort speichern') }}</button></form>
</section></main></body></html>
+1 -2
View File
@@ -76,9 +76,8 @@
<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' %}
{% include '_header_controls.html' %}
</div>
{% include '_language_switch.html' %}
</header>
<main>
<div class="page-title">
+1 -1
View File
@@ -32,7 +32,7 @@
</head>
<body>
{% include '_language_switch.html' %}
<div class="registration-header">{% include '_language_switch.html' %}</div>
<p><img src="/static/images/metalcircle-full.png" alt="MetalCircle" style="display:block;max-width:100%;height:auto;margin:0 auto 24px"></p>