Fix: alle funktionalen Inline-Event-Handler → addEventListener/Delegation (von CSP-Härtung 65cfa25 app-weit blockiert)
Chat (senden/öffnen/löschen/Foto), Tagebuch-Buch, KI-Berichte, Wiki-Moderation, Events-Detail, Walks-Lightbox, Routen-Foto, Navigations-CTAs (data-page), Presse-Copy + Züchter-Landing (externes JS). 35x UI.modal.close → data-modal-close, 28x totes event.stopPropagation entfernt. Verbleibend: kosmetische onerror/Hover. SW v1164
This commit is contained in:
parent
152fde716c
commit
2ddd8ac350
34 changed files with 228 additions and 173 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1163
|
||||
1164
|
||||
|
|
@ -86,14 +86,14 @@
|
|||
<title>Ban Yaro</title>
|
||||
|
||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
||||
<script src="/js/boot-early.js?v=1163"></script>
|
||||
<script src="/js/boot-early.js?v=1164"></script>
|
||||
|
||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1163">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1163">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1163">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1163">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1163">
|
||||
<link rel="stylesheet" href="/css/design-system.css?v=1164">
|
||||
<link rel="stylesheet" href="/css/layout.css?v=1164">
|
||||
<link rel="stylesheet" href="/css/components.css?v=1164">
|
||||
<link rel="stylesheet" href="/css/utilities.css?v=1164">
|
||||
<link rel="stylesheet" href="/css/lists.css?v=1164">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
@ -617,11 +617,11 @@
|
|||
<div id="modal-container"></div>
|
||||
|
||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||
<script src="/js/api.js?v=1163"></script>
|
||||
<script src="/js/ui.js?v=1163"></script>
|
||||
<script src="/js/app.js?v=1163"></script>
|
||||
<script src="/js/worlds.js?v=1163"></script>
|
||||
<script src="/js/offline-indicator.js?v=1163"></script>
|
||||
<script src="/js/api.js?v=1164"></script>
|
||||
<script src="/js/ui.js?v=1164"></script>
|
||||
<script src="/js/app.js?v=1164"></script>
|
||||
<script src="/js/worlds.js?v=1164"></script>
|
||||
<script src="/js/offline-indicator.js?v=1164"></script>
|
||||
|
||||
<!-- Feature-Seiten werden lazy geladen -->
|
||||
|
||||
|
|
@ -631,7 +631,7 @@
|
|||
|
||||
|
||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
||||
<script src="/js/boot.js?v=1163"></script>
|
||||
<script src="/js/boot.js?v=1164"></script>
|
||||
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '1163'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '1164'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
||||
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||
window.APP_VERSION = APP_VERSION;
|
||||
|
|
@ -443,6 +443,20 @@ const App = (() => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Foto-Lightbox (Inline-onclick ist CSP-blockiert)
|
||||
const lb = e.target.closest('[data-lightbox-url]');
|
||||
if (lb) {
|
||||
window.UI?.lightbox?.show?.([{ url: lb.dataset.lightboxUrl }], 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Externer Link in neuem Tab
|
||||
const ol = e.target.closest('[data-open-url]');
|
||||
if (ol) {
|
||||
window.open(ol.dataset.openUrl, '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
// Header-User-Button → Settings
|
||||
if (e.target.closest('#header-user-btn')) {
|
||||
navigate('settings');
|
||||
|
|
@ -1164,7 +1178,7 @@ const App = (() => {
|
|||
icon: UI.icon(icon),
|
||||
title: 'Anmelden erforderlich',
|
||||
text,
|
||||
action: `<button class="btn btn-primary" onclick="App.navigate('settings')">Anmelden</button>`,
|
||||
action: `<button class="btn btn-primary" data-page="settings">Anmelden</button>`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2661,7 +2661,7 @@ window.Page_admin = (() => {
|
|||
background:var(--c-surface-2);border-radius:var(--radius-md);
|
||||
padding:var(--space-3);max-height:60vh;overflow-y:auto;
|
||||
color:var(--c-text)">${UI.escape(l.body || '(kein Text gespeichert)')}</pre>`,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -2760,7 +2760,7 @@ window.Page_admin = (() => {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="${id}" type="submit">Speichern</button>`,
|
||||
});
|
||||
|
||||
|
|
@ -2993,7 +2993,7 @@ window.Page_admin = (() => {
|
|||
</div>
|
||||
</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<button class="btn btn-primary" id="adm-bew-save-note">Notiz speichern</button>`,
|
||||
});
|
||||
document.getElementById('adm-bew-save-note')?.addEventListener('click', async () => {
|
||||
|
|
|
|||
|
|
@ -780,7 +780,7 @@ window.Page_adoption = (() => {
|
|||
<button type="submit" form="adp-interest-form" class="btn btn-primary flex-1" id="adp-interest-submit">
|
||||
${UI.icon('heart')} Interesse bekunden
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button type="button" class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -879,7 +879,7 @@ window.Page_adoption = (() => {
|
|||
<button type="submit" form="adp-create-form" class="btn btn-primary w-full" id="adp-create-submit">
|
||||
${UI.icon('plus')} Inserat erstellen
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button type="button" class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,23 @@ window.Page_chat = (() => {
|
|||
_container = container;
|
||||
_myId = appState?.user?.id || null;
|
||||
|
||||
// Delegierter Click-Handler — Inline-onclick wird von der CSP blockiert.
|
||||
if (!_container._chatClickBound) {
|
||||
_container.addEventListener('click', e => {
|
||||
const t = e.target.closest('[data-chat-action]');
|
||||
if (!t) return;
|
||||
switch (t.dataset.chatAction) {
|
||||
case 'open': _openThread(parseInt(t.dataset.chatId, 10)); break;
|
||||
case 'list': _showList(); break;
|
||||
case 'photo': document.getElementById('chat-photo-input')?.click(); break;
|
||||
case 'send': _send(); break;
|
||||
case 'delete': _deleteMsg(parseInt(t.dataset.chatId, 10)); break;
|
||||
case 'img': window.open(t.dataset.chatUrl, '_blank'); break;
|
||||
}
|
||||
});
|
||||
_container._chatClickBound = true;
|
||||
}
|
||||
|
||||
// Heartbeat: alle 30s online-Status senden
|
||||
API.chat.heartbeat().catch(() => {});
|
||||
_heartbeatTimer = setInterval(() => {
|
||||
|
|
@ -132,7 +149,7 @@ window.Page_chat = (() => {
|
|||
? `<span class="online-dot" title="Online"></span>`
|
||||
: '';
|
||||
return `
|
||||
<div class="chat-conv-item" onclick="Page_chat._openThread(${c.id})">
|
||||
<div class="chat-conv-item" data-chat-action="open" data-chat-id="${c.id}">
|
||||
<div style="position:relative;flex-shrink:0">
|
||||
<div class="chat-conv-avatar">${initials}</div>
|
||||
${onlineDot ? `<span class="online-dot chat-avatar-dot"></span>` : ''}
|
||||
|
|
@ -166,14 +183,14 @@ window.Page_chat = (() => {
|
|||
|
||||
// Aktive Markierung in der Liste
|
||||
document.querySelectorAll('.chat-conv-item').forEach(el =>
|
||||
el.classList.toggle('active', el.getAttribute('onclick')?.includes(String(convId)))
|
||||
el.classList.toggle('active', el.dataset.chatId === String(convId))
|
||||
);
|
||||
|
||||
const threadHTML = `
|
||||
<div class="chat-thread" id="chat-thread">
|
||||
<div class="chat-thread-header">
|
||||
${_isDesktop() ? '' : `
|
||||
<button class="btn btn-ghost btn-sm" onclick="Page_chat._showList()" style="padding:var(--space-1)">
|
||||
<button class="btn btn-ghost btn-sm" data-chat-action="list" style="padding:var(--space-1)">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#arrow-left"></use></svg>
|
||||
</button>`}
|
||||
<div style="position:relative;flex-shrink:0">
|
||||
|
|
@ -188,14 +205,13 @@ window.Page_chat = (() => {
|
|||
</div>
|
||||
</div>
|
||||
<div class="chat-input-bar">
|
||||
<input type="file" id="chat-photo-input" accept="image/*" class="hidden"
|
||||
onchange="Page_chat._onPhotoSelected(this)">
|
||||
<button class="chat-photo-btn" onclick="document.getElementById('chat-photo-input').click()" title="Foto senden">
|
||||
<input type="file" id="chat-photo-input" accept="image/*" class="hidden">
|
||||
<button class="chat-photo-btn" data-chat-action="photo" title="Foto senden">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#camera"></use></svg>
|
||||
</button>
|
||||
<textarea id="chat-input" class="chat-input" rows="1"
|
||||
placeholder="Nachricht…" maxlength="2000"></textarea>
|
||||
<button class="chat-send-btn" id="chat-send-btn" onclick="Page_chat._send()">
|
||||
<button class="chat-send-btn" id="chat-send-btn" data-chat-action="send">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#paper-plane-tilt"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -219,9 +235,11 @@ window.Page_chat = (() => {
|
|||
input.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
Page_chat._send();
|
||||
_send();
|
||||
}
|
||||
});
|
||||
document.getElementById('chat-photo-input')
|
||||
?.addEventListener('change', e => _onPhotoSelected(e.target));
|
||||
|
||||
await _loadMessages(true);
|
||||
await API.chat.markRead(_convId).catch(() => {});
|
||||
|
|
@ -313,7 +331,7 @@ window.Page_chat = (() => {
|
|||
const timeStr = _fmtTime(m.created_at);
|
||||
const deleteBtn = isMine && !m.is_deleted
|
||||
? `<button class="btn btn-ghost" style="padding:2px;opacity:0.4;font-size:var(--text-xs)"
|
||||
onclick="Page_chat._deleteMsg(${m.id})" title="Löschen">
|
||||
data-chat-action="delete" data-chat-id="${m.id}" title="Löschen">
|
||||
<svg class="ph-icon" style="width:12px;height:12px"><use href="/icons/phosphor.svg#trash"></use></svg>
|
||||
</button>`
|
||||
: '';
|
||||
|
|
@ -328,7 +346,7 @@ window.Page_chat = (() => {
|
|||
// Medieninhalt
|
||||
let bubbleContent = '';
|
||||
if (m.media_url) {
|
||||
bubbleContent += `<img src="${UI.escape(m.media_url)}" class="chat-bubble-img" alt="Foto" onclick="window.open('${UI.escape(m.media_url)}','_blank')">`;
|
||||
bubbleContent += `<img src="${UI.escape(m.media_url)}" class="chat-bubble-img" alt="Foto" data-chat-action="img" data-chat-url="${UI.escape(m.media_url)}">`;
|
||||
}
|
||||
if (m.text) {
|
||||
bubbleContent += (m.media_url ? `<div style="margin-top:var(--space-1)">` : '') +
|
||||
|
|
|
|||
|
|
@ -1113,8 +1113,7 @@ window.Page_diary = (() => {
|
|||
<span class="diary-detail-date-center">${datumLang}</span>
|
||||
<div style="display:flex;align-items:center;gap:4px">
|
||||
${!_appState?.activeDog?.is_guest
|
||||
? `<button id="diary-dv-note" class="btn btn-ghost btn-xs" title="Notiz"
|
||||
onclick="event.stopPropagation()">
|
||||
? `<button id="diary-dv-note" class="btn btn-ghost btn-xs" title="Notiz">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
||||
</button>
|
||||
<button id="diary-dv-edit" class="diary-detail-edit">
|
||||
|
|
@ -1722,7 +1721,7 @@ window.Page_diary = (() => {
|
|||
<div id="import-result" style="display:none;margin-top:var(--space-4)"></div>`,
|
||||
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<button class="btn btn-primary" id="import-start-btn">Importieren</button>`,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -616,7 +616,7 @@ window.Page_dog_profile = (() => {
|
|||
footer: `
|
||||
<div class="w3-btn-stack">
|
||||
<button class="btn btn-primary" id="chip-edit-save-btn" class="w-full">Speichern</button>
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
</div>`,
|
||||
});
|
||||
document.getElementById('chip-edit-save-btn').addEventListener('click', async () => {
|
||||
|
|
@ -675,7 +675,7 @@ window.Page_dog_profile = (() => {
|
|||
${hasPhoto ? `<button class="btn btn-primary" id="pe-save-btn" class="w-full">Speichern</button>` : ''}
|
||||
<div class="flex-gap-2">
|
||||
${hasPhoto ? `<button class="btn btn-danger" id="pe-delete-btn">${UI.icon('trash')} Löschen</button>` : ''}
|
||||
<button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary flex-1" data-modal-close>Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -957,7 +957,7 @@ window.Page_dog_profile = (() => {
|
|||
</div>
|
||||
<div id="share-list-wrap" class="mt-4"></div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<button class="btn btn-primary" id="share-create-btn">Link erstellen</button>`,
|
||||
});
|
||||
|
||||
|
|
@ -1489,7 +1489,7 @@ window.Page_dog_profile = (() => {
|
|||
</p>
|
||||
${data.hinweis ? `<p style="font-size:var(--text-sm);color:var(--c-text-muted);margin-top:var(--space-3)">${UI.escape(data.hinweis)}</p>` : ''}
|
||||
</div>`,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -1608,7 +1608,7 @@ window.Page_dog_profile = (() => {
|
|||
</div>`,
|
||||
footer: `
|
||||
<div style="display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:flex-end">
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<a class="btn btn-secondary" href="/ausweis/${dog.id}" target="_blank" rel="noopener">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#identification-card"></use></svg>
|
||||
Ausweis öffnen
|
||||
|
|
@ -1832,7 +1832,7 @@ window.Page_dog_profile = (() => {
|
|||
</div>`,
|
||||
footer: `
|
||||
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="pp-meta-save">Speichern</button>
|
||||
</div>`,
|
||||
});
|
||||
|
|
@ -1896,7 +1896,7 @@ window.Page_dog_profile = (() => {
|
|||
</div>`,
|
||||
footer: `
|
||||
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="pp-vacc-save">Speichern</button>
|
||||
</div>`,
|
||||
});
|
||||
|
|
@ -1960,7 +1960,7 @@ window.Page_dog_profile = (() => {
|
|||
</div>`,
|
||||
footer: `
|
||||
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="pp-med-save">Speichern</button>
|
||||
</div>`,
|
||||
});
|
||||
|
|
@ -2017,7 +2017,7 @@ window.Page_dog_profile = (() => {
|
|||
UI.modal.open({
|
||||
title: 'Hundepass-Link teilen',
|
||||
body: shareWrap.innerHTML,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
document.getElementById('pp-sharelink-copy')?.addEventListener('click', async () => {
|
||||
await navigator.clipboard.writeText(url).catch(() => {});
|
||||
|
|
@ -2209,7 +2209,7 @@ window.Page_dog_profile = (() => {
|
|||
? 'background:#7a4f1a;color:#f5e4c0;border-color:#7a4f1a;'
|
||||
: 'background:#f5f0e8;color:#444;border-color:#e0d4b8;';
|
||||
const label = y === 'alle' ? 'Alle' : y;
|
||||
return `<button onclick="window._buchSetJahr('${y}')" style="
|
||||
return `<button data-buch-action="year" data-buch-year="${y}" style="
|
||||
border:1px solid;border-radius:8px;padding:8px 16px;
|
||||
font-size:0.9rem;cursor:pointer;font-family:inherit;
|
||||
${active}
|
||||
|
|
@ -2239,14 +2239,14 @@ window.Page_dog_profile = (() => {
|
|||
|
||||
<div style="margin-bottom:20px;display:flex;flex-direction:column;gap:10px">
|
||||
<label style="display:flex;align-items:center;gap:12px;cursor:pointer">
|
||||
<button onclick="window._buchToggleFotos()" style="
|
||||
<button data-buch-action="fotos" style="
|
||||
width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;
|
||||
${togStyle(nurFotos)}
|
||||
">${nurFotos ? '✓' : ''}</button>
|
||||
<span style="font-size:0.95rem">Nur Einträge mit Fotos</span>
|
||||
</label>
|
||||
<label style="display:flex;align-items:center;gap:12px;cursor:pointer">
|
||||
<button onclick="window._buchToggleMeilensteine()" style="
|
||||
<button data-buch-action="meilen" style="
|
||||
width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;
|
||||
${togStyle(nurMeilensteine)}
|
||||
">${nurMeilensteine ? '✓' : ''}</button>
|
||||
|
|
@ -2255,11 +2255,11 @@ window.Page_dog_profile = (() => {
|
|||
</div>
|
||||
|
||||
<div style="display:flex;gap:10px">
|
||||
<button onclick="window._buchOpen()" style="
|
||||
<button data-buch-action="open" style="
|
||||
flex:1;background:#7a4f1a;color:#f5e4c0;border:none;border-radius:10px;
|
||||
padding:14px;font-size:1rem;font-weight:700;cursor:pointer;font-family:inherit;
|
||||
">📖 Buch öffnen</button>
|
||||
<button onclick="window._buchClose()" style="
|
||||
<button data-buch-action="close" style="
|
||||
background:#f0f0f0;color:#555;border:none;border-radius:10px;
|
||||
padding:14px 18px;font-size:1rem;cursor:pointer;font-family:inherit;
|
||||
">✕</button>
|
||||
|
|
@ -2268,18 +2268,14 @@ window.Page_dog_profile = (() => {
|
|||
`;
|
||||
};
|
||||
|
||||
window._buchSetJahr = (y) => { selectedJahr = y; renderModal(); };
|
||||
window._buchToggleFotos = () => { nurFotos = !nurFotos; renderModal(); };
|
||||
window._buchToggleMeilensteine = () => { nurMeilensteine = !nurMeilensteine; renderModal(); };
|
||||
window._buchClose = () => {
|
||||
const setJahr = (y) => { selectedJahr = y; renderModal(); };
|
||||
const toggleFotos = () => { nurFotos = !nurFotos; renderModal(); };
|
||||
const toggleMeilen = () => { nurMeilensteine = !nurMeilensteine; renderModal(); };
|
||||
const closeModal = () => {
|
||||
modalEl.remove();
|
||||
delete window._buchSetJahr;
|
||||
delete window._buchToggleFotos;
|
||||
delete window._buchToggleMeilensteine;
|
||||
delete window._buchOpen;
|
||||
delete window._buchClose;
|
||||
document.removeEventListener('keydown', onKey);
|
||||
};
|
||||
window._buchOpen = () => {
|
||||
const openBuch = () => {
|
||||
const params = new URLSearchParams();
|
||||
if (selectedJahr !== 'alle') params.set('jahr', selectedJahr);
|
||||
if (nurFotos) params.set('nur_fotos', 'true');
|
||||
|
|
@ -2290,10 +2286,24 @@ window.Page_dog_profile = (() => {
|
|||
|
||||
renderModal();
|
||||
document.body.appendChild(modalEl);
|
||||
modalEl.addEventListener('click', e => { if (e.target === modalEl) window._buchClose(); });
|
||||
|
||||
// Delegierter Click-Handler (Inline-onclick wird von der CSP blockiert);
|
||||
// überlebt das Re-Rendern via renderModal().
|
||||
modalEl.addEventListener('click', e => {
|
||||
if (e.target === modalEl) { closeModal(); return; }
|
||||
const btn = e.target.closest('[data-buch-action]');
|
||||
if (!btn) return;
|
||||
switch (btn.dataset.buchAction) {
|
||||
case 'year': setJahr(btn.dataset.buchYear); break;
|
||||
case 'fotos': toggleFotos(); break;
|
||||
case 'meilen': toggleMeilen(); break;
|
||||
case 'open': openBuch(); break;
|
||||
case 'close': closeModal(); break;
|
||||
}
|
||||
});
|
||||
|
||||
const onKey = e => {
|
||||
if (e.key === 'Escape') { window._buchClose(); document.removeEventListener('keydown', onKey); }
|
||||
if (e.key === 'Escape') { closeModal(); }
|
||||
};
|
||||
document.addEventListener('keydown', onKey);
|
||||
}
|
||||
|
|
@ -2310,7 +2320,7 @@ window.Page_dog_profile = (() => {
|
|||
<use href="/icons/phosphor.svg#spinner-gap"></use>
|
||||
</svg>
|
||||
</div>`,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
size: 'large',
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ window.Page_ernaehrung = (() => {
|
|||
icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bowl-food"></use></svg>',
|
||||
title: 'Noch kein Hund angelegt',
|
||||
text: 'Erstelle zuerst ein Hundeprofil.',
|
||||
action: `<button class="btn btn-primary" onclick="App.navigate('dog-profile')">Profil erstellen</button>`,
|
||||
action: `<button class="btn btn-primary" data-page="dog-profile">Profil erstellen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -728,7 +728,7 @@ window.Page_ernaehrung = (() => {
|
|||
</form>
|
||||
`;
|
||||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="vert-futter-save-btn" form="${id}">Speichern</button>
|
||||
`;
|
||||
UI.modal.open({ title: 'Futter erfassen', body, footer });
|
||||
|
|
@ -840,7 +840,7 @@ window.Page_ernaehrung = (() => {
|
|||
</form>
|
||||
`;
|
||||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="vert-reaktion-save-btn" form="${id}">Speichern</button>
|
||||
`;
|
||||
UI.modal.open({ title: 'Reaktion erfassen', body, footer });
|
||||
|
|
|
|||
|
|
@ -221,17 +221,17 @@ window.Page_events = (() => {
|
|||
</div>
|
||||
${ev.rsvp_count ? `<span class="event-attendees" data-ev-attendees="${ev.id}">${_icon('users')} ${ev.rsvp_count} nehmen teil</span>` : ''}
|
||||
${ev.link ? `<div class="events-card-actions">
|
||||
<a class="btn btn-ghost btn-xs ev-ext-link" href="${UI.escape(ev.link)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">
|
||||
<a class="btn btn-ghost btn-xs ev-ext-link" href="${UI.escape(ev.link)}" target="_blank" rel="noopener">
|
||||
${_icon('arrow-square-out')} Details
|
||||
</a>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-1)">
|
||||
${isOwn ? `<button class="btn-icon" data-ev-edit="${ev.id}" title="Bearbeiten" onclick="event.stopPropagation()">${_icon('pencil-simple')}</button>` : ''}
|
||||
${isOwn ? `<button class="btn-icon" data-ev-edit="${ev.id}" title="Bearbeiten">${_icon('pencil-simple')}</button>` : ''}
|
||||
${_state.user ? `<button class="btn-icon ev-note-btn" data-ev-note-id="${ev.id}"
|
||||
data-ev-note-label="${UI.escape(ev.titel + ' ' + ev.datum)}"
|
||||
data-ev-note-ort="${UI.escape(ev.ort_name || '')}"
|
||||
title="Notiz" class="text-muted" onclick="event.stopPropagation()">
|
||||
title="Notiz" class="text-muted">
|
||||
${_icon('note-pencil')}</button>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -276,7 +276,7 @@ window.Page_events = (() => {
|
|||
<span style="color:var(--c-text-muted);font-size:12px">${datum}</span><br>
|
||||
${ev.ort_name ? `<span style="font-size:12px">📍 ${UI.escape(ev.ort_name)}</span><br>` : ''}
|
||||
${ev.beschreibung ? `<span style="font-size:12px">${UI.escape(ev.beschreibung.slice(0, 80))}${ev.beschreibung.length > 80 ? '…' : ''}</span><br>` : ''}
|
||||
<a href="#" onclick="event.preventDefault();Page_events._openDetail(${ev.id})"
|
||||
<a href="#" data-ev-detail="${ev.id}"
|
||||
style="font-size:12px;color:var(--c-primary,#2563eb)">Details</a>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -512,7 +512,7 @@ window.Page_events = (() => {
|
|||
</button>
|
||||
<div class="flex-gap-2">
|
||||
${isEdit ? `<button type="button" class="btn btn-danger" id="ev-form-delete">Löschen</button>` : ''}
|
||||
<button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary flex-1" data-modal-close>Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -570,6 +570,14 @@ window.Page_events = (() => {
|
|||
// Click-Handler
|
||||
// ----------------------------------------------------------
|
||||
function _onClick(e) {
|
||||
// Detail-Link (Karten-Popup) — Inline-onclick ist CSP-blockiert
|
||||
const detailLink = e.target.closest('[data-ev-detail]');
|
||||
if (detailLink) {
|
||||
e.preventDefault();
|
||||
_showDetail(parseInt(detailLink.dataset.evDetail, 10));
|
||||
return;
|
||||
}
|
||||
|
||||
// Quelle-Filter
|
||||
const sourceBtn = e.target.closest('[data-ev-quelle]');
|
||||
if (sourceBtn) {
|
||||
|
|
|
|||
|
|
@ -485,7 +485,7 @@ window.Page_expenses = (() => {
|
|||
</form>`;
|
||||
|
||||
const footer = `
|
||||
<button type="button" class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button type="button" class="btn btn-secondary flex-1" data-modal-close>Abbrechen</button>
|
||||
<button type="submit" form="exp-recurring-form" class="btn btn-primary flex-1">Speichern</button>`;
|
||||
|
||||
UI.modal.open({ title: r ? 'Dauerauftrag bearbeiten' : 'Neuer Dauerauftrag', body, footer });
|
||||
|
|
@ -755,10 +755,10 @@ window.Page_expenses = (() => {
|
|||
style="color:var(--c-danger);margin-right:auto">
|
||||
${UI.icon('trash')}
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button type="button" class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button type="submit" form="${formId}" class="btn btn-primary">Speichern</button>
|
||||
` : `
|
||||
<button type="button" class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button type="button" class="btn btn-secondary flex-1" data-modal-close>Abbrechen</button>
|
||||
<button type="submit" form="${formId}" class="btn btn-primary flex-1">Speichern</button>
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ function _fmtDate(iso) {
|
|||
</div>`;
|
||||
|
||||
UI.modal.open({ title: '🏆 Hund des Monats', body,
|
||||
footer: `<button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Schließen</button>` });
|
||||
footer: `<button class="btn btn-secondary flex-1" data-modal-close>Schließen</button>` });
|
||||
|
||||
document.getElementById('hdm-login-link')?.addEventListener('click', e => {
|
||||
e.preventDefault(); UI.modal.close(); App.navigate('settings');
|
||||
|
|
@ -1022,7 +1022,7 @@ function _fmtDate(iso) {
|
|||
</p>
|
||||
|
||||
</div>`,
|
||||
footer: `<button class="btn btn-primary flex-1" onclick="UI.modal.close()">Verstanden</button>`,
|
||||
footer: `<button class="btn btn-primary flex-1" data-modal-close>Verstanden</button>`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1376,7 +1376,7 @@ function _fmtDate(iso) {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-ghost flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-ghost flex-1" data-modal-close>Abbrechen</button>
|
||||
<button type="submit" form="${id}" class="btn btn-primary flex-1">${UI.icon('floppy-disk')} Speichern</button>`,
|
||||
});
|
||||
document.getElementById(id)?.addEventListener('submit', async e => {
|
||||
|
|
@ -1423,7 +1423,7 @@ function _fmtDate(iso) {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-ghost flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-ghost flex-1" data-modal-close>Abbrechen</button>
|
||||
<button type="submit" form="${id}" class="btn btn-primary flex-1">${UI.icon('floppy-disk')} Speichern</button>`,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ window.Page_friends = (() => {
|
|||
icon: UI.icon('users'),
|
||||
title: 'Anmelden erforderlich',
|
||||
text: 'Melde dich an, um Freunde zu finden und Anfragen zu verwalten.',
|
||||
action: `<button class="btn btn-primary" onclick="App.navigate('settings')">Anmelden</button>`,
|
||||
action: `<button class="btn btn-primary" data-page="settings">Anmelden</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -524,8 +524,7 @@ window.Page_friends = (() => {
|
|||
<button class="btn btn-ghost btn-sm fr-note-btn"
|
||||
data-fr-note-id="${f.friend_id}"
|
||||
data-fr-note-name="${UI.escape(f.friend_name)}"
|
||||
title="Notiz"
|
||||
onclick="event.stopPropagation()">
|
||||
title="Notiz">
|
||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm"
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ window.Page_health = (() => {
|
|||
icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#syringe"></use></svg>',
|
||||
title: 'Noch kein Hund angelegt',
|
||||
text: 'Erstelle zuerst ein Hundeprofil.',
|
||||
action: `<button class="btn btn-primary" onclick="App.navigate('dog-profile')">Profil erstellen</button>`,
|
||||
action: `<button class="btn btn-primary" data-page="dog-profile">Profil erstellen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -403,8 +403,7 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="${UI.escape(e.bezeichnung)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -511,8 +510,7 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="${UI.escape(e.bezeichnung)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -563,8 +561,7 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text" style="padding-top:var(--space-1)">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-1);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="Gewicht ${UI.escape(e.datum)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="Gewicht ${UI.escape(e.datum)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
|
@ -801,8 +798,7 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="Läufigkeit ${UI.escape(e.datum)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="Läufigkeit ${UI.escape(e.datum)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
|
@ -839,8 +835,7 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="${UI.escape(e.bezeichnung)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
|
|
@ -880,8 +875,7 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="${UI.escape(e.bezeichnung)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
|
@ -923,19 +917,16 @@ window.Page_health = (() => {
|
|||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
||||
<button class="btn btn-ghost btn-xs" style="margin-top:var(--space-2);font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 6px"
|
||||
data-action="open-note" data-entry-id="${e.id}"
|
||||
data-label="${UI.escape(e.bezeichnung)}"
|
||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||
${count
|
||||
? `<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2);align-items:center;flex-wrap:wrap">
|
||||
${mediaList.slice(0, 3).map(m => m.media_type === 'pdf'
|
||||
? `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
|
||||
class="btn btn-secondary btn-sm" style="display:inline-flex"
|
||||
onclick="event.stopPropagation()">
|
||||
class="btn btn-secondary btn-sm" style="display:inline-flex">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF
|
||||
</a>`
|
||||
: `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
|
||||
class="btn btn-secondary btn-sm" style="display:inline-flex"
|
||||
onclick="event.stopPropagation()">
|
||||
class="btn btn-secondary btn-sm" style="display:inline-flex">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#image"></use></svg> Bild
|
||||
</a>`
|
||||
).join('')}
|
||||
|
|
@ -1773,28 +1764,24 @@ window.Page_health = (() => {
|
|||
${ratingHtml}
|
||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2);flex-wrap:wrap">
|
||||
${p.telefon ? `
|
||||
<a href="tel:${UI.escape(p.telefon)}" class="btn btn-secondary btn-sm"
|
||||
onclick="event.stopPropagation()">
|
||||
<a href="tel:${UI.escape(p.telefon)}" class="btn btn-secondary btn-sm">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> Anrufen
|
||||
</a>` : ''}
|
||||
${p.notfall_telefon ? `
|
||||
<a href="tel:${UI.escape(p.notfall_telefon)}" class="btn btn-danger btn-sm"
|
||||
onclick="event.stopPropagation()">
|
||||
<a href="tel:${UI.escape(p.notfall_telefon)}" class="btn btn-danger btn-sm">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Notfall
|
||||
</a>` : ''}
|
||||
<button class="btn btn-sm btn-secondary"
|
||||
data-action="bewerten" data-praxis-id="${p.id}"
|
||||
title="Bewertung abgeben"
|
||||
style="flex-shrink:0"
|
||||
onclick="event.stopPropagation()">
|
||||
style="flex-shrink:0">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||
Bewerten
|
||||
</button>
|
||||
<button class="btn btn-sm ${isFav ? 'btn-primary' : 'btn-secondary'}"
|
||||
data-action="toggle-fav" data-praxis-id="${p.id}"
|
||||
title="${isFav ? 'Favorit entfernen' : 'Als mein Tierarzt merken'}"
|
||||
style="flex-shrink:0"
|
||||
onclick="event.stopPropagation()">
|
||||
style="flex-shrink:0">
|
||||
<svg class="ph-icon" aria-hidden="true">
|
||||
<use href="/icons/phosphor.svg#${isFav ? 'heart-fill' : 'heart'}"></use>
|
||||
</svg>
|
||||
|
|
@ -1803,8 +1790,7 @@ window.Page_health = (() => {
|
|||
<button class="btn btn-sm btn-secondary"
|
||||
data-action="edit-praxis" data-praxis-id="${p.id}"
|
||||
title="Praxis bearbeiten"
|
||||
style="flex-shrink:0"
|
||||
onclick="event.stopPropagation()">
|
||||
style="flex-shrink:0">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#pencil-simple"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -1881,7 +1867,7 @@ window.Page_health = (() => {
|
|||
<use href="/icons/phosphor.svg#spinner-gap"></use>
|
||||
</svg>
|
||||
</div>`,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<button class="btn btn-primary" id="detail-bewerten-btn">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||
Jetzt bewerten
|
||||
|
|
@ -1998,7 +1984,7 @@ window.Page_health = (() => {
|
|||
title: `${UI.escape(praxis.name)} bewerten`,
|
||||
body,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="bew-submit-btn" form="bew-form">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||
${existing ? 'Bewertung aktualisieren' : 'Bewertung abgeben'}
|
||||
|
|
@ -2373,7 +2359,7 @@ window.Page_health = (() => {
|
|||
value="${UI.escape(currentNr)}" placeholder="z.B. 276009200123456" maxlength="20">
|
||||
</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="transponder-save-btn">Speichern</button>`,
|
||||
});
|
||||
document.getElementById('transponder-save-btn').addEventListener('click', async () => {
|
||||
|
|
@ -2441,11 +2427,11 @@ window.Page_health = (() => {
|
|||
const b = berichte[idx];
|
||||
const nav = berichte.length > 1 ? `
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
|
||||
<button onclick="window._kiPrev()" style="padding:6px 16px;border-radius:999px;
|
||||
<button data-ki-nav="prev" style="padding:6px 16px;border-radius:999px;
|
||||
border:1.5px solid var(--c-border);background:var(--c-surface);cursor:pointer;
|
||||
font-size:var(--text-sm);${idx >= berichte.length-1 ? 'opacity:.3;pointer-events:none' : ''}">‹ Älter</button>
|
||||
<span class="text-xs-muted">${idx+1} / ${berichte.length}</span>
|
||||
<button onclick="window._kiNext()" style="padding:6px 16px;border-radius:999px;
|
||||
<button data-ki-nav="next" style="padding:6px 16px;border-radius:999px;
|
||||
border:1.5px solid var(--c-border);background:var(--c-surface);cursor:pointer;
|
||||
font-size:var(--text-sm);${idx <= 0 ? 'opacity:.3;pointer-events:none' : ''}">Neuer ›</button>
|
||||
</div>` : '';
|
||||
|
|
@ -2455,10 +2441,13 @@ window.Page_health = (() => {
|
|||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);text-align:center;margin-bottom:8px">${fmtDate(b)}</div>
|
||||
<div style="white-space:pre-wrap;line-height:1.7;font-size:var(--text-sm)">${UI.escape(b.bericht)}</div>`,
|
||||
});
|
||||
// Inline-onclick wird von der CSP blockiert → per addEventListener verdrahten.
|
||||
document.querySelector('[data-ki-nav="prev"]')
|
||||
?.addEventListener('click', () => { if (idx < berichte.length - 1) { idx++; showBericht(); } });
|
||||
document.querySelector('[data-ki-nav="next"]')
|
||||
?.addEventListener('click', () => { if (idx > 0) { idx--; showBericht(); } });
|
||||
}
|
||||
|
||||
window._kiPrev = () => { if (idx < berichte.length - 1) { idx++; showBericht(); } };
|
||||
window._kiNext = () => { if (idx > 0) { idx--; showBericht(); } };
|
||||
showBericht();
|
||||
});
|
||||
} catch (_) {
|
||||
|
|
@ -2620,15 +2609,13 @@ window.Page_health = (() => {
|
|||
${adresse ? `<div class="list-item-meta-row">${UI.escape(adresse)}</div>` : ''}
|
||||
${vet.telefon ? `
|
||||
<div class="mt-2">
|
||||
<a href="tel:${UI.escape(vet.telefon)}" class="btn btn-secondary btn-sm"
|
||||
onclick="event.stopPropagation()">
|
||||
<a href="tel:${UI.escape(vet.telefon)}" class="btn btn-secondary btn-sm">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> ${UI.escape(vet.telefon)}
|
||||
</a>
|
||||
</div>` : ''}
|
||||
${vet.notfall_telefon ? `
|
||||
<div style="margin-top:var(--space-1)">
|
||||
<a href="tel:${UI.escape(vet.notfall_telefon)}" class="btn btn-danger btn-sm"
|
||||
onclick="event.stopPropagation()">
|
||||
<a href="tel:${UI.escape(vet.notfall_telefon)}" class="btn btn-danger btn-sm">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Notfall: ${UI.escape(vet.notfall_telefon)}
|
||||
</a>
|
||||
</div>` : ''}
|
||||
|
|
@ -2718,14 +2705,13 @@ window.Page_health = (() => {
|
|||
${doc.beschreibung ? `<div class="list-item-text">${UI.escape(doc.beschreibung)}</div>` : ''}
|
||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2);flex-wrap:wrap">
|
||||
<a href="${UI.escape(doc.file_path)}" target="_blank" rel="noopener"
|
||||
class="btn btn-secondary btn-sm" onclick="event.stopPropagation()">
|
||||
class="btn btn-secondary btn-sm">
|
||||
${isImg
|
||||
? '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#image"></use></svg> Bild öffnen'
|
||||
: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF öffnen'}
|
||||
</a>
|
||||
<button class="btn btn-ghost btn-xs text-danger"
|
||||
data-action="delete-hdoc" data-doc-id="${doc.id}"
|
||||
onclick="event.stopPropagation()">
|
||||
data-action="delete-hdoc" data-doc-id="${doc.id}">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trash"></use></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -3007,7 +2993,7 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
|||
Bei ernsthaften oder sich verschlechternden Symptomen sofort zum Tierarzt.
|
||||
</div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<button class="btn btn-primary" id="ki-tierarzt-submit-btn">Frage stellen</button>`,
|
||||
});
|
||||
|
||||
|
|
@ -3233,7 +3219,7 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
|||
</div>
|
||||
</form>`;
|
||||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="ins-save-btn" form="${id}">Speichern</button>`;
|
||||
UI.modal.open({ title: existing ? 'Versicherung bearbeiten' : 'Versicherung eintragen', body, footer });
|
||||
setTimeout(() => {
|
||||
|
|
@ -3385,7 +3371,7 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
|||
</div>
|
||||
</form>`;
|
||||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="beh-save-btn" form="${id}">Speichern</button>`;
|
||||
UI.modal.open({ title: 'Verhalten erfassen', body, footer });
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@ window.Page_laeufi = (() => {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="laeufi-form" type="submit">${isEdit ? 'Speichern' : 'Eintragen'}</button>`,
|
||||
});
|
||||
document.getElementById('laeufi-form').addEventListener('submit', async e => {
|
||||
|
|
@ -472,7 +472,7 @@ window.Page_laeufi = (() => {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="deck-form" type="submit">${isEdit ? 'Speichern' : 'Eintragen'}</button>`,
|
||||
});
|
||||
document.getElementById('deck-form').addEventListener('submit', async e => {
|
||||
|
|
@ -505,7 +505,7 @@ window.Page_laeufi = (() => {
|
|||
title: `Progesterontests — ${_fmtDate(laeufi.beginn)}`,
|
||||
body: `<div id="prog-modal-content"><p class="text-muted">Lädt…</p></div>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Schließen</button>
|
||||
<button class="btn btn-primary" id="prog-add-btn">${UI.icon('plus')} Test eintragen</button>`,
|
||||
});
|
||||
await _loadProgContent(laeufi.id);
|
||||
|
|
@ -602,7 +602,7 @@ window.Page_laeufi = (() => {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="prog-form" type="submit">Eintragen</button>`,
|
||||
});
|
||||
document.getElementById('prog-form').addEventListener('submit', async e => {
|
||||
|
|
|
|||
|
|
@ -867,7 +867,7 @@ window.Page_litters = (() => {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="wl-form" type="submit">${isEdit ? 'Speichern' : 'Eintragen'}</button>`,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -410,7 +410,6 @@ window.Page_lost = (() => {
|
|||
<span style="font-size:10px;color:var(--c-warning,#d97706);font-weight:600">⏳ Sync ausstehend</span>
|
||||
<button class="btn btn-ghost btn-xs lost-discard-btn"
|
||||
data-pending-id="${r.id}"
|
||||
onclick="event.stopPropagation()"
|
||||
style="color:var(--c-danger,#dc2626)">
|
||||
🗑 Verwerfen
|
||||
</button>
|
||||
|
|
@ -419,7 +418,7 @@ window.Page_lost = (() => {
|
|||
<button class="btn btn-ghost btn-xs lost-note-btn"
|
||||
data-lost-note-id="${r.id}"
|
||||
data-lost-note-name="${UI.escape(r.name)}"
|
||||
title="Notiz" onclick="event.stopPropagation()">
|
||||
title="Notiz">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz
|
||||
</button>
|
||||
</div>` : '')}
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ window.Page_personality = (() => {
|
|||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||||
${typ.aktivitaeten.map(a => `
|
||||
<button class="btn btn-secondary" style="font-size:var(--text-xs);padding:6px 14px;border-radius:999px"
|
||||
onclick="App.navigate('${a.page}')">${a.label} →</button>`).join('')}
|
||||
data-page="${a.page}">${a.label} →</button>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -333,7 +333,7 @@ function _fmtDate(iso) {
|
|||
icon: UI.icon('paw-print'),
|
||||
title: 'Noch kein Hund',
|
||||
text: 'Lege zuerst einen Hund in deinem Profil an.',
|
||||
action: `<button class="btn btn-primary" onclick="App.navigate('dog-profile')">Hund anlegen</button>`,
|
||||
action: `<button class="btn btn-primary" data-page="dog-profile">Hund anlegen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ window.Page_poison = (() => {
|
|||
${_appState.user ? `<div style="margin-top:var(--space-2);text-align:right">
|
||||
<button class="btn btn-ghost btn-xs poison-note-btn"
|
||||
data-poison-note-id="${r.id}"
|
||||
title="Notiz" onclick="event.stopPropagation()">
|
||||
title="Notiz">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz
|
||||
</button>
|
||||
</div>` : ''}
|
||||
|
|
|
|||
|
|
@ -1946,7 +1946,7 @@ window.Page_routes = (() => {
|
|||
<textarea id="rk-nav-fb-text" class="form-control" rows="4"
|
||||
placeholder="z.B. Der Weg nach links ist gesperrt…" maxlength="500"></textarea>`;
|
||||
const footer = `
|
||||
<button type="button" class="btn btn-secondary flex-1" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button type="button" class="btn btn-secondary flex-1" data-modal-close>Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary flex-1" id="rk-nav-fb-send">${UI.icon('paper-plane-tilt')} Senden</button>`;
|
||||
UI.modal.open({ title: `${UI.icon('chat-circle-dots')} Feedback senden`, body, footer });
|
||||
document.getElementById('rk-nav-fb-send')?.addEventListener('click', async () => {
|
||||
|
|
@ -1987,7 +1987,7 @@ window.Page_routes = (() => {
|
|||
target="_blank" style="flex-shrink:0;margin-left:8px;color:var(--c-primary);font-size:12px">Navi</a>
|
||||
</div>`).join('')}
|
||||
</div>`).join('');
|
||||
UI.modal.open({ title: `${UI.icon('map-pin')} POIs entlang der Route`, body, footer: `<button class="btn btn-secondary flex-1" onclick="UI.modal.close()">Schließen</button>` });
|
||||
UI.modal.open({ title: `${UI.icon('map-pin')} POIs entlang der Route`, body, footer: `<button class="btn btn-secondary flex-1" data-modal-close>Schließen</button>` });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2299,7 +2299,7 @@ window.Page_routes = (() => {
|
|||
|
||||
const photoGallery = photos.length ? `
|
||||
<div class="rk-photo-gallery">
|
||||
${photos.map(u => `<img src="${UI.escape(u)}" class="rk-photo-thumb" onclick="window.open('${UI.escape(u)}','_blank')">`).join('')}
|
||||
${photos.map(u => `<img src="${UI.escape(u)}" class="rk-photo-thumb" data-open-url="${UI.escape(u)}">`).join('')}
|
||||
${isOwn ? `<label class="rk-photo-add" title="Foto hinzufügen">
|
||||
<span>+</span>
|
||||
<input type="file" id="rk-photo-input" accept="image/*" multiple class="hidden">
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ window.Page_settings = (() => {
|
|||
style="margin-top:2px;flex-shrink:0;accent-color:${color}">
|
||||
<span>
|
||||
Ich habe die <span style="color:var(--c-primary);cursor:pointer"
|
||||
onclick="App.navigate('agb')">AGB</span> gelesen und stimme ihnen zu.
|
||||
data-page="agb">AGB</span> gelesen und stimme ihnen zu.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -2396,7 +2396,7 @@ window.Page_settings = (() => {
|
|||
</div>
|
||||
</form>`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" form="${id}" type="submit">Link senden</button>`,
|
||||
});
|
||||
document.getElementById(id)?.addEventListener('submit', async e => {
|
||||
|
|
|
|||
|
|
@ -139,8 +139,7 @@ window.Page_sitting = (() => {
|
|||
${_state.user ? `<button class="btn-icon sit-note-btn"
|
||||
data-sit-note-id="${s.id}"
|
||||
data-sit-note-label="${UI.escHtml(s.sitter_name + ' ' + (s.datum || ''))}"
|
||||
title="Notiz" style="color:var(--c-text-muted);margin-top:var(--space-1)"
|
||||
onclick="event.stopPropagation()">
|
||||
title="Notiz" style="color:var(--c-text-muted);margin-top:var(--space-1)">
|
||||
${UI.icon('note-pencil')}</button>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -324,7 +323,7 @@ window.Page_sitting = (() => {
|
|||
</form>
|
||||
`;
|
||||
const footer = `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" type="submit" form="${id}" id="sit-anfrage-submit">${UI.icon('paper-plane-tilt')} Anfrage senden</button>
|
||||
`;
|
||||
UI.modal.open({ title: 'Anfrage senden', body, footer });
|
||||
|
|
@ -410,7 +409,7 @@ window.Page_sitting = (() => {
|
|||
<button class="btn btn-primary" type="submit" form="${id}" id="sit-profil-submit" class="w-full">
|
||||
${s ? `${UI.icon('floppy-disk')} Speichern` : `${UI.icon('plus')} Profil erstellen`}
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
</div>
|
||||
`;
|
||||
UI.modal.open({ title: s ? 'Sitter-Profil bearbeiten' : 'Sitter-Profil erstellen', body, footer });
|
||||
|
|
|
|||
|
|
@ -351,8 +351,7 @@ window.Page_walks = (() => {
|
|||
data-wk-note-id="${w.id}"
|
||||
data-wk-note-label="${UI.escape(w.titel + ' ' + w.datum)}"
|
||||
data-wk-note-ort="${UI.escape(w.ort_name || '')}"
|
||||
title="Notiz" style="color:var(--c-text-muted);font-size:var(--text-xs)"
|
||||
onclick="event.stopPropagation()">
|
||||
title="Notiz" style="color:var(--c-text-muted);font-size:var(--text-xs)">
|
||||
${UI.icon('note-pencil')}</button>` : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
|
|
@ -554,7 +553,7 @@ window.Page_walks = (() => {
|
|||
: (walk.photos || []).map(p => `
|
||||
<div style="position:relative;aspect-ratio:1">
|
||||
<img src="${UI.escape(p.url)}" style="width:100%;height:100%;object-fit:cover;border-radius:var(--radius-sm);cursor:pointer"
|
||||
onclick="UI.lightbox?.show?.([{ url:'${UI.escape(p.url)}' }], 0)">
|
||||
data-lightbox-url="${UI.escape(p.url)}">
|
||||
${p.user_id === _appState.user?.id || isOwn ? `
|
||||
<button type="button" class="wd-photo-del" data-photo-id="${p.id}"
|
||||
style="position:absolute;top:3px;right:3px;background:rgba(0,0,0,.6);color:#fff;border:none;border-radius:50%;width:20px;height:20px;cursor:pointer;font-size:11px;display:flex;align-items:center;justify-content:center;padding:0">✕</button>
|
||||
|
|
@ -637,7 +636,7 @@ window.Page_walks = (() => {
|
|||
div.style.cssText = 'position:relative;aspect-ratio:1';
|
||||
div.innerHTML = `
|
||||
<img src="${UI.escape(photo.url)}" style="width:100%;height:100%;object-fit:cover;border-radius:var(--radius-sm);cursor:pointer"
|
||||
onclick="UI.lightbox?.show?.([{ url:'${UI.escape(photo.url)}' }], 0)">
|
||||
data-lightbox-url="${UI.escape(photo.url)}">
|
||||
<button type="button" class="wd-photo-del" data-photo-id="${photo.id}"
|
||||
style="position:absolute;top:3px;right:3px;background:rgba(0,0,0,.6);color:#fff;border:none;border-radius:50%;width:20px;height:20px;cursor:pointer;font-size:11px;display:flex;align-items:center;justify-content:center;padding:0">✕</button>`;
|
||||
grid.appendChild(div);
|
||||
|
|
@ -1108,7 +1107,7 @@ window.Page_walks = (() => {
|
|||
<div class="challenge-sub-card">
|
||||
<img src="${UI.escape(s.foto_url)}" alt="Challenge-Foto" loading="lazy"
|
||||
onerror="this.src='/icons/icon-192.png'"
|
||||
onclick="UI.lightbox?.show?.([{ url:'${UI.escape(s.foto_url)}' }], 0)">
|
||||
data-lightbox-url="${UI.escape(s.foto_url)}">
|
||||
<div class="challenge-sub-info">
|
||||
<div class="challenge-sub-user">${UI.icon('user')} ${UI.escape(s.user_name || 'Anonym')}
|
||||
${s.dog_name ? ` · 🐕 ${UI.escape(s.dog_name)}` : ''}</div>
|
||||
|
|
@ -1169,7 +1168,7 @@ window.Page_walks = (() => {
|
|||
</form>
|
||||
`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="challenge-submit-ok">Einreichen</button>
|
||||
`,
|
||||
});
|
||||
|
|
@ -1366,7 +1365,7 @@ window.Page_walks = (() => {
|
|||
</form>
|
||||
`,
|
||||
footer: `
|
||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
||||
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||
<button class="btn btn-primary" id="gz-save-btn">Speichern</button>
|
||||
`,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ window.Page_widget = (() => {
|
|||
icon: UI.icon('dog'),
|
||||
title: 'Kein Hund angelegt',
|
||||
text: 'Erstelle zuerst ein Hundeprofil.',
|
||||
action: `<button class="btn btn-primary" onclick="App.navigate('dog-profile')">Profil erstellen</button>`,
|
||||
action: `<button class="btn btn-primary" data-page="dog-profile">Profil erstellen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,19 @@ window.Page_wiki = (() => {
|
|||
async function init(container, appState) {
|
||||
_container = container;
|
||||
_appState = appState;
|
||||
|
||||
// Delegierter Click-Handler — Inline-onclick wird von der CSP blockiert.
|
||||
if (!_container._wikiClickBound) {
|
||||
_container.addEventListener('click', e => {
|
||||
const btn = e.target.closest('[data-wiki-action]');
|
||||
if (!btn) return;
|
||||
const id = parseInt(btn.dataset.wikiId, 10);
|
||||
if (btn.dataset.wikiAction === 'approve') _approveSubmission(id);
|
||||
else if (btn.dataset.wikiAction === 'reject') _rejectSubmission(id);
|
||||
});
|
||||
_container._wikiClickBound = true;
|
||||
}
|
||||
|
||||
await _render();
|
||||
}
|
||||
|
||||
|
|
@ -180,11 +193,11 @@ window.Page_wiki = (() => {
|
|||
</div>
|
||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
||||
<button class="btn btn-primary btn-sm flex-1"
|
||||
onclick="Page_wiki._approveSubmission(${s.id})">
|
||||
data-wiki-action="approve" data-wiki-id="${s.id}">
|
||||
${UI.icon('check')} Freischalten
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm flex-1"
|
||||
onclick="Page_wiki._rejectSubmission(${s.id})">
|
||||
data-wiki-action="reject" data-wiki-id="${s.id}">
|
||||
${UI.icon('x')} Ablehnen
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -1400,7 +1413,7 @@ window.Page_wiki = (() => {
|
|||
</p>
|
||||
${data.hinweis ? `<p style="font-size:var(--text-sm);color:var(--c-text-muted);margin-top:var(--space-3)">${UI.escape(data.hinweis)}</p>` : ''}
|
||||
</div>`,
|
||||
footer: `<button class="btn btn-secondary" onclick="UI.modal.close()">Schließen</button>`,
|
||||
footer: `<button class="btn btn-secondary" data-modal-close>Schließen</button>`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,8 +118,9 @@ window.Page_zucht_profil = (() => {
|
|||
<div style="text-align:center;padding:var(--space-10) var(--space-4)">
|
||||
<div style="font-size:3rem;margin-bottom:var(--space-3)">${UI.icon('warning')}</div>
|
||||
<p class="text-danger">${UI.escape(err.message || 'Fehler beim Laden.')}</p>
|
||||
<button class="btn btn-secondary" onclick="history.back()">Zurück</button>
|
||||
<button class="btn btn-secondary" id="zp-back-btn">Zurück</button>
|
||||
</div>`;
|
||||
_container.querySelector('#zp-back-btn')?.addEventListener('click', () => history.back());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
11
backend/static/js/presse.js
Normal file
11
backend/static/js/presse.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// Presse-Seite — ausgelagert, da Inline-Script/onclick von der CSP blockiert wird.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const btn = document.querySelector('.copy-btn');
|
||||
btn?.addEventListener('click', () => {
|
||||
const text = document.getElementById('boilerplate-text').innerText.replace('Kopieren', '').trim();
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
btn.textContent = 'Kopiert ✓';
|
||||
setTimeout(() => btn.textContent = 'Kopieren', 2000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1399,8 +1399,9 @@ window.Worlds = (() => {
|
|||
<div style="font-size:4rem;margin-bottom:12px">🐾</div>
|
||||
<div class="world-info-title">Dein Hund wartet</div>
|
||||
<div class="world-info-sub" style="margin-bottom:20px">Melde dich an um loszulegen</div>
|
||||
<button class="btn btn-primary" onclick="Worlds.navigateTo('settings')">Anmelden</button>
|
||||
<button class="btn btn-primary" id="world-login-btn">Anmelden</button>
|
||||
</div>`;
|
||||
el.querySelector('#world-login-btn')?.addEventListener('click', () => navigateTo('settings'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
6
backend/static/js/zuechter.js
Normal file
6
backend/static/js/zuechter.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Züchter-Landingpage — ausgelagert, da Inline-onclick von der CSP blockiert wird.
|
||||
// Verhindert Redirect-Loop beim Öffnen der App aus der Landingpage.
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('[data-stay-in-app]').forEach(el =>
|
||||
el.addEventListener('click', () => sessionStorage.setItem('by_stay_in_app', '1')));
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<script src="/js/landing-init.js?v=1163"></script>
|
||||
<script src="/js/landing-init.js?v=1164"></script>
|
||||
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
||||
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
||||
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@
|
|||
<section>
|
||||
<div class="section-label">Über Ban Yaro — Kurztext für Redaktionen</div>
|
||||
<div class="boilerplate" id="boilerplate-text">
|
||||
<button class="copy-btn" onclick="copyBoilerplate()">Kopieren</button>
|
||||
<button class="copy-btn">Kopieren</button>
|
||||
<p>Ban Yaro ist eine kostenlose Hunde-App für den deutschsprachigen Raum. Die App läuft als Progressive Web App direkt im Smartphone-Browser — ohne Installation über den App Store. Funktionen: Hunde-Tagebuch mit Fotos und Wetter, digitale Gesundheitsakte, interaktive Karte mit Hundewiesen und Giftköder-Alarm, Community-Forum und Trainingspläne. Gegründet 2024 von René Degelmann, Ebersberg bei München. Erreichbar unter banyaro.app.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -386,16 +386,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyBoilerplate() {
|
||||
const text = document.getElementById('boilerplate-text').innerText.replace('Kopieren', '').trim();
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const btn = document.querySelector('.copy-btn');
|
||||
btn.textContent = 'Kopiert ✓';
|
||||
setTimeout(() => btn.textContent = 'Kopieren', 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="/js/presse.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
============================================================ */
|
||||
|
||||
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
||||
const VER = '1163';
|
||||
const VER = '1164';
|
||||
const CACHE_VERSION = `by-v${VER}`;
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@
|
|||
<span class="badge">VDH-kompatibel</span>
|
||||
<span class="badge">Kein App Store nötig</span>
|
||||
</div>
|
||||
<a href="/#register?rolle=breeder" class="cta-btn" onclick="sessionStorage.setItem('by_stay_in_app','1')">Jetzt als Züchter registrieren</a>
|
||||
<a href="/#register?rolle=breeder" class="cta-btn" data-stay-in-app>Jetzt als Züchter registrieren</a>
|
||||
<a href="#funktionen" class="cta-btn-secondary">Alle Features ansehen ↓</a>
|
||||
</div>
|
||||
</header>
|
||||
|
|
@ -217,8 +217,8 @@
|
|||
<a href="#vergleich">Vergleich</a>
|
||||
<a href="#vorteil">Alleinstellung</a>
|
||||
<a href="#start">Loslegen</a>
|
||||
<a href="/" onclick="sessionStorage.setItem('by_stay_in_app','1')">Zur App</a>
|
||||
<a href="/#register?rolle=breeder" class="nav-cta" onclick="sessionStorage.setItem('by_stay_in_app','1')">Registrieren</a>
|
||||
<a href="/" data-stay-in-app>Zur App</a>
|
||||
<a href="/#register?rolle=breeder" class="nav-cta" data-stay-in-app>Registrieren</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
|
@ -580,7 +580,7 @@
|
|||
<p style="font-size:.9rem;opacity:.7;margin-bottom:1.75rem">
|
||||
39 €/Jahr für die ersten 20 Gründer-Züchter · danach 49 €/Jahr · kein App Store · keine Kreditkarte zum Start
|
||||
</p>
|
||||
<a href="/#register?rolle=breeder" class="cta-btn" onclick="sessionStorage.setItem('by_stay_in_app','1')">Als Züchter registrieren</a>
|
||||
<a href="/#register?rolle=breeder" class="cta-btn" data-stay-in-app>Als Züchter registrieren</a>
|
||||
<p style="margin-top:1.5rem;font-size:0.85rem;opacity:0.55">Fragen? <a href="mailto:hallo@banyaro.app" style="color:rgba(255,255,255,.7)">hallo@banyaro.app</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -599,5 +599,6 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/js/zuechter.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue