Feature: Trauer-Feature, Futter-Verträglichkeit, Multi-Hund-Fixes, Wetter-Ort (Sprint 47)

- dog-profile.js: Verstorben-Button, Gedenkseite, KI-Abschiedstext
- database.py: futter_eintraege/reaktionen, route_dogs, exercise_progress.dog_id
- routes/ernaehrung.py: Futter-Verträglichkeit mit 20 Reaktionstypen + Analyse
- routes/routen.py: route_dogs Many-to-Many, Routen editierbar
- routes/training.py: exercise_progress per dog_id
- routes/ki.py: /ki/abschied Trauer-KI
- weather.py: Nominatim Ortsname parallel geladen
- ui.js: dogChip/bindDogChip, visualViewport-Modal
- api.js: gedenken, gedenkseite, futter-Methoden, route_dogs
- worlds.js: Ortsname im Wetter-Chip
- uebungen.js: _progressLoaded-Flag, dog-spezifischer Fortschritt
- trainingsplaene.js: dog_id Unterstützung
- diary.js/health.js: P-Badge Cleanup
- map.js: Wetter-Ort-Anzeige entfernt
- wetter.js: Ort in Wetter-Detail
This commit is contained in:
rene 2026-05-11 19:28:38 +02:00
parent 1ce802c8dc
commit bda61a0e40
16 changed files with 713 additions and 181 deletions

View file

@ -149,12 +149,6 @@ window.Page_diary = (() => {
// ----------------------------------------------------------
async function refresh() {
if (!_appState.activeDog) return;
// Mehrere Hunde → Picker zeigen (User kann Hund wählen)
if (_appState.dogs.length > 1) {
_renderDogPicker();
return;
}
// Einzelner Hund → Diary direkt neu laden
_offset = 0;
_entries = [];
_totalStats = null;
@ -194,52 +188,7 @@ window.Page_diary = (() => {
return;
}
if (_appState.dogs.length > 1) {
_renderDogPicker();
} else {
await _renderDiary();
}
}
// ----------------------------------------------------------
// HUNDE-PICKER — Einstiegsseite bei mehreren Hunden
// ----------------------------------------------------------
function _renderDogPicker() {
const activeDogId = _appState.activeDog?.id;
const cards = _appState.dogs.map(dog => {
const isActive = dog.id === activeDogId;
const av = dog.foto_url
? `<img src="${UI.escape(dog.foto_url)}" alt="${UI.escape(dog.name)}">`
: `<span>${UI.icon('dog')}</span>`;
return `
<div class="diary-picker-card${isActive ? ' diary-picker-card--active' : ''}"
data-dog-id="${dog.id}">
<div class="diary-picker-av">${av}</div>
<div class="diary-picker-name">${UI.escape(dog.name)}</div>
${dog.rasse ? `<div class="diary-picker-rasse">${UI.escape(dog.rasse)}</div>` : ''}
</div>`;
}).join('');
_container.innerHTML = `
<div class="diary-picker-wrap">
<p class="diary-picker-hint">Wessen Tagebuch?</p>
<div class="diary-picker-grid">${cards}</div>
</div>`;
_container.querySelectorAll('.diary-picker-card').forEach(el => {
el.addEventListener('click', async () => {
const id = parseInt(el.dataset.dogId);
if (id === _appState.activeDog?.id) {
// Bereits aktiver Hund → direkt Diary laden
_offset = 0; _entries = [];
await _renderDiary();
} else {
App.setActiveDog(id);
// onDogChange() → _renderDiary() via _notifyDogChange()
}
});
});
await _renderDiary();
}
// ----------------------------------------------------------
@ -247,6 +196,7 @@ window.Page_diary = (() => {
// ----------------------------------------------------------
async function _renderDiary() {
_container.innerHTML = `
${UI.dogChip(_appState)}
<div class="by-toolbar diary-toolbar">
<div class="diary-search-wrap" id="diary-search-wrap">
<svg class="ph-icon diary-search-icon" aria-hidden="true"><use href="/icons/phosphor.svg#magnifying-glass"></use></svg>
@ -274,6 +224,7 @@ window.Page_diary = (() => {
</svg>
</button>
`;
UI.bindDogChip(_container, _appState);
_container.querySelector('#diary-milestone-filter')
?.addEventListener('click', async () => {