/* ============================================================ BAN YARO — Hunde-Profil Seiten-Modul: Profil anlegen / anzeigen / bearbeiten. ============================================================ */ window.Page_dog_profile = (() => { let _container = null; let _appState = null; // ---------------------------------------------------------- // INIT / REFRESH / LIFECYCLE // ---------------------------------------------------------- async function init(container, appState) { _container = container; _appState = appState; // Event-Delegation auf dem persistenten Container — überlebt innerHTML-Ersatz _container.addEventListener('click', e => { if (e.target.closest('#dp-add-dog-btn')) { _openCreateModal(); return; } if (e.target.closest('#dp-edit-btn')) { if (_appState.activeDog) _openEditModal(_appState.activeDog); return; } if (e.target.closest('#profile-goto-login')) { App.navigate('settings'); } }); await _render(); } async function refresh() { await _render(); } async function onDogChange(dog) { await _render(); } // ---------------------------------------------------------- // HAUPTRENDER // ---------------------------------------------------------- async function _render() { if (!_appState.user) { _container.innerHTML = UI.emptyState({ icon : '', title : 'Anmelden erforderlich', text : 'Melde dich an, um ein Hundeprofil anzulegen.', action: ``, }); _container.querySelector('#profile-goto-login') ?.addEventListener('click', () => App.navigate('settings')); return; } if (!_appState.activeDog) { _renderCreateForm(); } else { _renderProfile(_appState.activeDog); } } // ---------------------------------------------------------- // PROFIL-ANSICHT // ---------------------------------------------------------- function _renderProfile(dog) { const geburtstag = dog.geburtstag ? new Date(dog.geburtstag + 'T00:00:00') .toLocaleDateString('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }) : null; _container.innerHTML = `
${dog.foto_url ? `${_esc(dog.name)}` : `
${UI.icon('dog')}
`}

${_esc(dog.name)}

${dog.rasse ? `

${_esc(dog.rasse)}

` : `

`}
${geburtstag ? `
Geburtstag
${geburtstag}
${_calcAlter(dog.geburtstag)}
` : ''} ${dog.geschlecht ? `
${dog.geschlecht === 'm' ? '' : ''} Geschlecht
${dog.geschlecht === 'm' ? 'Rüde' : 'Hündin'}
` : ''} ${dog.gewicht_kg ? `
Gewicht
${dog.gewicht_kg} kg
` : ''}
Transponder
${dog.chip_nr ? `
${_esc(dog.chip_nr)}
` : `
nicht eingetragen
` }
${dog.bio ? `

"${_esc(dog.bio)}"

` : ''} ${dog.is_public ? `
NFC-Link
banyaro.app/hund/${dog.id}

Dieser Link kann auf ein NFC-Tag gebrannt werden

` : ''}
`; // Foto hochladen document.getElementById('dp-photo-input')?.addEventListener('change', async e => { const file = e.target.files[0]; if (!file) return; try { const fd = new FormData(); fd.append('file', file); const result = await API.dogs.uploadPhoto(dog.id, fd); dog.foto_url = result.foto_url; _appState.activeDog = { ..._appState.activeDog, foto_url: result.foto_url }; _appState.dogs = _appState.dogs.map(d => d.id === dog.id ? _appState.activeDog : d ); UI.toast.success('Foto gespeichert.'); _renderProfile(_appState.activeDog); } catch (err) { UI.toast.error(err.message || 'Fehler beim Hochladen.'); } }); // NFC-Link kopieren document.getElementById('dp-copy-link-btn')?.addEventListener('click', async () => { const url = `https://banyaro.app/hund/${dog.id}`; try { await navigator.clipboard.writeText(url); UI.toast.success('Link kopiert!'); } catch { // Fallback für ältere Browser const el = document.getElementById('dp-nfc-link'); const range = document.createRange(); range.selectNodeContents(el); const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); document.execCommand('copy'); sel.removeAllRanges(); UI.toast.success('Link kopiert!'); } }); // Transponder "Eintragen"-Button document.getElementById('dp-chip-edit-btn')?.addEventListener('click', () => { _showChipEdit(dog); }); document.getElementById('dp-ausweis-btn')?.addEventListener('click', () => { window.open(`/ausweis/${dog.id}`, '_blank'); }); document.getElementById('dp-share-btn')?.addEventListener('click', () => { _showShareModal(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: `
`, footer: ` `, }); 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.'); } }); } // ---------------------------------------------------------- // TEILEN // ---------------------------------------------------------- async function _showShareModal(dog) { UI.modal.open({ title: `${_esc(dog.name)} teilen`, body: `

Erstelle einen Einladungslink, den du per WhatsApp, Signal oder E-Mail teilen kannst. Die eingeladene Person sieht Tagebuch und Gesundheitsakte nach dem Annehmen.

`, footer: ` `, }); _loadShareList(dog.id); document.getElementById('share-create-btn').addEventListener('click', async () => { const role = document.getElementById('share-role-select').value; const btn = document.getElementById('share-create-btn'); UI.setLoading(btn, true); try { const res = await API.sharing.create(dog.id, role); const link = `${location.origin}${res.invite_path}`; const inp = document.getElementById('share-link-input'); inp.value = link; document.getElementById('share-link-result').style.display = 'block'; document.getElementById('share-link-copy').onclick = async () => { await navigator.clipboard.writeText(link).catch(() => {}); UI.toast.success('Link kopiert!'); }; UI.setLoading(btn, false); _loadShareList(dog.id); } catch (e) { UI.setLoading(btn, false); UI.toast.error(e.message || 'Fehler'); } }); } async function _loadShareList(dogId) { const wrap = document.getElementById('share-list-wrap'); if (!wrap) return; try { const shares = await API.sharing.list(dogId); if (!shares.length) { wrap.innerHTML = ''; return; } wrap.innerHTML = `
Aktive Einladungen
` + shares.map(s => `
${s.shared_with_name ? `${_esc(s.shared_with_name)} · ${s.role}` : `Ausstehend · ${s.role}`}
`).join(''); wrap.querySelectorAll('.share-revoke-btn').forEach(btn => { btn.addEventListener('click', async () => { await API.sharing.revoke(dogId, parseInt(btn.dataset.shareId)); _loadShareList(dogId); }); }); } catch (e) { /* ignore */ } } // ---------------------------------------------------------- // NEU ANLEGEN (direkt auf der Seite, kein Modal) // ---------------------------------------------------------- function _renderCreateForm() { _container.innerHTML = `
${UI.icon('dog')}

Hund anlegen

Erstelle das Profil für deinen Hund.

${_formHTML(null)}
`; _bindForm(null, false); } // ---------------------------------------------------------- // NEUEN HUND ANLEGEN (Modal) — auch aufrufbar via addNew() // ---------------------------------------------------------- function _openCreateModal() { UI.modal.open({ title: 'Weiteren Hund anlegen', body: _formHTML(null, true), footer: ` `, }); _bindForm(null, true); } // ---------------------------------------------------------- // BEARBEITEN (Modal) // ---------------------------------------------------------- function _openEditModal(dog) { UI.modal.open({ title: `${dog.name} bearbeiten`, body: _formHTML(dog, true), footer: ` `, }); _bindForm(dog, true); } // ---------------------------------------------------------- // FORMULAR HTML // ---------------------------------------------------------- function _formHTML(dog, inModal = false) { const today = new Date().toISOString().slice(0, 10); return `
${!inModal ? `
${dog ? `` : ''}
` : ''} ${dog ? `
` : ''}
`; } // ---------------------------------------------------------- // FORMULAR EVENTS // ---------------------------------------------------------- function _bindForm(dog, inModal) { const form = document.getElementById('dp-form'); if (!form) return; // Foto-Vorschau const fotoInput = document.getElementById('dp-form-foto'); const fotoPreview = document.getElementById('dp-form-preview'); if (fotoInput && fotoPreview) { fotoInput.addEventListener('change', () => { const file = fotoInput.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = e => { fotoPreview.src = e.target.result; fotoPreview.style.display = 'block'; }; reader.readAsDataURL(file); }); } document.getElementById('dp-form-cancel') ?.addEventListener('click', UI.modal.close); document.getElementById('dp-delete-btn')?.addEventListener('click', async () => { const ok = await UI.modal.confirm({ title : `${dog.name} löschen?`, message: 'Tagebuch-Einträge und Gesundheitsdaten werden ebenfalls gelöscht. Nicht rückgängig.', confirmText: 'Löschen', danger : true, }); if (!ok) return; try { await API.dogs.delete(dog.id); _appState.dogs = _appState.dogs.filter(d => d.id !== dog.id); _appState.activeDog = _appState.dogs[0] || null; if (inModal) UI.modal.close(); UI.toast.success(`${dog.name} wurde gelöscht.`); await _render(); } catch (err) { UI.toast.error(err.message || 'Fehler beim Löschen.'); } }); form.addEventListener('submit', async e => { e.preventDefault(); const btn = document.querySelector('[form="dp-form"][type="submit"]') || form.querySelector('[type="submit"]'); const fd = UI.formData(form); if (!fd.name?.trim()) { UI.toast.warning('Bitte einen Namen eingeben.'); return; } await UI.asyncButton(btn, async () => { const payload = { name: fd.name.trim(), rasse: fd.rasse || null, geburtstag: fd.geburtstag || null, geschlecht: fd.geschlecht || 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, }; let saved; if (dog) { saved = await API.dogs.update(dog.id, payload); _appState.dogs = _appState.dogs.map(d => d.id === dog.id ? saved : d); _appState.activeDog = saved; if (inModal) UI.modal.close(); UI.toast.success('Profil gespeichert.'); } else { saved = await API.dogs.create(payload); _appState.dogs.push(saved); _appState.activeDog = saved; localStorage.setItem('by_active_dog', String(saved.id)); if (inModal) UI.modal.close(); UI.toast.success(`${saved.name} wurde angelegt! 🎉`); } // Foto hochladen wenn gewählt const fotoFile = document.getElementById('dp-form-foto')?.files[0]; if (fotoFile) { try { const fd = new FormData(); fd.append('file', fotoFile); const result = await API.dogs.uploadPhoto(saved.id, fd); saved.foto_url = result.foto_url; _appState.activeDog = { ...saved }; _appState.dogs = _appState.dogs.map(d => d.id === saved.id ? _appState.activeDog : d); } catch { UI.toast.warning('Profil gespeichert, Foto konnte nicht hochgeladen werden.'); } } // Dog Switcher in Header + Sidebar aktualisieren App.renderDogSwitcher?.(); await _render(); }); }); } // ---------------------------------------------------------- // HELPER // ---------------------------------------------------------- function _calcAlter(geburtstag) { const born = new Date(geburtstag + 'T00:00:00'); const tage = Math.floor((Date.now() - born) / 86400000); if (tage < 0) return ''; if (tage < 30) return `${tage} Tag${tage !== 1 ? 'e' : ''} alt`; if (tage < 365) { const m = Math.floor(tage / 30); return `${m} Monat${m !== 1 ? 'e' : ''} alt`; } const j = Math.floor(tage / 365); const m = Math.floor((tage % 365) / 30); return m > 0 ? `${j} Jahr${j !== 1 ? 'e' : ''}, ${m} Monat${m !== 1 ? 'e' : ''} alt` : `${j} Jahr${j !== 1 ? 'e' : ''} alt`; } function _esc(str) { if (!str) return ''; return str.replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"'); } // ---------------------------------------------------------- // PUBLIC // ---------------------------------------------------------- return { init, refresh, onDogChange, addNew: _openCreateModal }; })();