Sprint 12: UI-Vereinheitlichung + Läufigkeits-Tracker

- by-tabs/by-tab: einheitliche Tab/Pill-Navigation in allen Seiten
- by-section-label, by-toolbar: einheitliche Section-Labels und Toolbars
- Design-Tokens: fehlende --c-amber, --c-primary-soft ergänzt, Fallback-Werte entfernt
- sitting.js: sitting-layout für konsistentes flush-Layout (wie walks)
- Läufigkeits-Tracker: neuer Health-Tab für Hündinnen mit Zyklusvorhersage,
  Timeline vergangener Läufigkeiten, Erinnerungen und auto-berechnetem Nächst-Datum
- emptyState-Bug: icon-Parameter muss SVG sein, nicht Icon-Name (dog/bell/warning gefixt)
- SW-Cache: by-v103, APP_VER: 79
This commit is contained in:
rene 2026-04-16 22:31:33 +02:00
parent 32d630d5a1
commit b58789373c
30 changed files with 4344 additions and 523 deletions

View file

@ -83,17 +83,18 @@ window.Page_forum = (() => {
<h2 class="forum-header-title">Forum</h2>
<div class="forum-header-actions">
${isMod ? `<button class="btn btn-ghost btn-sm" id="forum-mod-btn" title="Moderationsberichte">${UI.icon('warning')}</button>` : ''}
<button class="btn btn-ghost btn-sm" id="forum-rules-btn" title="Regeln & Netiquette" style="color:var(--c-text-muted)">${UI.icon('info')} Regeln</button>
<button class="btn btn-primary btn-sm" id="forum-new-btn">${UI.icon('plus')} Neues Thema</button>
</div>
</div>
<!-- Kategorie-Tabs -->
<div class="forum-category-tabs" id="forum-tabs">
<div class="forum-category-tabs by-tabs" id="forum-tabs">
${KATEGORIEN.map(k => `
<button class="forum-tab ${k.key === _aktivKat ? 'active' : ''}"
<button class="by-tab ${k.key === _aktivKat ? 'active' : ''}"
data-kat="${k.key}">${_esc(k.label)}</button>
`).join('')}
<button class="forum-tab ${_activeSection === 'map' ? 'active' : ''}"
<button class="by-tab ${_activeSection === 'map' ? 'active' : ''}"
data-section="map">${UI.icon('users')} Mitgliederkarte</button>
</div>
@ -119,7 +120,7 @@ window.Page_forum = (() => {
if (btn.dataset.section === 'map') {
_aktivKat = 'alle';
_activeSection = 'map';
document.querySelectorAll('.forum-tab').forEach(b => b.classList.remove('active'));
document.querySelectorAll('#forum-tabs .by-tab').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
_renderMembersMap();
return;
@ -127,7 +128,7 @@ window.Page_forum = (() => {
_aktivKat = btn.dataset.kat;
_activeSection = 'list';
document.querySelectorAll('.forum-tab').forEach(b => b.classList.remove('active'));
document.querySelectorAll('#forum-tabs .by-tab').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
_offset = 0;
_threads = [];
@ -154,6 +155,9 @@ window.Page_forum = (() => {
// Moderations-Panel
document.getElementById('forum-mod-btn')?.addEventListener('click', _showModPanel);
// Regeln & Netiquette
document.getElementById('forum-rules-btn').addEventListener('click', _showRules);
}
// ----------------------------------------------------------
@ -657,6 +661,78 @@ window.Page_forum = (() => {
// ----------------------------------------------------------
// Neues Thema
// ----------------------------------------------------------
// Regeln & Netiquette
// ----------------------------------------------------------
function _showRules() {
UI.modal.open({
title: `${UI.icon('info')} Regeln & Netiquette`,
body: `
<div style="display:flex;flex-direction:column;gap:var(--space-4)">
<div>
<p style="color:var(--c-text-secondary);font-size:var(--text-sm);margin-bottom:var(--space-3)">
Das Ban-Yaro-Forum ist ein Ort für Hundehalter freundlich, hilfsbereit und respektvoll.
Bitte halte diese Grundregeln ein, damit es für alle schön bleibt.
</p>
</div>
<div>
<div style="font-weight:var(--weight-semibold);margin-bottom:var(--space-2);display:flex;align-items:center;gap:var(--space-2)">
${UI.icon('chat-circle-dots')} Ton & Umgang
</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-1);font-size:var(--text-sm);color:var(--c-text-secondary)">
<li>${UI.icon('check')} Freundlich und respektvoll bleiben auch bei verschiedenen Meinungen</li>
<li>${UI.icon('check')} Konstruktive Kritik statt persönliche Angriffe</li>
<li>${UI.icon('check')} Andere Haltungsmethoden tolerieren solange der Hund nicht leidet</li>
<li>${UI.icon('prohibit')} Keine Beleidigungen, Drohungen oder Diskriminierung</li>
</ul>
</div>
<div>
<div style="font-weight:var(--weight-semibold);margin-bottom:var(--space-2);display:flex;align-items:center;gap:var(--space-2)">
${UI.icon('files')} Inhalte
</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-1);font-size:var(--text-sm);color:var(--c-text-secondary)">
<li>${UI.icon('check')} Thema passend zur gewählten Kategorie</li>
<li>${UI.icon('check')} Aussagekräftiger Titel kein "Hilfe!!!" ohne Kontext</li>
<li>${UI.icon('prohibit')} Keine Werbung, Spam oder Affiliate-Links</li>
<li>${UI.icon('prohibit')} Keine Doppelposts bestehenden Thread suchen bevor du neu erstellst</li>
</ul>
</div>
<div>
<div style="font-weight:var(--weight-semibold);margin-bottom:var(--space-2);display:flex;align-items:center;gap:var(--space-2)">
${UI.icon('syringe')} Gesundheit & Notfälle
</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-1);font-size:var(--text-sm);color:var(--c-text-secondary)">
<li>${UI.icon('check')} Erfahrungen teilen ist wertvoll bitte immer den Tierarzt empfehlen</li>
<li>${UI.icon('prohibit')} Keine Diagnosen oder Medikamentendosierungen für fremde Hunde</li>
<li>${UI.icon('warning-circle')} Bei Notfällen: direkt zum Tierarzt nicht erst im Forum fragen</li>
</ul>
</div>
<div>
<div style="font-weight:var(--weight-semibold);margin-bottom:var(--space-2);display:flex;align-items:center;gap:var(--space-2)">
${UI.icon('flag')} Moderation
</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-1);font-size:var(--text-sm);color:var(--c-text-secondary)">
<li>${UI.icon('check')} Regelverstoß? Melde-Funktion nutzen statt selbst zu reagieren</li>
<li>${UI.icon('check')} Moderatoren können Beiträge bearbeiten, verstecken oder löschen</li>
<li>${UI.icon('check')} Bei Unklarheiten freundlich nachfragen</li>
</ul>
</div>
<p style="font-size:var(--text-xs);color:var(--c-text-muted);border-top:1px solid var(--c-border-light);padding-top:var(--space-3);margin:0">
Wer wiederholt gegen die Regeln verstößt, kann vorübergehend gesperrt werden.
Das Ziel ist ein freundliches Forum nicht Kontrolle um der Kontrolle willen. 🐾
</p>
</div>`,
footer: `<button class="btn btn-primary flex-1" onclick="UI.modal.close()">Verstanden</button>`,
});
}
// ----------------------------------------------------------
function _showCreateForm() {
const katOptions = KATEGORIEN.filter(k => k.key !== 'alle').map(k =>
@ -687,7 +763,12 @@ window.Page_forum = (() => {
</div>
<div id="forum-thread-previews" class="forum-upload-previews" style="margin-top:var(--space-2)"></div>
</div>
</form>`;
</form>
<p style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:var(--space-3)">
${UI.icon('info')} Bitte die
<button type="button" style="background:none;border:none;padding:0;color:var(--c-primary);cursor:pointer;font-size:inherit;text-decoration:underline" id="ff-rules-link">Regeln & Netiquette</button>
beachten.
</p>`;
const footer = `
<button type="button" class="btn btn-secondary flex-1" id="ff-cancel">Abbrechen</button>
@ -696,6 +777,7 @@ window.Page_forum = (() => {
UI.modal.open({ title: '+ Neues Thema', body, footer });
document.getElementById('ff-cancel')?.addEventListener('click', UI.modal.close);
document.getElementById('ff-rules-link')?.addEventListener('click', _showRules);
// Foto-Vorschau
document.getElementById('forum-thread-files')?.addEventListener('change', e => {