/* ============================================================ BAN YARO — Ernährung Tabs: Kalorien-Rechner | Futter-Guide | Giftliste | KI-Berater ============================================================ */ window.Page_ernaehrung = (() => { let _container = null; let _appState = null; let _activeTab = 'rechner'; let _profil = {}; const TABS = [ { key: 'rechner', label: 'Kalorien', icon: '' }, { key: 'guide', label: 'Futter-Guide', icon: '' }, { key: 'gift', label: 'Giftliste', icon: '' }, { key: 'ki', label: 'KI-Berater', icon: '' }, { key: 'vertraeglichkeit', label: 'Verträglichkeit', icon: '' }, ]; // ------------------------------------------------------------------ // Escape helper // ------------------------------------------------------------------ function _esc(s) { if (s == null) return ''; return String(s) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } // ------------------------------------------------------------------ // LIFECYCLE // ------------------------------------------------------------------ async function init(container, appState, params) { _container = container; _appState = appState; if (params?.tab && TABS.some(t => t.key === params.tab)) { _activeTab = params.tab; } await _render(); } async function refresh() { await _render(); } async function onDogChange() { _profil = {}; _activeTab = 'rechner'; // Tab zurücksetzen damit neuer Hund frisch startet await _render(); } // ------------------------------------------------------------------ // RENDER // ------------------------------------------------------------------ async function _render() { if (!_appState.activeDog) { _container.innerHTML = UI.emptyState({ icon: '', title: 'Noch kein Hund angelegt', text: 'Erstelle zuerst ein Hundeprofil.', action: ``, }); return; } // Profil laden const dog = _appState.activeDog; try { _profil = await API.get(`/dogs/${dog.id}/ernaehrung`); } catch (_) { _profil = {}; } _container.innerHTML = `
${UI.dogChip(_appState)}
`; UI.bindDogChip(_container, _appState); _renderTabBar(); _renderTab(); } // ------------------------------------------------------------------ // TAB-BAR // ------------------------------------------------------------------ function _renderTabBar() { const el = _container.querySelector('#ern-tabs'); if (!el) return; el.innerHTML = TABS.map(t => ` `).join(''); el.querySelectorAll('.by-tab').forEach(btn => { btn.addEventListener('click', () => { _activeTab = btn.dataset.tab; el.querySelectorAll('.by-tab').forEach(b => b.classList.remove('active')); btn.classList.add('active'); _renderTab(); }); }); } function _renderTab() { const el = _container.querySelector('#ern-tab-content'); if (!el) return; switch (_activeTab) { case 'rechner': _renderRechner(el); break; case 'guide': _renderGuide(el); break; case 'gift': _renderGift(el); break; case 'ki': _renderKi(el); break; case 'vertraeglichkeit': _renderVertraeglichkeit(el); break; } } // ------------------------------------------------------------------ // TAB 1: KALORIEN-RECHNER // ------------------------------------------------------------------ function _renderRechner(el) { const dog = _appState.activeDog; // Auto-Werte aus Hundeprofil const gewichtDefault = dog?.gewicht || ''; const alterDefault = dog?.alter || ''; el.innerHTML = `
`; // Aktivität Pills el.querySelectorAll('[data-akt]').forEach(btn => { btn.addEventListener('click', () => { el.querySelectorAll('[data-akt]').forEach(b => b.classList.remove('active')); btn.classList.add('active'); }); }); // Kastriert Pills el.querySelectorAll('[data-kas]').forEach(btn => { btn.addEventListener('click', () => { el.querySelectorAll('[data-kas]').forEach(b => b.classList.remove('active')); btn.classList.add('active'); }); }); el.querySelector('#ern-rechner-btn').addEventListener('click', () => _berechne(el)); } function _berechne(el) { const gewicht = parseFloat(el.querySelector('#ern-gewicht').value); const aktivitaet = el.querySelector('[data-akt].active')?.dataset.akt || 'normal'; const kastriert = el.querySelector('[data-kas].active')?.dataset.kas === 'ja'; if (!gewicht || gewicht < 0.5) { UI.toast.warning('Bitte ein gültiges Gewicht eingeben.'); return; } const rer = 70 * Math.pow(gewicht, 0.75); const faktoren = { gering: { intakt: 1.2, kastriert: 1.0 }, normal: { intakt: 1.6, kastriert: 1.4 }, aktiv: { intakt: 1.8, kastriert: 1.6 }, sport: { intakt: 2.1, kastriert: 1.9 }, }; const kcal = Math.round(rer * faktoren[aktivitaet][kastriert ? 'kastriert' : 'intakt']); // Umrechnung in Futtermengen const trocken = Math.round(kcal / 3.5); // ~350 kcal/100g const nass = Math.round(kcal / 0.85); // ~85 kcal/100g const barf = Math.round(kcal / 1.5); // ~150 kcal/100g const kcalFormatted = kcal.toLocaleString('de-DE'); const resultEl = el.querySelector('#ern-rechner-result'); resultEl.style.display = ''; resultEl.innerHTML = `
ca. ${kcalFormatted} kcal
pro Tag
🌾 Trockenfutter
(~350 kcal/100g)
${trocken} g / Tag
= ${Math.round(trocken/2)} g morgens + ${Math.round(trocken/2)} g abends
🥫 Nassfutter
(~85 kcal/100g)
${nass} g / Tag
= ${Math.round(nass/2)} g morgens + ${Math.round(nass/2)} g abends
🥩 BARF
(~150 kcal/100g)
${barf} g / Tag
= ${Math.round(barf/2)} g morgens + ${Math.round(barf/2)} g abends

Richtwert nach Nationaler Forschungsratsformel (NRC). Immer den Körperzustand beobachten.

`; // Profil-Speichern einblenden und kcal vorbelegen const profilSection = el.querySelector('#ern-profil-speichern'); profilSection.style.display = ''; // kcal für Speichern merken profilSection.dataset.kcal = kcal; el.querySelector('#ern-prof-save-btn').onclick = () => _speichereProfil(el, kcal); } async function _speichereProfil(el, kcal) { const dog = _appState.activeDog; const futter_typ = el.querySelector('#ern-prof-typ').value || null; const marke = el.querySelector('#ern-prof-marke').value.trim() || null; const portionen = parseInt(el.querySelector('#ern-prof-portionen').value) || 2; const notizen = el.querySelector('#ern-prof-notizen').value.trim() || null; const btn = el.querySelector('#ern-prof-save-btn'); await UI.asyncButton(btn, async () => { try { _profil = await API.put(`/dogs/${dog.id}/ernaehrung`, { futter_typ, marke, kcal_tag: kcal, portionen, notizen, }); UI.toast.success('Profil gespeichert.'); } catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); } }); } // ------------------------------------------------------------------ // TAB 2: FUTTER-GUIDE // ------------------------------------------------------------------ function _renderGuide(el) { const cards = [ { id: 'barf', emoji: '🥩', titel: 'BARF (Rohfütterung)', inhalt: `

Zusammensetzung: 70 % Muskelfleisch, 10 % rohe Knochen, 10 % Organe, 10 % Gemüse & Obst

Vorteile: Naturnahste Ernährungsform, glänzendes Fell, weniger Kot, keine Zusatzstoffe

Risiken: Keimbelastung durch rohes Fleisch, Calcium-Phosphor-Balance muss stimmen, zeitaufwändig und teurer

Tipp: Niemals BARF und Trockenfutter in derselben Mahlzeit mischen — unterschiedliche Verdauungszeiten können zu Problemen führen.

`, }, { id: 'nass', emoji: '🥫', titel: 'Nassfutter', inhalt: `

Zusammensetzung: 70–80 % Wasseranteil, meist höherer Fleischanteil als Trockenfutter

Vorteile: Hunde trinken automatisch mehr (gut für die Niere), schmackhafter, gut für wählerische Hunde

Worauf achten: Erste Zutat auf der Liste = Fleisch (nicht „Tierische Nebenerzeugnisse"), kein Zucker, kein Karamell

Zähne: Schlechter für die Zahngesundheit als Trockenfutter — öfter Zähne putzen oder Kauartikel geben.

`, }, { id: 'trocken', emoji: '🌾', titel: 'Trockenfutter', inhalt: `

Zusammensetzung: 6–10 % Wasser, ca. 350–400 kcal/100 g, konzentrierte Nährstoffe

Gute Zutaten: Benanntes Fleisch an erster Stelle (Huhn, Lachs), mind. 40 % Tierprotein, kein Getreide als Hauptzutat

Schlechte Zutaten: „Getreide" als erste Zutat, Zucker, Karamell, Konservierungsstoffe E320 / E321

Wichtig: Immer frisches Wasser bereitstellen — Trockenfutter enthält kaum Feuchtigkeit.

`, }, ]; el.innerHTML = `

Klicke auf eine Karte für Details.

${cards.map(c => `
${c.emoji} ${c.titel}
`).join('')}
`; el.querySelectorAll('.ern-guide-card').forEach(card => { card.querySelector('.ern-guide-head').addEventListener('click', () => { const body = card.querySelector('.ern-guide-body'); const chevron = card.querySelector('.ern-guide-chevron'); const open = body.style.display !== 'none'; body.style.display = open ? 'none' : ''; chevron.style.transform = open ? '' : 'rotate(180deg)'; }); }); } // ------------------------------------------------------------------ // TAB 3: GIFTLISTE // ------------------------------------------------------------------ function _renderGift(el) { const items = [ { emoji: '🍫', name: 'Schokolade', grund: 'Theobromin → Herzrasen, Krämpfe, kann tödlich sein' }, { emoji: '🍇', name: 'Trauben & Rosinen', grund: 'Nierenversagen — auch kleinste Mengen gefährlich' }, { emoji: '🧅', name: 'Zwiebeln & Knoblauch', grund: 'Zerstören rote Blutkörperchen → Anämie' }, { emoji: '🥑', name: 'Avocado', grund: 'Persin → Erbrechen, Durchfall, Atemnot' }, { emoji: '🌰', name: 'Macadamia-Nüsse', grund: 'Lähmungserscheinungen, Zittern, Erbrechen' }, { emoji: '🍬', name: 'Xylitol (Süßstoff)', grund: 'Schwere Leberschäden, Unterzucker — oft in Kaugummi' }, { emoji: '🥛', name: 'Milch & Milchprodukte', grund: 'Laktose-Intoleranz bei vielen Hunden → Durchfall' }, { emoji: '🦴', name: 'Gekochte Knochen', grund: 'Splitter → innere Verletzungen, Darmverschluss' }, { emoji: '☕', name: 'Koffein (Kaffee, Tee)', grund: 'Herzrasen, Zittern, Nervensystem' }, { emoji: '🧂', name: 'Salz', grund: 'Natriumvergiftung → Erbrechen, Krämpfe' }, ]; el.innerHTML = `
⚠️ Notfall-Tierarzt: Bei Verdacht auf Vergiftung sofort zum Tierarzt. Nicht abwarten, auch wenn noch keine Symptome sichtbar sind.
${items.map(item => `
${item.emoji}
${_esc(item.name)}
${_esc(item.grund)}
`).join('')}

Diese Liste ist nicht vollständig. Im Zweifel gilt: lieber weglassen.

`; } // ------------------------------------------------------------------ // TAB 4: KI-FUTTERBERATER // ------------------------------------------------------------------ function _renderKi(el) { const dog = _appState.activeDog; el.innerHTML = `
Der KI-Futterberater beantwortet Ernährungsfragen für ${_esc(dog?.name || 'deinen Hund')}. Bei Gesundheitsfragen immer den Tierarzt zurate ziehen.
${[ 'Welches Futter empfiehlst du für meine Rasse?', 'Wie oft soll ich meinen Hund füttern?', 'Ist Getreide im Futter schlecht?', 'Welche Leckerlis sind gesund?', ].map(q => ` `).join('')}
`; // Vorschläge el.querySelectorAll('.ern-ki-vorschlag').forEach(btn => { btn.addEventListener('click', () => { el.querySelector('#ern-ki-frage').value = btn.dataset.q; el.querySelector('#ern-ki-frage').focus(); }); }); // Senden el.querySelector('#ern-ki-send-btn').addEventListener('click', () => _kiSenden(el)); el.querySelector('#ern-ki-frage').addEventListener('keydown', e => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) _kiSenden(el); }); } async function _kiSenden(el) { const dog = _appState.activeDog; const frageEl = el.querySelector('#ern-ki-frage'); const frage = frageEl.value.trim(); if (!frage) { UI.toast.warning('Bitte eine Frage eingeben.'); return; } const chatEl = el.querySelector('#ern-ki-chat'); const sendBtn = el.querySelector('#ern-ki-send-btn'); // Userfrage anzeigen chatEl.insertAdjacentHTML('beforeend', `
${_esc(frage)}
`); frageEl.value = ''; // KI-Antwort Placeholder const placeholderId = `ern-ki-placeholder-${Date.now()}`; chatEl.insertAdjacentHTML('beforeend', `
Denke nach…
`); chatEl.scrollTop = chatEl.scrollHeight; await UI.asyncButton(sendBtn, async () => { let antwort = ''; try { const result = await API.post(`/dogs/${dog.id}/ernaehrung/ki-beratung`, { frage, dog_name: dog?.name || null, rasse: dog?.rasse || null, alter: dog?.alter != null ? String(dog.alter) : null, gewicht: dog?.gewicht || null, aktiv: false, }); antwort = result.antwort || 'Keine Antwort erhalten.'; } catch (err) { if (err.status === 503) { antwort = 'Die KI ist momentan nicht verfügbar. Bitte später versuchen.'; } else { antwort = 'Fehler bei der KI-Anfrage. Bitte später erneut versuchen.'; } } const antwortHtml = _esc(antwort) .replace(/\n\n/g, '

') .replace(/\n/g, '
'); const placeholder = document.getElementById(placeholderId); if (placeholder) { placeholder.innerHTML = `

${antwortHtml}

`; } chatEl.scrollTop = chatEl.scrollHeight; }); } // ------------------------------------------------------------------ // TAB 5: VERTRÄGLICHKEIT // ------------------------------------------------------------------ async function _renderVertraeglichkeit(el) { const dog = _appState?.activeDog; if (!dog) { el.innerHTML = ''; return; } el.innerHTML = `
Haut- & Fellsymptome zeigen sich erst nach Wochen — trage regelmäßig ein um Muster zu erkennen.
Lade Analyse…
`; el.querySelector('#vert-btn-futter').addEventListener('click', () => _openFutterModal(el, dog)); el.querySelector('#vert-btn-reaktion').addEventListener('click', () => _openReaktionModal(el, dog)); await _loadAnalyse(el, dog); await _loadVerlauf(el, dog); } function _todayStr() { return new Date().toISOString().slice(0, 10); } function _nowTimeStr() { const d = new Date(); return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`; } function _openFutterModal(el, dog) { const id = `fm-${Date.now()}`; const body = `
`; const footer = ` `; UI.modal.open({ title: 'Futter erfassen', body, footer }); // Datalist mit bekannten Futter-Namen füllen API.dogs.futterList(dog.id).then(list => { const dl = document.getElementById('vert-futter-datalist'); if (!dl) return; const names = [...new Set((list || []).map(e => e.futter_name))]; dl.innerHTML = names.map(n => `