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

@ -0,0 +1,389 @@
window.Page_erste_hilfe = (() => {
let _container = null;
let _appState = null;
async function init(container, appState) {
_container = container;
_appState = appState;
_render();
}
function refresh() {}
function onDogChange() {}
// ----------------------------------------------------------------
// DATA
// ----------------------------------------------------------------
const NOTFALLNUMMERN = [
{ label: 'Tiergiftzentrale München', tel: '+4989 19240', display: '+49 89 19240' },
{ label: 'Tiergiftzentrale Berlin', tel: '+4930 19240', display: '+49 30 19240' },
{ label: 'Tiergiftzentrale Wien', tel: '+431 4064343', display: '+43 1 4064343' },
];
const SCHNELL = [
{ notfall: 'Vergiftung / Giftköder', massnahme: 'Ruhig halten, NICHT erbrechen lassen', tierarzt: 'Sofort' },
{ notfall: 'Hitzschlag', massnahme: 'Kühlen, Wasser anbieten', tierarzt: 'Sofort' },
{ notfall: 'Bewusstlosigkeit', massnahme: 'Atemwege frei, Stabile Seitenlage', tierarzt: 'Sofort' },
{ notfall: 'Starke Blutung', massnahme: 'Druckverband anlegen', tierarzt: 'Sofort' },
{ notfall: 'Knochenbruch', massnahme: 'Ruhigstellen, nicht bewegen', tierarzt: 'Sofort' },
{ notfall: 'Zeckenbiss', massnahme: 'Zecke entfernen, Stelle beobachten', tierarzt: 'Bei Entzündung' },
{ notfall: 'Pfotenverletzung', massnahme: 'Reinigen, Verband', tierarzt: 'Bei tiefer Wunde' },
{ notfall: 'Fremdkörper verschluckt', massnahme: 'Beobachten, nicht erbrechen lassen', tierarzt: 'Bei Symptomen' },
{ notfall: 'Bisswunde', massnahme: 'Reinigen, Wunde beurteilen', tierarzt: 'Bei tiefer Wunde' },
{ notfall: 'Epileptischer Anfall', massnahme: 'Nicht festhalten, sichern', tierarzt: 'Nach dem Anfall' },
];
const KATEGORIEN = [
{
id: 'lebensgefahr',
label: 'Lebensbedrohliche Notfälle',
color: 'var(--c-danger, #ef4444)',
icon: 'warning',
eintraege: [
{
titel: 'Vergiftung / Giftköder',
icon: 'skull',
symptome: ['Erbrechen, Durchfall, übermäßiges Speicheln','Zittern, Krämpfe, Muskelzucken','Taumeln, Orientierungslosigkeit','Blasse oder blaue Schleimhäute','Plötzliche Schwäche, Zusammenbruch'],
massnahmen: ['Hund ruhig halten und von der Giftquelle entfernen','NICHT selbst zum Erbrechen bringen — kann die Vergiftung verschlimmern','Giftköder oder Erbrochenes wenn möglich in einem Beutel sichern','Sofort Tierarzt oder Tiergiftzentrale anrufen','Auf dem Weg: Hund warm halten, ruhig sprechen'],
warn: [{ typ: 'danger', text: 'Nie: Erbrechen einleiten ohne Anweisung des Tierarztes' }],
extra: '<p style="margin-top:var(--space-3);font-size:var(--text-sm);color:var(--c-text-secondary)"><strong>Häufige Giftquellen:</strong> Rattengift, Schneckenkorn (Metaldehyd), Ibuprofen, Paracetamol, Schokolade, Weintrauben/Rosinen, Zwiebeln, Xylit (Kaugummi, Erdnussbutter), präparierte Köder.</p>',
},
{
titel: 'Hitzschlag',
icon: 'thermometer-hot',
symptome: ['Starkes, lautes Hecheln','Taumeln, Koordinationsprobleme','Erbrechen','Glasiger Blick, Apathie','Rote oder blasse Schleimhäute','Bewusstlosigkeit'],
massnahmen: ['Sofort in den Schatten / kühlen Raum bringen','Mit lauwarmem (nicht eiskaltem) Wasser kühlen — Pfoten, Leiste, Nacken','Frisches Wasser anbieten — nicht zwingen','Nassen Lappen auf Bauch und Pfoten legen','Sofort zum Tierarzt — auch wenn der Hund sich erholt'],
warn: [{ typ: 'danger', text: 'Nie: Eiswasser oder Eiswürfel — verursacht Schock durch zu schnelle Abkühlung' }],
},
{
titel: 'Bewusstlosigkeit / Herzstillstand',
icon: 'heartbeat',
symptome: ['Hund reagiert nicht auf Ansprechen oder Berühren','Keine sichtbare Atembewegung','Schleimhäute blass oder blau'],
massnahmen: ['Atemwege freihalten: Maul öffnen, Zunge nach vorne, Fremdkörper entfernen','Atmet der Hund? → Stabile Seitenlage, sofort Tierarzt anrufen','Atmet er nicht? → Herz-Lungen-Wiederbelebung beginnen'],
extra: '<div style="margin-top:var(--space-3);padding:var(--space-3);background:var(--c-surface-2);border-radius:var(--radius-md);font-size:var(--text-sm);color:var(--c-text)"><strong>Herzdruckmassage:</strong> Hund auf die rechte Seite, Hände auf breiteste Stelle des Brustkorbs hinter dem Ellenbogen, 100120 Kompressionen/min, ca. 1/3 eindrücken. Bei kleinen Hunden: eine Hand oder zwei Finger.<br><br><strong>Beatmung:</strong> Nach je 30 Kompressionen 2 Atemzüge — Maul schließen, durch die Nase blasen bis der Brustkorb sich hebt.<br><br>Weiterführen bis: Hund selbst atmet, Tierarzt übernimmt oder nach 10 Min. ohne Reaktion.</div>',
},
{
titel: 'Starke Blutung',
icon: 'drop',
symptome: [],
massnahmen: ['Sauberes Tuch fest auf die Wunde drücken','Druck min. 5 Minuten halten — nicht zwischendurch nachschauen','Druckverband anlegen: Watte auf Wunde, fest mit Binde umwickeln','Hund ruhig halten — Bewegung verstärkt die Blutung','Bei arterieller Blutung (spritzend, hellrot): sofort Tierarzt'],
warn: [{ typ: 'danger', text: 'Niemals ein Tourniquet anlegen — außer als letzter Ausweg bei abgetrennter Gliedmaße' }],
},
{
titel: 'Knochenbruch',
icon: 'bone',
symptome: ['Hund belastet Gliedmaße nicht','Sichtbare Fehlstellung','Starke Schmerzen, Schreien bei Berührung','Schwellung, Blutung'],
massnahmen: ['Hund so wenig wie möglich bewegen','Gebrochene Stelle nicht einrenken oder massieren','Improvisierte Schiene nur wenn nötig: gerades Brett mit Tuch fixieren, nicht zu fest','Hund in Decke einwickeln, ruhig transportieren','Sofort Tierarzt'],
},
],
},
{
id: 'haeufig',
label: 'Häufige Notfälle',
color: 'var(--c-warning, #f59e0b)',
icon: 'first-aid',
eintraege: [
{
titel: 'Zeckenbiss',
icon: 'bug',
symptome: [],
massnahmen: ['Zeckenzange oder Zeckenkarte verwenden — kein Öl, kein Klebstoff, kein Feuer','Zecke so nah wie möglich an der Haut fassen','Gerade herausziehen — nicht drehen','Einstichstelle desinfizieren','Datum und Stelle notieren, 4 Wochen beobachten'],
warn: [{ typ: 'warning', text: 'Zum Tierarzt bei: Rötung/Schwellung, Fieber, Apathie, Lahmheit innerhalb von 4 Wochen oder abgebrochenem Zeckenkopf' }],
extra: '<p style="margin-top:var(--space-3);font-size:var(--text-sm);color:var(--c-text-secondary)"><strong>Übertragende Krankheiten (DE):</strong> Borreliose (häufig), FSME (selten), Babesiose (Süddeutschland, zunehmend), Anaplasmose.</p>',
},
{
titel: 'Pfotenverletzung',
icon: 'paw-print',
symptome: [],
massnahmen: ['Pfote vorsichtig mit lauwarmem Wasser reinigen','Sichtbaren Fremdkörper mit Pinzette entfernen','Leichte Verletzung: reinigen, Pfotenschutzspray, beobachten','Tiefer Schnitt: sauberen Verband anlegen, Tierarzt aufsuchen'],
warn: [{ typ: 'warning', text: 'Notverband: Watte auf Wunde, Mullbinde umwickeln (nicht zu fest), mit Kohäsivbinde sichern' }],
extra: '<p style="margin-top:var(--space-3);font-size:var(--text-sm);color:var(--c-text-secondary)"><strong>Zum Tierarzt wenn:</strong> Wunde klafft, Blutung nicht stoppt, tiefer Einstich, oder Hund nach 24 h noch nicht belastet.</p>',
},
{
titel: 'Fremdkörper verschluckt',
icon: 'circle-dashed',
symptome: ['Im Rachen: Würgen, Pfoten ans Maul, Speicheln','Im Magen: Erbrechen, Appetitlosigkeit','Im Darm: Erbrechen, Blähungen, kein Kot, Schmerzen'],
massnahmen: ['Hund beobachten — viele Gegenstände gehen von selbst durch','Nicht zum Erbrechen bringen (außer auf Anweisung des Tierarztes)','Kein Öl oder Futter geben um nachzuschieben','Bei Würgen: Maul öffnen, sichtbaren Gegenstand entfernen — nur wenn gut erreichbar','Bei Atemnot: Heimlich-Manöver anwenden'],
warn: [{ typ: 'warning', text: 'Sofort zum Tierarzt: anhaltend würgen, Atemnot, angespannter Bauch, kein Kot seit 24 h + Unwohlsein' }],
extra: '<p style="margin-top:var(--space-3);font-size:var(--text-sm);color:var(--c-text-secondary)"><strong>Heimlich-Manöver:</strong> Kleiner Hund: auf den Rücken, sanft aber fest auf den Bauch unter dem Brustkorb drücken. Großer Hund: hinter dem Hund stehen, Arme um den Bauch, Hände unter dem Brustkorb zusammenführen, nach oben und innen drücken.</p>',
},
{
titel: 'Bisswunde',
icon: 'dog',
symptome: [],
massnahmen: ['Hund beruhigen — Schmerz macht auch ruhige Hunde aggressiv','Mit lauwarmem Wasser spülen, kein Alkohol direkt in die Wunde','Oberfläche beurteilen — Bisswunden sehen oft klein aus, sind aber tief'],
warn: [{ typ: 'warning', text: 'Bisswunden sind immer tiefer als sie aussehen. Hunde- und Katzenzähne sind lang und dünn.' },{ typ: 'danger', text: 'Sofort zum Tierarzt: Wunde am Hals/Bauch/Brust, Atembeschwerden, starke Blutung, Apathie/Schock, Bisse von fremden Tieren (Tollwut-Risiko)' }],
},
{
titel: 'Epileptischer Anfall',
icon: 'lightning',
symptome: ['Zuckungen, Krämpfe der Gliedmaßen','Bewusstseinsverlust, starrer Blick','Speicheln, Urin- oder Kotabgang','Desorientierung vor und nach dem Anfall'],
massnahmen: ['Ruhe bewahren — Anfälle enden meist von selbst','Hund NICHT festhalten — Verletzungsgefahr','Gefährliche Gegenstände aus dem Weg räumen','Raum abdunkeln, Geräusche minimieren','Zeit messen — dauert länger als 5 Min: Notfalltierarzt'],
warn: [{ typ: 'warning', text: 'Nach dem Anfall: Hund ist oft desorientiert, kann blind wirken — das ist normal (postiktale Phase). Ruhig sprechen, nicht bedrängen.' }],
extra: '<p style="margin-top:var(--space-3);font-size:var(--text-sm);color:var(--c-text-secondary)"><strong>Sofort zum Tierarzt:</strong> erster Anfall überhaupt, Dauer > 5 Min, mehrere Anfälle in 24 h, Hund kommt nach 30 Min nicht zu sich.</p>',
},
{
titel: 'Verbrennung / Verbrühung',
icon: 'fire',
symptome: [],
massnahmen: ['Betroffene Stelle 1015 Min mit kühlem (nicht eiskaltem) Wasser kühlen','Kein Öl, keine Butter, keine Zahncreme — verstärken den Schaden','Leichte Rötung: kühlen, beobachten','Blasenbildung oder offene Wunden: sofort Tierarzt'],
warn: [{ typ: 'warning', text: 'Heißer Asphalt: Handfläche 5 Sek. auf Boden — zu heiß für dich = zu heiß für Pfoten' }],
},
],
},
{
id: 'wissen',
label: 'Nützliches Wissen',
color: '#ca8a04',
icon: 'book-open',
eintraege: [
{
titel: 'Verbotene Medikamente für Hunde',
icon: 'pill',
symptome: [],
massnahmen: [],
extra: `<div style="overflow-x:auto;margin-top:var(--space-2)">
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm)">
<thead><tr style="background:var(--c-surface-2)">
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-weight:var(--weight-semibold)">Medikament</th>
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-weight:var(--weight-semibold)">Wirkung beim Hund</th>
</tr></thead>
<tbody>
<tr><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Ibuprofen</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Magenblutungen, Nierenversagen schon 1 Tablette gefährlich</td></tr>
<tr style="background:var(--c-surface-2)"><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Paracetamol</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Leberschäden, tödlich</td></tr>
<tr><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Aspirin</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Magenblutungen</td></tr>
<tr style="background:var(--c-surface-2)"><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Diclofenac</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Nieren- und Magenprobleme</td></tr>
<tr><td style="padding:var(--space-2) var(--space-3)">Antidepressiva</td><td style="padding:var(--space-2) var(--space-3)">Krämpfe, Herzprobleme</td></tr>
</tbody>
</table></div>`,
},
{
titel: 'Giftige Pflanzen (Auswahl)',
icon: 'plant',
symptome: [],
massnahmen: [],
extra: `<div style="overflow-x:auto;margin-top:var(--space-2)">
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm)">
<thead><tr style="background:var(--c-surface-2)">
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-weight:var(--weight-semibold)">Pflanze</th>
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-weight:var(--weight-semibold)">Giftigkeit</th>
</tr></thead>
<tbody>
${[['Herbstzeitlose','Sehr giftig, alle Teile'],['Goldregen','Sehr giftig, besonders Samen'],['Eibe','Sehr giftig, alle Teile außer rotem Fruchtfleisch'],['Maiglöckchen','Giftig, Herzrhythmusstörungen'],['Stechapfel','Sehr giftig'],['Oleander','Sehr giftig'],['Kirschlorbeer','Giftig, besonders Samen'],['Buchsbaum','Giftig'],['Narzisse / Tulpe','Giftig, besonders Zwiebel'],['Wisteria (Blauregen)','Giftig']].map((r, i) => `<tr${i%2?' style="background:var(--c-surface-2)"':''}><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">${r[0]}</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">${r[1]}</td></tr>`).join('')}
</tbody>
</table></div>`,
},
{
titel: 'Schleimhäute prüfen',
icon: 'stethoscope',
symptome: [],
massnahmen: [],
extra: `<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin-bottom:var(--space-3)">Zahnfleisch anheben, Finger andrücken, loslassen — Farbe muss binnen 2 Sek. zurückkehren (kapilläre Füllungszeit).</p>
<div style="overflow-x:auto">
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm)">
<thead><tr style="background:var(--c-surface-2)">
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-weight:var(--weight-semibold)">Farbe</th>
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-weight:var(--weight-semibold)">Bedeutung</th>
</tr></thead>
<tbody>
<tr><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Rosa, feucht</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border);color:#22c55e;font-weight:var(--weight-semibold)">Normal</td></tr>
<tr style="background:var(--c-surface-2)"><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Blass / weiß</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border);color:var(--c-danger)">Schock, Blutverlust, Vergiftung</td></tr>
<tr><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Blau / grau</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border);color:var(--c-danger);font-weight:var(--weight-semibold)">Sauerstoffmangel NOTFALL</td></tr>
<tr style="background:var(--c-surface-2)"><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Gelb</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border);color:var(--c-warning)">Leberprobleme</td></tr>
<tr><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border)">Ziegelrot</td><td style="padding:var(--space-2) var(--space-3);border-bottom:1px solid var(--c-border);color:var(--c-danger)">Hitzschlag, Vergiftung</td></tr>
<tr style="background:var(--c-surface-2)"><td style="padding:var(--space-2) var(--space-3)">Trocken</td><td style="padding:var(--space-2) var(--space-3);color:var(--c-warning)">Austrocknung</td></tr>
</tbody>
</table></div>`,
},
{
titel: 'Erste-Hilfe-Ausrüstung',
icon: 'backpack',
symptome: [],
massnahmen: ['Mullbinden und Verbandsmull','Kohäsivbinde (haftet selbst, kein Kleber)','Zeckenzange oder Zeckenkarte','Pinzette','Desinfektionsspray (Chlorhexidin)','Pfotenschutzspray','Einmalhandschuhe','Notfalldecke (Rettungsfolie)','Taschenlampe','Tierarzt-Notfallnummer gespeichert'],
},
],
},
];
// ----------------------------------------------------------------
// RENDER
// ----------------------------------------------------------------
function _render() {
_container.innerHTML = `
<div id="eh-wrap" style="padding-bottom:var(--space-8)">
${_renderNotfallbanner()}
${_renderSchnell()}
<div style="margin:var(--space-6) 0 var(--space-3);display:flex;gap:var(--space-2);flex-wrap:wrap" id="eh-tabs">
${KATEGORIEN.map(k => `
<button class="btn eh-tab-btn" data-tab="${k.id}"
style="border:2px solid ${k.color};padding:var(--space-2) var(--space-4);border-radius:var(--radius-pill);font-size:var(--text-sm);font-weight:var(--weight-semibold);color:${k.color};background:transparent;cursor:pointer">
<svg class="ph-icon" aria-hidden="true" style="color:${k.color}"><use href="/icons/phosphor.svg#${k.icon}"></use></svg>
${k.label}
</button>
`).join('')}
</div>
${KATEGORIEN.map(k => `
<div class="eh-tab-panel" id="eh-panel-${k.id}" style="display:none">
${k.eintraege.map((e, i) => _renderEintrag(e, k.id, i, k.color)).join('')}
</div>
`).join('')}
<div style="margin-top:var(--space-6);padding:var(--space-4);background:var(--c-surface-2);border-radius:var(--radius-md);font-size:var(--text-sm);color:var(--c-text-secondary);line-height:1.6">
<svg class="ph-icon" aria-hidden="true" style="color:var(--c-primary)"><use href="/icons/phosphor.svg#info"></use></svg>
Diese Inhalte ersetzen keinen Tierarztbesuch. Im Zweifel immer sofort zum Tierarzt oder den tierärztlichen Notdienst anrufen.
</div>
</div>
`;
_bindTabs();
_bindAccordions();
_activateTab('lebensgefahr');
}
function _renderNotfallbanner() {
const nums = NOTFALLNUMMERN.map(n => `
<a href="tel:${n.tel}"
style="display:flex;align-items:center;gap:var(--space-2);color:#fff;text-decoration:none;font-size:var(--text-sm);padding:var(--space-2) var(--space-3);background:rgba(255,255,255,0.15);border-radius:var(--radius-md)">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg>
<span><strong>${n.label}</strong><br>${n.display}</span>
</a>
`).join('');
return `
<div style="background:var(--c-danger);border-radius:var(--radius-lg);padding:var(--space-4);margin-bottom:var(--space-4)">
<div style="display:flex;align-items:center;gap:var(--space-2);color:#fff;font-weight:var(--weight-bold);font-size:var(--text-base);margin-bottom:var(--space-3)">
<svg class="ph-icon" style="width:20px;height:20px" aria-hidden="true"><use href="/icons/phosphor.svg#siren"></use></svg>
Tiergiftzentralen jetzt anrufen
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-2)">
${nums}
</div>
<p style="margin-top:var(--space-3);font-size:var(--text-xs);color:rgba(255,255,255,0.8)">
Tierärztlicher Notdienst: Über die Tierarztsuche in der Banyaro-Karte
</p>
</div>
`;
}
function _renderSchnell() {
const rows = SCHNELL.map((s, i) => `
<tr style="${i % 2 ? 'background:var(--c-surface-2)' : ''}">
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm);border-bottom:1px solid var(--c-border)">${s.notfall}</td>
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm);border-bottom:1px solid var(--c-border)">${s.massnahme}</td>
<td style="padding:var(--space-2) var(--space-3);font-size:var(--text-sm);border-bottom:1px solid var(--c-border);font-weight:var(--weight-semibold);color:${s.tierarzt === 'Sofort' ? 'var(--c-danger)' : 'var(--c-warning)'}">${s.tierarzt}</td>
</tr>
`).join('');
return `
<div class="card" style="padding:0;overflow:hidden;margin-bottom:var(--space-4)">
<div style="padding:var(--space-3) var(--space-4);background:var(--c-surface-2);font-weight:var(--weight-semibold);font-size:var(--text-sm);display:flex;align-items:center;gap:var(--space-2)">
<svg class="ph-icon" aria-hidden="true" style="color:var(--c-primary)"><use href="/icons/phosphor.svg#list-bullets"></use></svg>
Schnellübersicht: Was tun bei
</div>
<div style="overflow-x:auto">
<table style="width:100%;border-collapse:collapse">
<thead><tr style="background:var(--c-surface-2)">
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-size:var(--text-xs);color:var(--c-text-secondary);font-weight:var(--weight-semibold)">Notfall</th>
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-size:var(--text-xs);color:var(--c-text-secondary);font-weight:var(--weight-semibold)">Sofortmaßnahme</th>
<th style="padding:var(--space-2) var(--space-3);text-align:left;font-size:var(--text-xs);color:var(--c-text-secondary);font-weight:var(--weight-semibold)">Tierarzt</th>
</tr></thead>
<tbody>${rows}</tbody>
</table>
</div>
</div>
`;
}
function _renderEintrag(e, katId, idx, katColor) {
const accId = `eh-acc-${katId}-${idx}`;
const bodyId = `eh-body-${katId}-${idx}`;
const symptomeHtml = e.symptome.length
? `<p style="font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--c-text-secondary);margin:0 0 var(--space-2)">Symptome</p>
<ul style="margin:0 0 var(--space-3);padding-left:var(--space-5);font-size:var(--text-sm);color:var(--c-text);line-height:1.6">
${e.symptome.map(s => `<li>${s}</li>`).join('')}
</ul>`
: '';
const massnahmenHtml = e.massnahmen.length
? `<p style="font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--c-text-secondary);margin:0 0 var(--space-2)">Sofortmaßnahmen</p>
<ol style="margin:0 0 var(--space-3);padding-left:var(--space-5);font-size:var(--text-sm);color:var(--c-text);line-height:1.6">
${e.massnahmen.map(m => `<li>${m}</li>`).join('')}
</ol>`
: '';
const warnHtml = (e.warn || []).map(w => `
<div style="padding:var(--space-2) var(--space-3);border-radius:var(--radius-md);margin-bottom:var(--space-2);font-size:var(--text-sm);line-height:1.5;
background:${w.typ === 'danger' ? 'rgba(239,68,68,0.1)' : 'rgba(245,158,11,0.1)'};
color:${w.typ === 'danger' ? 'var(--c-danger)' : 'var(--c-warning)'};
border-left:3px solid ${w.typ === 'danger' ? 'var(--c-danger)' : 'var(--c-warning)'}">
<svg class="ph-icon" aria-hidden="true" style="vertical-align:middle;margin-right:4px"><use href="/icons/phosphor.svg#${w.typ === 'danger' ? 'prohibit' : 'warning-circle'}"></use></svg>
${w.text}
</div>
`).join('');
return `
<div id="${accId}" style="border-bottom:1px solid var(--c-border)">
<button data-acc-id="${bodyId}" data-acc-arrow="arr-${katId}-${idx}"
style="width:100%;display:flex;align-items:center;justify-content:space-between;padding:var(--space-4);background:none;border:none;cursor:pointer;text-align:left;gap:var(--space-3)"
aria-expanded="false">
<span style="display:flex;align-items:center;gap:var(--space-3)">
<svg class="ph-icon" aria-hidden="true" style="color:${katColor};flex-shrink:0"><use href="/icons/phosphor.svg#${e.icon}"></use></svg>
<strong style="font-size:var(--text-base);color:var(--c-text)">${e.titel}</strong>
</span>
<svg class="ph-icon" id="arr-${katId}-${idx}" aria-hidden="true" style="flex-shrink:0;color:var(--c-text-secondary);transition:transform 0.2s"><use href="/icons/phosphor.svg#caret-down"></use></svg>
</button>
<div id="${bodyId}" hidden style="padding:0 var(--space-4) var(--space-4)">
${symptomeHtml}
${massnahmenHtml}
${warnHtml}
${e.extra || ''}
</div>
</div>
`;
}
// ----------------------------------------------------------------
// EVENTS
// ----------------------------------------------------------------
function _bindTabs() {
_container.querySelectorAll('.eh-tab-btn').forEach(btn => {
btn.addEventListener('click', () => _activateTab(btn.dataset.tab));
});
}
function _activateTab(id) {
_container.querySelectorAll('.eh-tab-btn').forEach(btn => {
const kat = KATEGORIEN.find(k => k.id === btn.dataset.tab);
const active = btn.dataset.tab === id;
btn.style.background = active ? kat.color : 'transparent';
btn.style.color = active ? '#fff' : kat.color;
});
_container.querySelectorAll('.eh-tab-panel').forEach(panel => {
panel.style.display = panel.id === `eh-panel-${id}` ? 'block' : 'none';
});
}
function _bindAccordions() {
_container.querySelectorAll('[data-acc-id]').forEach(btn => {
btn.addEventListener('click', () => {
const bodyId = btn.dataset.accId;
const arrowId = btn.dataset.accArrow;
const body = document.getElementById(bodyId);
const arrow = document.getElementById(arrowId);
if (!body) return;
const open = !body.hidden;
body.hidden = open;
btn.setAttribute('aria-expanded', String(!open));
if (arrow) arrow.style.transform = open ? '' : 'rotate(180deg)';
});
});
}
// ----------------------------------------------------------------
// PUBLIC
// ----------------------------------------------------------------
return { init, refresh, onDogChange };
})();