@@ -1346,9 +1327,8 @@ window.Page_dog_profile = (() => {
rasse_id: fd.rasse_id ? parseInt(fd.rasse_id) : null,
geburtstag: fd.geburtstag || null,
geschlecht: fd.geschlecht || null,
- gewicht_kg: fd.gewicht_kg ? parseFloat(fd.gewicht_kg) : null,
- widerrist_cm: fd.widerrist_cm ? parseFloat(fd.widerrist_cm) : null,
- chip_nr: fd.chip_nr || null,
+ gewicht_kg: fd.gewicht_kg ? parseFloat(fd.gewicht_kg) : null,
+ chip_nr: fd.chip_nr || null,
bio: fd.bio || null,
is_public: 'is_public' in fd,
fell_typ: fd.fell_typ || null,
diff --git a/backend/static/js/pages/health.js b/backend/static/js/pages/health.js
index bf68615..b984515 100644
--- a/backend/static/js/pages/health.js
+++ b/backend/static/js/pages/health.js
@@ -22,8 +22,6 @@ window.Page_health = (() => {
{ key: 'allergie', label: 'Allergien', icon: '
' },
{ key: 'dokument', label: 'Dokumente', icon: '
' },
{ key: 'praxen', label: 'Praxen', icon: '
' },
- { key: 'versicherung', label: 'Versicherung', icon: '
' },
- { key: 'verhalten', label: 'Verhalten', icon: '
' },
];
const LAEUFIGKEIT_TAB = { key: 'laeufigkeit', label: 'Läufigkeit', icon: '
' };
@@ -113,14 +111,12 @@ window.Page_health = (() => {
-
`;
_renderTabBar();
UI.bindDogChip(_container, _appState);
- _loadRemindersBanner();
_container.querySelector('#health-ki-btn')
.addEventListener('click', _showKiSummary);
_container.querySelector('#health-ki-tierarzt-btn')
@@ -336,8 +332,6 @@ window.Page_health = (() => {
case 'allergie': content.innerHTML = _renderAllergien(entries); break;
case 'dokument': content.innerHTML = _renderDokumente(entries); break;
case 'praxen': content.innerHTML = _renderPraxen(); break;
- case 'versicherung': _renderVersicherung(content); break;
- case 'verhalten': _renderVerhalten(content); break;
}
_bindTabEvents(content);
@@ -3056,331 +3050,6 @@ window.Page_health = (() => {
});
}
- // ==============================================================
- // BEVORSTEHENDE ERINNERUNGEN (Banner oben in der Health-Seite)
- // ==============================================================
- async function _loadRemindersBanner() {
- const dog = _appState?.activeDog;
- if (!dog) return;
- const wrap = _container?.querySelector('#health-reminders-banner');
- if (!wrap) return;
- let items;
- try { items = await API.health.reminders(dog.id); }
- catch { return; }
- if (!items.length) { wrap.style.display = 'none'; return; }
-
- const TYPE_LABEL = { impfung: 'Impfung', entwurmung: 'Entwurmung', medikament: 'Medikament' };
- const fmt = d => { try { const p = d.split('-'); return `${parseInt(p[2])}.${parseInt(p[1])}.${p[0]}`; } catch { return d; } };
-
- wrap.style.display = '';
- wrap.innerHTML = items.slice(0, 3).map(r => {
- const overdue = r.ueberfaellig;
- const color = overdue ? 'var(--c-danger,#ef4444)' : r.delta_tage <= 3 ? '#f59e0b' : 'var(--c-primary)';
- const bg = overdue ? 'rgba(239,68,68,0.08)' : r.delta_tage <= 3 ? 'rgba(245,158,11,0.08)' : 'var(--c-primary-subtle)';
- const label = overdue ? `Überfällig seit ${Math.abs(r.delta_tage)} Tag${Math.abs(r.delta_tage)!==1?'en':''}` :
- r.delta_tage === 0 ? 'Heute fällig' :
- `in ${r.delta_tage} Tag${r.delta_tage!==1?'en':''}`;
- return `
-
-
-
- ${_esc(r.bezeichnung)}
- ${TYPE_LABEL[r.typ] || r.typ}
-
-
${label}
-
`;
- }).join('');
- }
-
- // ==============================================================
- // TAB: VERSICHERUNG
- // ==============================================================
- async function _renderVersicherung(content) {
- const dog = _appState?.activeDog;
- if (!dog) return;
- content.innerHTML = `
`;
-
- let policies;
- try { policies = await API.health.insuranceList(dog.id); }
- catch { content.innerHTML = `
Fehler beim Laden.
`; return; }
-
- const _fmtDate = d => { if (!d) return '–'; try { const p=d.split('-'); return `${parseInt(p[2])}.${parseInt(p[1])}.${p[0]}`; } catch { return d; } };
- const _fmtEur = v => v ? `${v.toFixed(2).replace('.',',')} €/Jahr` : '–';
-
- const cardsHtml = policies.length ? policies.map(p => `
-
-
-
-
${_esc(p.anbieter)}
- ${p.police_nr ? `
Police: ${_esc(p.police_nr)}
` : ''}
-
-
-
-
-
-
-
-
Jahresbeitrag
${_fmtEur(p.jahresbeitrag)}
-
Läuft ab
${_fmtDate(p.ablaufdatum)}
- ${p.kontakt ? `
Kontakt
${_esc(p.kontakt)}
` : ''}
- ${p.notizen ? `
Notizen
${_esc(p.notizen)}
` : ''}
-
-
`).join('') : `
-
-
-
Noch keine Versicherung eingetragen.
-
`;
-
- content.innerHTML = `
- ${cardsHtml}
-
-
`;
-
- content.querySelector('#ins-add-btn')?.addEventListener('click', () => _openInsuranceForm(dog, null, () => _renderVersicherung(content)));
- content.querySelectorAll('.ins-edit-btn').forEach(btn => {
- const pol = policies.find(p => p.id === parseInt(btn.dataset.id));
- btn.addEventListener('click', () => _openInsuranceForm(dog, pol, () => _renderVersicherung(content)));
- });
- content.querySelectorAll('.ins-del-btn').forEach(btn => {
- btn.addEventListener('click', async () => {
- if (!window.confirm('Versicherung löschen?')) return;
- await API.health.insuranceDelete(dog.id, parseInt(btn.dataset.id));
- _renderVersicherung(content);
- });
- });
- }
-
- function _openInsuranceForm(dog, existing, onSave) {
- const id = `ins-form-${Date.now()}`;
- const body = `
`;
- const footer = `
-
-
`;
- UI.modal.open({ title: existing ? 'Versicherung bearbeiten' : 'Versicherung eintragen', body, footer });
- setTimeout(() => {
- document.getElementById('ins-save-btn')?.addEventListener('click', async ev => {
- ev.preventDefault();
- const form = document.getElementById(id);
- if (!form) return;
- const fd = new FormData(form);
- const data = {
- anbieter: (fd.get('anbieter')||'').trim(),
- police_nr: fd.get('police_nr')||null,
- jahresbeitrag: fd.get('jahresbeitrag') ? parseFloat(fd.get('jahresbeitrag')) : null,
- ablaufdatum: fd.get('ablaufdatum')||null,
- kontakt: fd.get('kontakt')||null,
- notizen: fd.get('notizen')||null,
- };
- if (!data.anbieter) { UI.toast.warning('Bitte Anbieter angeben.'); return; }
- await UI.asyncButton(document.getElementById('ins-save-btn'), async () => {
- try {
- if (existing) await API.health.insuranceUpdate(dog.id, existing.id, data);
- else await API.health.insuranceCreate(dog.id, data);
- UI.modal.close();
- UI.toast.success('Gespeichert.');
- onSave();
- } catch (err) { UI.toast.error(err.message || 'Fehler.'); }
- });
- });
- }, 50);
- }
-
- // ==============================================================
- // TAB: VERHALTEN
- // ==============================================================
- const _KAT_LABELS = {
- angst: 'Angst / Panik', aggression: 'Aggression', ueberreaktion: 'Überreaktion',
- ressource: 'Ressourcenverteidigung', separation: 'Trennungsangst',
- leine: 'Leinenprobleme', sozial: 'Sozialkompetenz', sonstiges: 'Sonstiges',
- };
- const _KAT_COLORS = {
- angst: '#3b82f6', aggression: '#ef4444', ueberreaktion: '#f59e0b',
- ressource: '#8b5cf6', separation: '#ec4899', leine: '#06b6d4',
- sozial: '#22c55e', sonstiges: '#6b7280',
- };
- const _TRIGGER_LABELS = {
- fremde_hunde: 'Fremde Hunde', fremde_menschen: 'Fremde Menschen', kinder: 'Kinder',
- laerm_feuerwerk: 'Feuerwerk', laerm_gewitter: 'Gewitter', auto_fahrrad: 'Autos/Fahrräder',
- tierarzt: 'Tierarztbesuch', allein_zuhause: 'Allein zuhause',
- andere_tiere: 'Andere Tiere', besucher_zuhause: 'Besucher', sonstiges: 'Sonstiges',
- };
-
- async function _renderVerhalten(content) {
- const dog = _appState?.activeDog;
- if (!dog) return;
- content.innerHTML = `
`;
-
- let resp;
- try { resp = await API.health.behaviorList(dog.id); }
- catch { content.innerHTML = `
Fehler beim Laden.
`; return; }
-
- const entries = resp.entries || [];
- const fmtDate = d => { try { const p=d.split('-'); return `${parseInt(p[2])}.${parseInt(p[1])}.${p[0]}`; } catch { return d; } };
-
- const listHtml = entries.length ? entries.map(e => {
- const color = _KAT_COLORS[e.kategorie] || '#6b7280';
- const katLabel = _KAT_LABELS[e.kategorie] || e.kategorie;
- const trigLabel = _TRIGGER_LABELS[e.trigger] || e.trigger || '';
- const dots = Array.from({length: 5}, (_,i) =>
- `
`
- ).join('');
- return `
-
-
-
-
- ${_esc(katLabel)}
- ${trigLabel ? `${_esc(trigLabel)}` : ''}
- ${fmtDate(e.datum)}${e.uhrzeit ? ' ' + e.uhrzeit : ''}
-
-
${dots}
- ${e.notiz ? `
${_esc(e.notiz)}
` : ''}
-
-
-
`;
- }).join('') : `
-
-
-
Noch keine Einträge. Protokolliere auffälliges Verhalten um Muster zu erkennen.
-
`;
-
- content.innerHTML = `
- ${listHtml}
-
-
`;
-
- content.querySelector('#beh-add-btn')?.addEventListener('click', () => _openBehaviorForm(dog, () => _renderVerhalten(content)));
- content.querySelectorAll('.beh-del-btn').forEach(btn => {
- btn.addEventListener('click', async () => {
- if (!window.confirm('Eintrag löschen?')) return;
- await API.health.behaviorDelete(dog.id, parseInt(btn.dataset.id));
- _renderVerhalten(content);
- });
- });
- }
-
- function _openBehaviorForm(dog, onSave) {
- const id = `beh-form-${Date.now()}`;
- const today = new Date().toISOString().slice(0, 10);
- const nowTime = (() => { const d=new Date(); return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; })();
- const body = `
`;
- const footer = `
-
-
`;
- UI.modal.open({ title: 'Verhalten erfassen', body, footer });
- setTimeout(() => {
- document.querySelectorAll('.beh-int-btn').forEach(btn => {
- btn.addEventListener('click', () => {
- const val = parseInt(btn.dataset.val);
- document.querySelectorAll('.beh-int-btn').forEach((b,i) => {
- b.style.background = i < val ? 'var(--c-primary)' : 'var(--c-bg-card)';
- b.style.color = i < val ? '#fff' : 'var(--c-text-secondary)';
- });
- const hi = document.querySelector('[name="intensitaet"]');
- if (hi) hi.value = val;
- });
- });
- document.getElementById('beh-save-btn')?.addEventListener('click', async ev => {
- ev.preventDefault();
- const form = document.getElementById(id);
- if (!form) return;
- const fd = new FormData(form);
- const data = {
- datum: fd.get('datum'),
- uhrzeit: fd.get('uhrzeit')||null,
- kategorie: fd.get('kategorie'),
- intensitaet: parseInt(fd.get('intensitaet')||'3'),
- trigger: fd.get('trigger')||null,
- notiz: (fd.get('notiz')||'').trim()||null,
- };
- if (!data.kategorie) { UI.toast.warning('Bitte Kategorie wählen.'); return; }
- await UI.asyncButton(document.getElementById('beh-save-btn'), async () => {
- try {
- await API.health.behaviorCreate(dog.id, data);
- UI.modal.close();
- UI.toast.success('Eintrag gespeichert.');
- onSave();
- } catch (err) { UI.toast.error(err.message || 'Fehler.'); }
- });
- });
- }, 50);
- }
-
return { init, refresh, openNew, onDogChange };
})();
diff --git a/backend/static/js/pages/map.js b/backend/static/js/pages/map.js
index dda7473..de58da5 100644
--- a/backend/static/js/pages/map.js
+++ b/backend/static/js/pages/map.js
@@ -75,7 +75,7 @@ window.Page_map = (() => {
// z: zIndexOffset — höher = weiter oben bei Überlappung
const TYPEN = {
- restaurant: { icon: '
', label: 'Hundefreundl. Café/Restaurant', color: '#F97316', z: 10 },
+ restaurant: { icon: '
', label: 'Restaurant', color: '#F97316', z: 10 },
freilauf: { icon: '
', label: 'Freilauf', color: '#22C55E', z: 20 },
shop: { icon: '
', label: 'Shop', color: '#3B82F6', z: 15 },
kotbeutel: { icon: '
', label: 'Kotbeutel', color: '#84A98C', z: 5 },
@@ -92,7 +92,6 @@ window.Page_map = (() => {
treffpunkt: { icon: '
', label: 'Treffpunkt', color: '#7C3AED', z: 25 },
community: { icon: '
', label: 'Sonstiges', color: '#F59E0B', z: 30 },
zuechter: { icon: '
', label: 'Züchter', color: '#7C3AED', z: 50 },
- hotel: { icon: '
', label: 'Hundefreundl. Hotel', color: '#0369a1', z: 20 },
};
// Frontend-Layer → Backend-Typ Mapping
@@ -110,7 +109,6 @@ window.Page_map = (() => {
parkplatz: 'parkplatz',
treffpunkt: 'treffpunkt',
community: 'sonstiges',
- hotel: 'hotel',
};
// Gefahren-Radius-Kreis: prominente rote Fläche
diff --git a/backend/static/sw.js b/backend/static/sw.js
index 797d5b7..e9b1388 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
-const CACHE_VERSION = 'by-v875';
+const CACHE_VERSION = 'by-v872';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache