Fix: UI.escape definieren + Transponder-Karte immer im Profil anzeigen

- UI.escape() fehlte im Public-API von ui.js (crash nach Profil speichern)
- Chip-Nr.-Karte im Hunde-Profil immer sichtbar, auch ohne Wert
- "Eintragen"-Button öffnet Inline-Edit-Modal direkt im Profil
- SW-Cache by-v143
This commit is contained in:
rene 2026-04-17 15:29:57 +02:00
parent 94e0ed3daa
commit d5f09cd16b
3 changed files with 60 additions and 8 deletions

View file

@ -133,14 +133,19 @@ window.Page_dog_profile = (() => {
<div style="font-weight:500;font-size:var(--text-sm)">${dog.gewicht_kg} kg</div>
</div>
` : ''}
${dog.chip_nr ? `
<div class="card" style="padding:var(--space-3)">
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
margin-bottom:2px"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#tag"></use></svg> Chip-Nr.</div>
<div style="font-size:var(--text-xs);font-weight:500;
word-break:break-all">${_esc(dog.chip_nr)}</div>
<div class="card" style="padding:var(--space-3)">
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);
margin-bottom:2px">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#wave-sine"></use></svg> Transponder
</div>
` : ''}
${dog.chip_nr
? `<div style="font-size:var(--text-xs);font-weight:500;word-break:break-all">${_esc(dog.chip_nr)}</div>`
: `<div style="font-size:var(--text-xs);color:var(--c-text-muted)">nicht eingetragen
<button class="btn btn-link btn-sm" id="dp-chip-edit-btn"
style="padding:0 0 0 var(--space-1);font-size:var(--text-xs)">Eintragen</button>
</div>`
}
</div>
</div>
${dog.bio ? `
@ -230,9 +235,46 @@ window.Page_dog_profile = (() => {
}
});
// Transponder "Eintragen"-Button
document.getElementById('dp-chip-edit-btn')?.addEventListener('click', () => {
_showChipEdit(dog);
});
// Edit- und Add-Klicks laufen über Event-Delegation in init() — keine direkten Listener nötig.
}
function _showChipEdit(dog) {
UI.modal.open({
title: 'Transpondernummer',
body: `
<div class="mb-3">
<label class="form-label">Chip-Nummer (15-stellig)</label>
<input id="chip-edit-input" class="form-control" type="text"
value="${_esc(dog.chip_nr || '')}" placeholder="z.B. 276009200123456" maxlength="20">
</div>`,
footer: `
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
<button class="btn btn-primary" id="chip-edit-save-btn">Speichern</button>`,
});
document.getElementById('chip-edit-save-btn').addEventListener('click', async () => {
const nr = document.getElementById('chip-edit-input').value.trim() || null;
const btn = document.getElementById('chip-edit-save-btn');
UI.setLoading(btn, true);
try {
await API.dogs.update(dog.id, { chip_nr: nr });
dog.chip_nr = nr;
_appState.activeDog = { ..._appState.activeDog, chip_nr: nr };
_appState.dogs = _appState.dogs.map(d => d.id === dog.id ? _appState.activeDog : d);
UI.modal.close();
UI.toast.success('Transpondernummer gespeichert.');
_renderProfile(_appState.activeDog);
} catch (e) {
UI.setLoading(btn, false);
UI.toast.error('Fehler beim Speichern.');
}
});
}
// ----------------------------------------------------------
// NEU ANLEGEN (direkt auf der Seite, kein Modal)
// ----------------------------------------------------------

View file

@ -259,6 +259,15 @@ const UI = (() => {
`;
}
function escape(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
// Öffentliche API
return {
toast, modal,
@ -267,6 +276,7 @@ const UI = (() => {
emptyState, time,
setupPhotoPreview, scrollTop, skeleton,
icon: _svgIcon,
escape,
};
})();

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v142';
const CACHE_VERSION = 'by-v143';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten