/* ============================================================
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: ' ' },
];
// ------------------------------------------------------------------
// 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 = {};
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: `Profil erstellen `,
});
return;
}
// Profil laden
const dog = _appState.activeDog;
try {
_profil = await API.get(`/dogs/${dog.id}/ernaehrung`);
} catch (_) {
_profil = {};
}
_container.innerHTML = `
`;
_renderTabBar();
_renderTab();
}
// ------------------------------------------------------------------
// TAB-BAR
// ------------------------------------------------------------------
function _renderTabBar() {
const el = _container.querySelector('#ern-tabs');
if (!el) return;
el.innerHTML = TABS.map(t => `
${t.icon} ${t.label}
`).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;
}
}
// ------------------------------------------------------------------
// 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
🛋️ Gemütlich
🚶 Normal
🏃 Aktiv
🏅 Sportlich
✂️ Kastriert / Sterilisiert
Nein
Ja
Kalorienbedarf berechnen
`;
// 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}
${c.inhalt}
`).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 => `
${_esc(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', `
`);
frageEl.value = '';
// KI-Antwort Placeholder
const placeholderId = `ern-ki-placeholder-${Date.now()}`;
chatEl.insertAdjacentHTML('beforeend', `
`);
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 = `
`;
}
chatEl.scrollTop = chatEl.scrollHeight;
});
}
// ------------------------------------------------------------------
// PUBLIC API
// ------------------------------------------------------------------
return { init, refresh, onDogChange };
})();