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

@ -33,7 +33,7 @@ window.Page_wiki = (() => {
{
titel: 'Vergiftungen — Sofortmaßnahmen',
icon: 'skull',
text: 'Verdacht auf Vergiftung: Sofort Tierarzt oder Tiergiftzentrale (Berlin: 030 19240, München: 089 19240).\n\nNICHT versuchen den Hund zum Erbrechen zu bringen ohne tierärztlichen Rat.\n\nHäufige Giftquellen: Schokolade, Trauben/Rosinen, Zwiebeln, Xylitol (Süßungsmittel), Ibuprofen.',
text: 'Verdacht auf Vergiftung: Sofort Tierarzt oder Tiergiftzentrale (Berlin: 030 19240, München: 089 19240, Wien: 01 4064343).\n\nNICHT versuchen den Hund zum Erbrechen zu bringen ohne tierärztlichen Rat.\n\nHäufige Giftquellen: Schokolade, Trauben/Rosinen, Zwiebeln, Xylitol (Süßungsmittel), Ibuprofen.',
},
{
titel: 'Hitzschlag',
@ -97,12 +97,12 @@ window.Page_wiki = (() => {
const isMod = _appState.user && (_appState.user.is_moderator || _appState.user.rolle === 'admin');
_container.innerHTML = `
<div class="wiki-tab-bar" id="wiki-tab-bar">
<button class="wiki-tab-btn${_tab === 'rassen' ? ' active' : ''}" data-tab="rassen">${UI.icon('dog')} Rassen</button>
<button class="wiki-tab-btn${_tab === 'gesundheit'? ' active' : ''}" data-tab="gesundheit">${UI.icon('syringe')} Gesundheit</button>
<button class="wiki-tab-btn${_tab === 'recht' ? ' active' : ''}" data-tab="recht">${UI.icon('handshake')} Recht</button>
<button class="wiki-tab-btn${_tab === 'quiz' ? ' active' : ''}" data-tab="quiz">${UI.icon('star')} Quiz</button>
${isMod ? `<button class="wiki-tab-btn${_tab === 'fotos' ? ' active' : ''}" data-tab="fotos" id="wiki-fotos-tab">${UI.icon('camera')} Fotos <span id="wiki-fotos-badge" style="display:none" class="badge badge-sm">0</span></button>` : ''}
<div class="by-tabs" id="wiki-tab-bar">
<button class="by-tab${_tab === 'rassen' ? ' active' : ''}" data-tab="rassen">${UI.icon('dog')} Rassen</button>
<button class="by-tab${_tab === 'gesundheit'? ' active' : ''}" data-tab="gesundheit">${UI.icon('syringe')} Gesundheit</button>
<button class="by-tab${_tab === 'recht' ? ' active' : ''}" data-tab="recht">${UI.icon('handshake')} Recht</button>
<button class="by-tab${_tab === 'quiz' ? ' active' : ''}" data-tab="quiz">${UI.icon('star')} Quiz</button>
${isMod ? `<button class="by-tab${_tab === 'fotos' ? ' active' : ''}" data-tab="fotos" id="wiki-fotos-tab">${UI.icon('camera')} Fotos <span id="wiki-fotos-badge" style="display:none" class="badge badge-sm">0</span></button>` : ''}
</div>
<div id="wiki-content"></div>
`;
@ -111,7 +111,7 @@ window.Page_wiki = (() => {
const btn = e.target.closest('[data-tab]');
if (!btn) return;
_tab = btn.dataset.tab;
_container.querySelectorAll('.wiki-tab-btn').forEach(b => b.classList.toggle('active', b.dataset.tab === _tab));
_container.querySelectorAll('.by-tab').forEach(b => b.classList.toggle('active', b.dataset.tab === _tab));
_renderTab();
});
@ -174,7 +174,7 @@ window.Page_wiki = (() => {
? `<div style="font-size:var(--text-xs);color:var(--c-text-secondary);margin-top:4px">
Aktuelles Foto: <img src="${_esc(s.aktuell_foto)}" style="height:20px;vertical-align:middle;border-radius:2px">
</div>`
: `<div style="font-size:var(--text-xs);color:var(--c-warning,#e8a000);margin-top:4px">Kein Foto vorhanden</div>`
: `<div style="font-size:var(--text-xs);color:var(--c-warning);margin-top:4px">Kein Foto vorhanden</div>`
}
</div>
</div>
@ -227,7 +227,7 @@ window.Page_wiki = (() => {
try {
stats = await _apiFetch('/api/wiki/stats');
} catch {
el.innerHTML = UI.emptyState({ icon: 'warning', title: 'Fehler', text: 'Wiki konnte nicht geladen werden.' });
el.innerHTML = UI.emptyState({ icon: UI.icon('warning'), title: 'Fehler', text: 'Wiki konnte nicht geladen werden.' });
return;
}
@ -753,7 +753,7 @@ window.Page_wiki = (() => {
try {
data = await _apiFetch(`/api/wiki/quiz/result?${params}`);
} catch {
el.innerHTML = UI.emptyState({ icon: 'warning', title: 'Fehler', text: 'Ergebnis konnte nicht geladen werden.' });
el.innerHTML = UI.emptyState({ icon: UI.icon('warning'), title: 'Fehler', text: 'Ergebnis konnte nicht geladen werden.' });
return;
}
@ -796,7 +796,7 @@ window.Page_wiki = (() => {
el.querySelectorAll('.wiki-quiz-mehr').forEach(btn => {
btn.addEventListener('click', () => {
_tab = 'rassen';
_container.querySelectorAll('.wiki-tab-btn').forEach(b => b.classList.toggle('active', b.dataset.tab === 'rassen'));
_container.querySelectorAll('.by-tab').forEach(b => b.classList.toggle('active', b.dataset.tab === 'rassen'));
_openBreedDetail(btn.dataset.slug);
});
});