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>
|
<title>Ban Yaro</title>
|
||||||
|
|
||||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
<!-- 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 -->
|
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||||
<link rel="stylesheet" href="/css/design-system.css?v=1163">
|
<link rel="stylesheet" href="/css/design-system.css?v=1164">
|
||||||
<link rel="stylesheet" href="/css/layout.css?v=1163">
|
<link rel="stylesheet" href="/css/layout.css?v=1164">
|
||||||
<link rel="stylesheet" href="/css/components.css?v=1163">
|
<link rel="stylesheet" href="/css/components.css?v=1164">
|
||||||
<link rel="stylesheet" href="/css/utilities.css?v=1163">
|
<link rel="stylesheet" href="/css/utilities.css?v=1164">
|
||||||
<link rel="stylesheet" href="/css/lists.css?v=1163">
|
<link rel="stylesheet" href="/css/lists.css?v=1164">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
@ -617,11 +617,11 @@
|
||||||
<div id="modal-container"></div>
|
<div id="modal-container"></div>
|
||||||
|
|
||||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||||
<script src="/js/api.js?v=1163"></script>
|
<script src="/js/api.js?v=1164"></script>
|
||||||
<script src="/js/ui.js?v=1163"></script>
|
<script src="/js/ui.js?v=1164"></script>
|
||||||
<script src="/js/app.js?v=1163"></script>
|
<script src="/js/app.js?v=1164"></script>
|
||||||
<script src="/js/worlds.js?v=1163"></script>
|
<script src="/js/worlds.js?v=1164"></script>
|
||||||
<script src="/js/offline-indicator.js?v=1163"></script>
|
<script src="/js/offline-indicator.js?v=1164"></script>
|
||||||
|
|
||||||
<!-- Feature-Seiten werden lazy geladen -->
|
<!-- Feature-Seiten werden lazy geladen -->
|
||||||
|
|
||||||
|
|
@ -631,7 +631,7 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
<!-- 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>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
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
|
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_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||||
window.APP_VERSION = APP_VERSION;
|
window.APP_VERSION = APP_VERSION;
|
||||||
|
|
@ -443,6 +443,20 @@ const App = (() => {
|
||||||
return;
|
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
|
// Header-User-Button → Settings
|
||||||
if (e.target.closest('#header-user-btn')) {
|
if (e.target.closest('#header-user-btn')) {
|
||||||
navigate('settings');
|
navigate('settings');
|
||||||
|
|
@ -1164,7 +1178,7 @@ const App = (() => {
|
||||||
icon: UI.icon(icon),
|
icon: UI.icon(icon),
|
||||||
title: 'Anmelden erforderlich',
|
title: 'Anmelden erforderlich',
|
||||||
text,
|
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);
|
background:var(--c-surface-2);border-radius:var(--radius-md);
|
||||||
padding:var(--space-3);max-height:60vh;overflow-y:auto;
|
padding:var(--space-3);max-height:60vh;overflow-y:auto;
|
||||||
color:var(--c-text)">${UI.escape(l.body || '(kein Text gespeichert)')}</pre>`,
|
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>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" form="${id}" type="submit">Speichern</button>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2993,7 +2993,7 @@ window.Page_admin = (() => {
|
||||||
</div>
|
</div>
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" id="adm-bew-save-note">Notiz speichern</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById('adm-bew-save-note')?.addEventListener('click', async () => {
|
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">
|
<button type="submit" form="adp-interest-form" class="btn btn-primary flex-1" id="adp-interest-submit">
|
||||||
${UI.icon('heart')} Interesse bekunden
|
${UI.icon('heart')} Interesse bekunden
|
||||||
</button>
|
</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>
|
</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">
|
<button type="submit" form="adp-create-form" class="btn btn-primary w-full" id="adp-create-submit">
|
||||||
${UI.icon('plus')} Inserat erstellen
|
${UI.icon('plus')} Inserat erstellen
|
||||||
</button>
|
</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>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,23 @@ window.Page_chat = (() => {
|
||||||
_container = container;
|
_container = container;
|
||||||
_myId = appState?.user?.id || null;
|
_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
|
// Heartbeat: alle 30s online-Status senden
|
||||||
API.chat.heartbeat().catch(() => {});
|
API.chat.heartbeat().catch(() => {});
|
||||||
_heartbeatTimer = setInterval(() => {
|
_heartbeatTimer = setInterval(() => {
|
||||||
|
|
@ -132,7 +149,7 @@ window.Page_chat = (() => {
|
||||||
? `<span class="online-dot" title="Online"></span>`
|
? `<span class="online-dot" title="Online"></span>`
|
||||||
: '';
|
: '';
|
||||||
return `
|
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 style="position:relative;flex-shrink:0">
|
||||||
<div class="chat-conv-avatar">${initials}</div>
|
<div class="chat-conv-avatar">${initials}</div>
|
||||||
${onlineDot ? `<span class="online-dot chat-avatar-dot"></span>` : ''}
|
${onlineDot ? `<span class="online-dot chat-avatar-dot"></span>` : ''}
|
||||||
|
|
@ -166,14 +183,14 @@ window.Page_chat = (() => {
|
||||||
|
|
||||||
// Aktive Markierung in der Liste
|
// Aktive Markierung in der Liste
|
||||||
document.querySelectorAll('.chat-conv-item').forEach(el =>
|
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 = `
|
const threadHTML = `
|
||||||
<div class="chat-thread" id="chat-thread">
|
<div class="chat-thread" id="chat-thread">
|
||||||
<div class="chat-thread-header">
|
<div class="chat-thread-header">
|
||||||
${_isDesktop() ? '' : `
|
${_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>
|
<svg class="ph-icon"><use href="/icons/phosphor.svg#arrow-left"></use></svg>
|
||||||
</button>`}
|
</button>`}
|
||||||
<div style="position:relative;flex-shrink:0">
|
<div style="position:relative;flex-shrink:0">
|
||||||
|
|
@ -188,14 +205,13 @@ window.Page_chat = (() => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-input-bar">
|
<div class="chat-input-bar">
|
||||||
<input type="file" id="chat-photo-input" accept="image/*" class="hidden"
|
<input type="file" id="chat-photo-input" accept="image/*" class="hidden">
|
||||||
onchange="Page_chat._onPhotoSelected(this)">
|
<button class="chat-photo-btn" data-chat-action="photo" title="Foto senden">
|
||||||
<button class="chat-photo-btn" onclick="document.getElementById('chat-photo-input').click()" title="Foto senden">
|
|
||||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#camera"></use></svg>
|
<svg class="ph-icon"><use href="/icons/phosphor.svg#camera"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
<textarea id="chat-input" class="chat-input" rows="1"
|
<textarea id="chat-input" class="chat-input" rows="1"
|
||||||
placeholder="Nachricht…" maxlength="2000"></textarea>
|
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>
|
<svg class="ph-icon"><use href="/icons/phosphor.svg#paper-plane-tilt"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -219,9 +235,11 @@ window.Page_chat = (() => {
|
||||||
input.addEventListener('keydown', e => {
|
input.addEventListener('keydown', e => {
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Page_chat._send();
|
_send();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
document.getElementById('chat-photo-input')
|
||||||
|
?.addEventListener('change', e => _onPhotoSelected(e.target));
|
||||||
|
|
||||||
await _loadMessages(true);
|
await _loadMessages(true);
|
||||||
await API.chat.markRead(_convId).catch(() => {});
|
await API.chat.markRead(_convId).catch(() => {});
|
||||||
|
|
@ -313,7 +331,7 @@ window.Page_chat = (() => {
|
||||||
const timeStr = _fmtTime(m.created_at);
|
const timeStr = _fmtTime(m.created_at);
|
||||||
const deleteBtn = isMine && !m.is_deleted
|
const deleteBtn = isMine && !m.is_deleted
|
||||||
? `<button class="btn btn-ghost" style="padding:2px;opacity:0.4;font-size:var(--text-xs)"
|
? `<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>
|
<svg class="ph-icon" style="width:12px;height:12px"><use href="/icons/phosphor.svg#trash"></use></svg>
|
||||||
</button>`
|
</button>`
|
||||||
: '';
|
: '';
|
||||||
|
|
@ -328,7 +346,7 @@ window.Page_chat = (() => {
|
||||||
// Medieninhalt
|
// Medieninhalt
|
||||||
let bubbleContent = '';
|
let bubbleContent = '';
|
||||||
if (m.media_url) {
|
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) {
|
if (m.text) {
|
||||||
bubbleContent += (m.media_url ? `<div style="margin-top:var(--space-1)">` : '') +
|
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>
|
<span class="diary-detail-date-center">${datumLang}</span>
|
||||||
<div style="display:flex;align-items:center;gap:4px">
|
<div style="display:flex;align-items:center;gap:4px">
|
||||||
${!_appState?.activeDog?.is_guest
|
${!_appState?.activeDog?.is_guest
|
||||||
? `<button id="diary-dv-note" class="btn btn-ghost btn-xs" title="Notiz"
|
? `<button id="diary-dv-note" class="btn btn-ghost btn-xs" title="Notiz">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
<button id="diary-dv-edit" class="diary-detail-edit">
|
<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>`,
|
<div id="import-result" style="display:none;margin-top:var(--space-4)"></div>`,
|
||||||
|
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" id="import-start-btn">Importieren</button>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -616,7 +616,7 @@ window.Page_dog_profile = (() => {
|
||||||
footer: `
|
footer: `
|
||||||
<div class="w3-btn-stack">
|
<div class="w3-btn-stack">
|
||||||
<button class="btn btn-primary" id="chip-edit-save-btn" class="w-full">Speichern</button>
|
<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>`,
|
</div>`,
|
||||||
});
|
});
|
||||||
document.getElementById('chip-edit-save-btn').addEventListener('click', async () => {
|
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>` : ''}
|
${hasPhoto ? `<button class="btn btn-primary" id="pe-save-btn" class="w-full">Speichern</button>` : ''}
|
||||||
<div class="flex-gap-2">
|
<div class="flex-gap-2">
|
||||||
${hasPhoto ? `<button class="btn btn-danger" id="pe-delete-btn">${UI.icon('trash')} Löschen</button>` : ''}
|
${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>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -957,7 +957,7 @@ window.Page_dog_profile = (() => {
|
||||||
</div>
|
</div>
|
||||||
<div id="share-list-wrap" class="mt-4"></div>`,
|
<div id="share-list-wrap" class="mt-4"></div>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" id="share-create-btn">Link erstellen</button>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1489,7 +1489,7 @@ window.Page_dog_profile = (() => {
|
||||||
</p>
|
</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>` : ''}
|
${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>`,
|
</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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1608,7 +1608,7 @@ window.Page_dog_profile = (() => {
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
footer: `
|
||||||
<div style="display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:flex-end">
|
<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">
|
<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>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#identification-card"></use></svg>
|
||||||
Ausweis öffnen
|
Ausweis öffnen
|
||||||
|
|
@ -1832,7 +1832,7 @@ window.Page_dog_profile = (() => {
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
footer: `
|
||||||
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
|
<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>
|
<button class="btn btn-primary" id="pp-meta-save">Speichern</button>
|
||||||
</div>`,
|
</div>`,
|
||||||
});
|
});
|
||||||
|
|
@ -1896,7 +1896,7 @@ window.Page_dog_profile = (() => {
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
footer: `
|
||||||
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
|
<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>
|
<button class="btn btn-primary" id="pp-vacc-save">Speichern</button>
|
||||||
</div>`,
|
</div>`,
|
||||||
});
|
});
|
||||||
|
|
@ -1960,7 +1960,7 @@ window.Page_dog_profile = (() => {
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
footer: `
|
||||||
<div style="display:flex;gap:var(--space-2);justify-content:flex-end">
|
<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>
|
<button class="btn btn-primary" id="pp-med-save">Speichern</button>
|
||||||
</div>`,
|
</div>`,
|
||||||
});
|
});
|
||||||
|
|
@ -2017,7 +2017,7 @@ window.Page_dog_profile = (() => {
|
||||||
UI.modal.open({
|
UI.modal.open({
|
||||||
title: 'Hundepass-Link teilen',
|
title: 'Hundepass-Link teilen',
|
||||||
body: shareWrap.innerHTML,
|
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 () => {
|
document.getElementById('pp-sharelink-copy')?.addEventListener('click', async () => {
|
||||||
await navigator.clipboard.writeText(url).catch(() => {});
|
await navigator.clipboard.writeText(url).catch(() => {});
|
||||||
|
|
@ -2209,7 +2209,7 @@ window.Page_dog_profile = (() => {
|
||||||
? 'background:#7a4f1a;color:#f5e4c0;border-color:#7a4f1a;'
|
? 'background:#7a4f1a;color:#f5e4c0;border-color:#7a4f1a;'
|
||||||
: 'background:#f5f0e8;color:#444;border-color:#e0d4b8;';
|
: 'background:#f5f0e8;color:#444;border-color:#e0d4b8;';
|
||||||
const label = y === 'alle' ? 'Alle' : y;
|
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;
|
border:1px solid;border-radius:8px;padding:8px 16px;
|
||||||
font-size:0.9rem;cursor:pointer;font-family:inherit;
|
font-size:0.9rem;cursor:pointer;font-family:inherit;
|
||||||
${active}
|
${active}
|
||||||
|
|
@ -2239,14 +2239,14 @@ window.Page_dog_profile = (() => {
|
||||||
|
|
||||||
<div style="margin-bottom:20px;display:flex;flex-direction:column;gap:10px">
|
<div style="margin-bottom:20px;display:flex;flex-direction:column;gap:10px">
|
||||||
<label style="display:flex;align-items:center;gap:12px;cursor:pointer">
|
<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;
|
width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;
|
||||||
${togStyle(nurFotos)}
|
${togStyle(nurFotos)}
|
||||||
">${nurFotos ? '✓' : ''}</button>
|
">${nurFotos ? '✓' : ''}</button>
|
||||||
<span style="font-size:0.95rem">Nur Einträge mit Fotos</span>
|
<span style="font-size:0.95rem">Nur Einträge mit Fotos</span>
|
||||||
</label>
|
</label>
|
||||||
<label style="display:flex;align-items:center;gap:12px;cursor:pointer">
|
<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;
|
width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;
|
||||||
${togStyle(nurMeilensteine)}
|
${togStyle(nurMeilensteine)}
|
||||||
">${nurMeilensteine ? '✓' : ''}</button>
|
">${nurMeilensteine ? '✓' : ''}</button>
|
||||||
|
|
@ -2255,11 +2255,11 @@ window.Page_dog_profile = (() => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display:flex;gap:10px">
|
<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;
|
flex:1;background:#7a4f1a;color:#f5e4c0;border:none;border-radius:10px;
|
||||||
padding:14px;font-size:1rem;font-weight:700;cursor:pointer;font-family:inherit;
|
padding:14px;font-size:1rem;font-weight:700;cursor:pointer;font-family:inherit;
|
||||||
">📖 Buch öffnen</button>
|
">📖 Buch öffnen</button>
|
||||||
<button onclick="window._buchClose()" style="
|
<button data-buch-action="close" style="
|
||||||
background:#f0f0f0;color:#555;border:none;border-radius:10px;
|
background:#f0f0f0;color:#555;border:none;border-radius:10px;
|
||||||
padding:14px 18px;font-size:1rem;cursor:pointer;font-family:inherit;
|
padding:14px 18px;font-size:1rem;cursor:pointer;font-family:inherit;
|
||||||
">✕</button>
|
">✕</button>
|
||||||
|
|
@ -2268,18 +2268,14 @@ window.Page_dog_profile = (() => {
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
window._buchSetJahr = (y) => { selectedJahr = y; renderModal(); };
|
const setJahr = (y) => { selectedJahr = y; renderModal(); };
|
||||||
window._buchToggleFotos = () => { nurFotos = !nurFotos; renderModal(); };
|
const toggleFotos = () => { nurFotos = !nurFotos; renderModal(); };
|
||||||
window._buchToggleMeilensteine = () => { nurMeilensteine = !nurMeilensteine; renderModal(); };
|
const toggleMeilen = () => { nurMeilensteine = !nurMeilensteine; renderModal(); };
|
||||||
window._buchClose = () => {
|
const closeModal = () => {
|
||||||
modalEl.remove();
|
modalEl.remove();
|
||||||
delete window._buchSetJahr;
|
document.removeEventListener('keydown', onKey);
|
||||||
delete window._buchToggleFotos;
|
|
||||||
delete window._buchToggleMeilensteine;
|
|
||||||
delete window._buchOpen;
|
|
||||||
delete window._buchClose;
|
|
||||||
};
|
};
|
||||||
window._buchOpen = () => {
|
const openBuch = () => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (selectedJahr !== 'alle') params.set('jahr', selectedJahr);
|
if (selectedJahr !== 'alle') params.set('jahr', selectedJahr);
|
||||||
if (nurFotos) params.set('nur_fotos', 'true');
|
if (nurFotos) params.set('nur_fotos', 'true');
|
||||||
|
|
@ -2290,10 +2286,24 @@ window.Page_dog_profile = (() => {
|
||||||
|
|
||||||
renderModal();
|
renderModal();
|
||||||
document.body.appendChild(modalEl);
|
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 => {
|
const onKey = e => {
|
||||||
if (e.key === 'Escape') { window._buchClose(); document.removeEventListener('keydown', onKey); }
|
if (e.key === 'Escape') { closeModal(); }
|
||||||
};
|
};
|
||||||
document.addEventListener('keydown', onKey);
|
document.addEventListener('keydown', onKey);
|
||||||
}
|
}
|
||||||
|
|
@ -2310,7 +2320,7 @@ window.Page_dog_profile = (() => {
|
||||||
<use href="/icons/phosphor.svg#spinner-gap"></use>
|
<use href="/icons/phosphor.svg#spinner-gap"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</div>`,
|
</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',
|
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>',
|
icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bowl-food"></use></svg>',
|
||||||
title: 'Noch kein Hund angelegt',
|
title: 'Noch kein Hund angelegt',
|
||||||
text: 'Erstelle zuerst ein Hundeprofil.',
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -728,7 +728,7 @@ window.Page_ernaehrung = (() => {
|
||||||
</form>
|
</form>
|
||||||
`;
|
`;
|
||||||
const footer = `
|
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>
|
<button class="btn btn-primary" id="vert-futter-save-btn" form="${id}">Speichern</button>
|
||||||
`;
|
`;
|
||||||
UI.modal.open({ title: 'Futter erfassen', body, footer });
|
UI.modal.open({ title: 'Futter erfassen', body, footer });
|
||||||
|
|
@ -840,7 +840,7 @@ window.Page_ernaehrung = (() => {
|
||||||
</form>
|
</form>
|
||||||
`;
|
`;
|
||||||
const footer = `
|
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>
|
<button class="btn btn-primary" id="vert-reaktion-save-btn" form="${id}">Speichern</button>
|
||||||
`;
|
`;
|
||||||
UI.modal.open({ title: 'Reaktion erfassen', body, footer });
|
UI.modal.open({ title: 'Reaktion erfassen', body, footer });
|
||||||
|
|
|
||||||
|
|
@ -221,17 +221,17 @@ window.Page_events = (() => {
|
||||||
</div>
|
</div>
|
||||||
${ev.rsvp_count ? `<span class="event-attendees" data-ev-attendees="${ev.id}">${_icon('users')} ${ev.rsvp_count} nehmen teil</span>` : ''}
|
${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">
|
${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
|
${_icon('arrow-square-out')} Details
|
||||||
</a>
|
</a>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-1)">
|
<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}"
|
${_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-label="${UI.escape(ev.titel + ' ' + ev.datum)}"
|
||||||
data-ev-note-ort="${UI.escape(ev.ort_name || '')}"
|
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>` : ''}
|
${_icon('note-pencil')}</button>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -276,7 +276,7 @@ window.Page_events = (() => {
|
||||||
<span style="color:var(--c-text-muted);font-size:12px">${datum}</span><br>
|
<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.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>` : ''}
|
${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>
|
style="font-size:12px;color:var(--c-primary,#2563eb)">Details</a>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -512,7 +512,7 @@ window.Page_events = (() => {
|
||||||
</button>
|
</button>
|
||||||
<div class="flex-gap-2">
|
<div class="flex-gap-2">
|
||||||
${isEdit ? `<button type="button" class="btn btn-danger" id="ev-form-delete">Löschen</button>` : ''}
|
${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>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -570,6 +570,14 @@ window.Page_events = (() => {
|
||||||
// Click-Handler
|
// Click-Handler
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
function _onClick(e) {
|
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
|
// Quelle-Filter
|
||||||
const sourceBtn = e.target.closest('[data-ev-quelle]');
|
const sourceBtn = e.target.closest('[data-ev-quelle]');
|
||||||
if (sourceBtn) {
|
if (sourceBtn) {
|
||||||
|
|
|
||||||
|
|
@ -485,7 +485,7 @@ window.Page_expenses = (() => {
|
||||||
</form>`;
|
</form>`;
|
||||||
|
|
||||||
const footer = `
|
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>`;
|
<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 });
|
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">
|
style="color:var(--c-danger);margin-right:auto">
|
||||||
${UI.icon('trash')}
|
${UI.icon('trash')}
|
||||||
</button>
|
</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="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>
|
<button type="submit" form="${formId}" class="btn btn-primary flex-1">Speichern</button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,7 @@ function _fmtDate(iso) {
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
UI.modal.open({ title: '🏆 Hund des Monats', body,
|
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 => {
|
document.getElementById('hdm-login-link')?.addEventListener('click', e => {
|
||||||
e.preventDefault(); UI.modal.close(); App.navigate('settings');
|
e.preventDefault(); UI.modal.close(); App.navigate('settings');
|
||||||
|
|
@ -1022,7 +1022,7 @@ function _fmtDate(iso) {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>`,
|
</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>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<button type="submit" form="${id}" class="btn btn-primary flex-1">${UI.icon('floppy-disk')} Speichern</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById(id)?.addEventListener('submit', async e => {
|
document.getElementById(id)?.addEventListener('submit', async e => {
|
||||||
|
|
@ -1423,7 +1423,7 @@ function _fmtDate(iso) {
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<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'),
|
icon: UI.icon('users'),
|
||||||
title: 'Anmelden erforderlich',
|
title: 'Anmelden erforderlich',
|
||||||
text: 'Melde dich an, um Freunde zu finden und Anfragen zu verwalten.',
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -524,8 +524,7 @@ window.Page_friends = (() => {
|
||||||
<button class="btn btn-ghost btn-sm fr-note-btn"
|
<button class="btn btn-ghost btn-sm fr-note-btn"
|
||||||
data-fr-note-id="${f.friend_id}"
|
data-fr-note-id="${f.friend_id}"
|
||||||
data-fr-note-name="${UI.escape(f.friend_name)}"
|
data-fr-note-name="${UI.escape(f.friend_name)}"
|
||||||
title="Notiz"
|
title="Notiz">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
<svg class="ph-icon"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-ghost btn-sm"
|
<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>',
|
icon: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#syringe"></use></svg>',
|
||||||
title: 'Noch kein Hund angelegt',
|
title: 'Noch kein Hund angelegt',
|
||||||
text: 'Erstelle zuerst ein Hundeprofil.',
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -403,8 +403,7 @@ window.Page_health = (() => {
|
||||||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="${UI.escape(e.bezeichnung)}"
|
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -511,8 +510,7 @@ window.Page_health = (() => {
|
||||||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="${UI.escape(e.bezeichnung)}"
|
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
</div>
|
</div>
|
||||||
</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>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="Gewicht ${UI.escape(e.datum)}"
|
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>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
@ -801,8 +798,7 @@ window.Page_health = (() => {
|
||||||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="Läufigkeit ${UI.escape(e.datum)}"
|
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>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
@ -839,8 +835,7 @@ window.Page_health = (() => {
|
||||||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="${UI.escape(e.bezeichnung)}"
|
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
|
|
@ -880,8 +875,7 @@ window.Page_health = (() => {
|
||||||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="${UI.escape(e.bezeichnung)}"
|
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
@ -923,19 +917,16 @@ window.Page_health = (() => {
|
||||||
${e.notiz ? `<div class="list-item-text">${UI.escape(e.notiz)}</div>` : ''}
|
${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"
|
<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-action="open-note" data-entry-id="${e.id}"
|
||||||
data-label="${UI.escape(e.bezeichnung)}"
|
data-label="${UI.escape(e.bezeichnung)}"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
||||||
onclick="event.stopPropagation()"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</button>
|
|
||||||
${count
|
${count
|
||||||
? `<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2);align-items:center;flex-wrap:wrap">
|
? `<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'
|
${mediaList.slice(0, 3).map(m => m.media_type === 'pdf'
|
||||||
? `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
|
? `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
|
||||||
class="btn btn-secondary btn-sm" style="display:inline-flex"
|
class="btn btn-secondary btn-sm" style="display:inline-flex">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF
|
||||||
</a>`
|
</a>`
|
||||||
: `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
|
: `<a href="${UI.escape(m.url)}" target="_blank" rel="noopener"
|
||||||
class="btn btn-secondary btn-sm" style="display:inline-flex"
|
class="btn btn-secondary btn-sm" style="display:inline-flex">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#image"></use></svg> Bild
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#image"></use></svg> Bild
|
||||||
</a>`
|
</a>`
|
||||||
).join('')}
|
).join('')}
|
||||||
|
|
@ -1773,28 +1764,24 @@ window.Page_health = (() => {
|
||||||
${ratingHtml}
|
${ratingHtml}
|
||||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2);flex-wrap:wrap">
|
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2);flex-wrap:wrap">
|
||||||
${p.telefon ? `
|
${p.telefon ? `
|
||||||
<a href="tel:${UI.escape(p.telefon)}" class="btn btn-secondary btn-sm"
|
<a href="tel:${UI.escape(p.telefon)}" class="btn btn-secondary btn-sm">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> Anrufen
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> Anrufen
|
||||||
</a>` : ''}
|
</a>` : ''}
|
||||||
${p.notfall_telefon ? `
|
${p.notfall_telefon ? `
|
||||||
<a href="tel:${UI.escape(p.notfall_telefon)}" class="btn btn-danger btn-sm"
|
<a href="tel:${UI.escape(p.notfall_telefon)}" class="btn btn-danger btn-sm">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Notfall
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Notfall
|
||||||
</a>` : ''}
|
</a>` : ''}
|
||||||
<button class="btn btn-sm btn-secondary"
|
<button class="btn btn-sm btn-secondary"
|
||||||
data-action="bewerten" data-praxis-id="${p.id}"
|
data-action="bewerten" data-praxis-id="${p.id}"
|
||||||
title="Bewertung abgeben"
|
title="Bewertung abgeben"
|
||||||
style="flex-shrink:0"
|
style="flex-shrink:0">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||||
Bewerten
|
Bewerten
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm ${isFav ? 'btn-primary' : 'btn-secondary'}"
|
<button class="btn btn-sm ${isFav ? 'btn-primary' : 'btn-secondary'}"
|
||||||
data-action="toggle-fav" data-praxis-id="${p.id}"
|
data-action="toggle-fav" data-praxis-id="${p.id}"
|
||||||
title="${isFav ? 'Favorit entfernen' : 'Als mein Tierarzt merken'}"
|
title="${isFav ? 'Favorit entfernen' : 'Als mein Tierarzt merken'}"
|
||||||
style="flex-shrink:0"
|
style="flex-shrink:0">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true">
|
<svg class="ph-icon" aria-hidden="true">
|
||||||
<use href="/icons/phosphor.svg#${isFav ? 'heart-fill' : 'heart'}"></use>
|
<use href="/icons/phosphor.svg#${isFav ? 'heart-fill' : 'heart'}"></use>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
@ -1803,8 +1790,7 @@ window.Page_health = (() => {
|
||||||
<button class="btn btn-sm btn-secondary"
|
<button class="btn btn-sm btn-secondary"
|
||||||
data-action="edit-praxis" data-praxis-id="${p.id}"
|
data-action="edit-praxis" data-praxis-id="${p.id}"
|
||||||
title="Praxis bearbeiten"
|
title="Praxis bearbeiten"
|
||||||
style="flex-shrink:0"
|
style="flex-shrink:0">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#pencil-simple"></use></svg>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#pencil-simple"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1881,7 +1867,7 @@ window.Page_health = (() => {
|
||||||
<use href="/icons/phosphor.svg#spinner-gap"></use>
|
<use href="/icons/phosphor.svg#spinner-gap"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</div>`,
|
</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">
|
<button class="btn btn-primary" id="detail-bewerten-btn">
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||||
Jetzt bewerten
|
Jetzt bewerten
|
||||||
|
|
@ -1998,7 +1984,7 @@ window.Page_health = (() => {
|
||||||
title: `${UI.escape(praxis.name)} bewerten`,
|
title: `${UI.escape(praxis.name)} bewerten`,
|
||||||
body,
|
body,
|
||||||
footer: `
|
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">
|
<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>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#star"></use></svg>
|
||||||
${existing ? 'Bewertung aktualisieren' : 'Bewertung abgeben'}
|
${existing ? 'Bewertung aktualisieren' : 'Bewertung abgeben'}
|
||||||
|
|
@ -2373,7 +2359,7 @@ window.Page_health = (() => {
|
||||||
value="${UI.escape(currentNr)}" placeholder="z.B. 276009200123456" maxlength="20">
|
value="${UI.escape(currentNr)}" placeholder="z.B. 276009200123456" maxlength="20">
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" id="transponder-save-btn">Speichern</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById('transponder-save-btn').addEventListener('click', async () => {
|
document.getElementById('transponder-save-btn').addEventListener('click', async () => {
|
||||||
|
|
@ -2441,11 +2427,11 @@ window.Page_health = (() => {
|
||||||
const b = berichte[idx];
|
const b = berichte[idx];
|
||||||
const nav = berichte.length > 1 ? `
|
const nav = berichte.length > 1 ? `
|
||||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
|
<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;
|
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>
|
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>
|
<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;
|
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>
|
font-size:var(--text-sm);${idx <= 0 ? 'opacity:.3;pointer-events:none' : ''}">Neuer ›</button>
|
||||||
</div>` : '';
|
</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="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>`,
|
<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();
|
showBericht();
|
||||||
});
|
});
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
|
@ -2620,15 +2609,13 @@ window.Page_health = (() => {
|
||||||
${adresse ? `<div class="list-item-meta-row">${UI.escape(adresse)}</div>` : ''}
|
${adresse ? `<div class="list-item-meta-row">${UI.escape(adresse)}</div>` : ''}
|
||||||
${vet.telefon ? `
|
${vet.telefon ? `
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<a href="tel:${UI.escape(vet.telefon)}" class="btn btn-secondary btn-sm"
|
<a href="tel:${UI.escape(vet.telefon)}" class="btn btn-secondary btn-sm">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> ${UI.escape(vet.telefon)}
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#phone"></use></svg> ${UI.escape(vet.telefon)}
|
||||||
</a>
|
</a>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
${vet.notfall_telefon ? `
|
${vet.notfall_telefon ? `
|
||||||
<div style="margin-top:var(--space-1)">
|
<div style="margin-top:var(--space-1)">
|
||||||
<a href="tel:${UI.escape(vet.notfall_telefon)}" class="btn btn-danger btn-sm"
|
<a href="tel:${UI.escape(vet.notfall_telefon)}" class="btn btn-danger btn-sm">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Notfall: ${UI.escape(vet.notfall_telefon)}
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning"></use></svg> Notfall: ${UI.escape(vet.notfall_telefon)}
|
||||||
</a>
|
</a>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
|
|
@ -2718,14 +2705,13 @@ window.Page_health = (() => {
|
||||||
${doc.beschreibung ? `<div class="list-item-text">${UI.escape(doc.beschreibung)}</div>` : ''}
|
${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">
|
<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"
|
<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
|
${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#image"></use></svg> Bild öffnen'
|
||||||
: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF öffnen'}
|
: '<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#file-text"></use></svg> PDF öffnen'}
|
||||||
</a>
|
</a>
|
||||||
<button class="btn btn-ghost btn-xs text-danger"
|
<button class="btn btn-ghost btn-xs text-danger"
|
||||||
data-action="delete-hdoc" data-doc-id="${doc.id}"
|
data-action="delete-hdoc" data-doc-id="${doc.id}">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trash"></use></svg>
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#trash"></use></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -3007,7 +2993,7 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
||||||
Bei ernsthaften oder sich verschlechternden Symptomen sofort zum Tierarzt.
|
Bei ernsthaften oder sich verschlechternden Symptomen sofort zum Tierarzt.
|
||||||
</div>`,
|
</div>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" id="ki-tierarzt-submit-btn">Frage stellen</button>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3233,7 +3219,7 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
||||||
</div>
|
</div>
|
||||||
</form>`;
|
</form>`;
|
||||||
const footer = `
|
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>`;
|
<button class="btn btn-primary" id="ins-save-btn" form="${id}">Speichern</button>`;
|
||||||
UI.modal.open({ title: existing ? 'Versicherung bearbeiten' : 'Versicherung eintragen', body, footer });
|
UI.modal.open({ title: existing ? 'Versicherung bearbeiten' : 'Versicherung eintragen', body, footer });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -3385,7 +3371,7 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
||||||
</div>
|
</div>
|
||||||
</form>`;
|
</form>`;
|
||||||
const footer = `
|
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>`;
|
<button class="btn btn-primary" id="beh-save-btn" form="${id}">Speichern</button>`;
|
||||||
UI.modal.open({ title: 'Verhalten erfassen', body, footer });
|
UI.modal.open({ title: 'Verhalten erfassen', body, footer });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
||||||
|
|
@ -394,7 +394,7 @@ window.Page_laeufi = (() => {
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" form="laeufi-form" type="submit">${isEdit ? 'Speichern' : 'Eintragen'}</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById('laeufi-form').addEventListener('submit', async e => {
|
document.getElementById('laeufi-form').addEventListener('submit', async e => {
|
||||||
|
|
@ -472,7 +472,7 @@ window.Page_laeufi = (() => {
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" form="deck-form" type="submit">${isEdit ? 'Speichern' : 'Eintragen'}</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById('deck-form').addEventListener('submit', async e => {
|
document.getElementById('deck-form').addEventListener('submit', async e => {
|
||||||
|
|
@ -505,7 +505,7 @@ window.Page_laeufi = (() => {
|
||||||
title: `Progesterontests — ${_fmtDate(laeufi.beginn)}`,
|
title: `Progesterontests — ${_fmtDate(laeufi.beginn)}`,
|
||||||
body: `<div id="prog-modal-content"><p class="text-muted">Lädt…</p></div>`,
|
body: `<div id="prog-modal-content"><p class="text-muted">Lädt…</p></div>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" id="prog-add-btn">${UI.icon('plus')} Test eintragen</button>`,
|
||||||
});
|
});
|
||||||
await _loadProgContent(laeufi.id);
|
await _loadProgContent(laeufi.id);
|
||||||
|
|
@ -602,7 +602,7 @@ window.Page_laeufi = (() => {
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" form="prog-form" type="submit">Eintragen</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById('prog-form').addEventListener('submit', async e => {
|
document.getElementById('prog-form').addEventListener('submit', async e => {
|
||||||
|
|
|
||||||
|
|
@ -867,7 +867,7 @@ window.Page_litters = (() => {
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<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>
|
<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"
|
<button class="btn btn-ghost btn-xs lost-discard-btn"
|
||||||
data-pending-id="${r.id}"
|
data-pending-id="${r.id}"
|
||||||
onclick="event.stopPropagation()"
|
|
||||||
style="color:var(--c-danger,#dc2626)">
|
style="color:var(--c-danger,#dc2626)">
|
||||||
🗑 Verwerfen
|
🗑 Verwerfen
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -419,7 +418,7 @@ window.Page_lost = (() => {
|
||||||
<button class="btn btn-ghost btn-xs lost-note-btn"
|
<button class="btn btn-ghost btn-xs lost-note-btn"
|
||||||
data-lost-note-id="${r.id}"
|
data-lost-note-id="${r.id}"
|
||||||
data-lost-note-name="${UI.escape(r.name)}"
|
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
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz
|
||||||
</button>
|
</button>
|
||||||
</div>` : '')}
|
</div>` : '')}
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@ window.Page_personality = (() => {
|
||||||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||||||
${typ.aktivitaeten.map(a => `
|
${typ.aktivitaeten.map(a => `
|
||||||
<button class="btn btn-secondary" style="font-size:var(--text-xs);padding:6px 14px;border-radius:999px"
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ function _fmtDate(iso) {
|
||||||
icon: UI.icon('paw-print'),
|
icon: UI.icon('paw-print'),
|
||||||
title: 'Noch kein Hund',
|
title: 'Noch kein Hund',
|
||||||
text: 'Lege zuerst einen Hund in deinem Profil an.',
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ window.Page_poison = (() => {
|
||||||
${_appState.user ? `<div style="margin-top:var(--space-2);text-align:right">
|
${_appState.user ? `<div style="margin-top:var(--space-2);text-align:right">
|
||||||
<button class="btn btn-ghost btn-xs poison-note-btn"
|
<button class="btn btn-ghost btn-xs poison-note-btn"
|
||||||
data-poison-note-id="${r.id}"
|
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
|
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz
|
||||||
</button>
|
</button>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
|
|
|
||||||
|
|
@ -1946,7 +1946,7 @@ window.Page_routes = (() => {
|
||||||
<textarea id="rk-nav-fb-text" class="form-control" rows="4"
|
<textarea id="rk-nav-fb-text" class="form-control" rows="4"
|
||||||
placeholder="z.B. Der Weg nach links ist gesperrt…" maxlength="500"></textarea>`;
|
placeholder="z.B. Der Weg nach links ist gesperrt…" maxlength="500"></textarea>`;
|
||||||
const footer = `
|
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>`;
|
<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 });
|
UI.modal.open({ title: `${UI.icon('chat-circle-dots')} Feedback senden`, body, footer });
|
||||||
document.getElementById('rk-nav-fb-send')?.addEventListener('click', async () => {
|
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>
|
target="_blank" style="flex-shrink:0;margin-left:8px;color:var(--c-primary);font-size:12px">Navi</a>
|
||||||
</div>`).join('')}
|
</div>`).join('')}
|
||||||
</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 ? `
|
const photoGallery = photos.length ? `
|
||||||
<div class="rk-photo-gallery">
|
<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">
|
${isOwn ? `<label class="rk-photo-add" title="Foto hinzufügen">
|
||||||
<span>+</span>
|
<span>+</span>
|
||||||
<input type="file" id="rk-photo-input" accept="image/*" multiple class="hidden">
|
<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}">
|
style="margin-top:2px;flex-shrink:0;accent-color:${color}">
|
||||||
<span>
|
<span>
|
||||||
Ich habe die <span style="color:var(--c-primary);cursor:pointer"
|
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>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2396,7 +2396,7 @@ window.Page_settings = (() => {
|
||||||
</div>
|
</div>
|
||||||
</form>`,
|
</form>`,
|
||||||
footer: `
|
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>`,
|
<button class="btn btn-primary" form="${id}" type="submit">Link senden</button>`,
|
||||||
});
|
});
|
||||||
document.getElementById(id)?.addEventListener('submit', async e => {
|
document.getElementById(id)?.addEventListener('submit', async e => {
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,7 @@ window.Page_sitting = (() => {
|
||||||
${_state.user ? `<button class="btn-icon sit-note-btn"
|
${_state.user ? `<button class="btn-icon sit-note-btn"
|
||||||
data-sit-note-id="${s.id}"
|
data-sit-note-id="${s.id}"
|
||||||
data-sit-note-label="${UI.escHtml(s.sitter_name + ' ' + (s.datum || ''))}"
|
data-sit-note-label="${UI.escHtml(s.sitter_name + ' ' + (s.datum || ''))}"
|
||||||
title="Notiz" style="color:var(--c-text-muted);margin-top:var(--space-1)"
|
title="Notiz" style="color:var(--c-text-muted);margin-top:var(--space-1)">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
${UI.icon('note-pencil')}</button>` : ''}
|
${UI.icon('note-pencil')}</button>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -324,7 +323,7 @@ window.Page_sitting = (() => {
|
||||||
</form>
|
</form>
|
||||||
`;
|
`;
|
||||||
const footer = `
|
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>
|
<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 });
|
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">
|
<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`}
|
${s ? `${UI.icon('floppy-disk')} Speichern` : `${UI.icon('plus')} Profil erstellen`}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary" onclick="UI.modal.close()">Abbrechen</button>
|
<button class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
UI.modal.open({ title: s ? 'Sitter-Profil bearbeiten' : 'Sitter-Profil erstellen', body, footer });
|
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-id="${w.id}"
|
||||||
data-wk-note-label="${UI.escape(w.titel + ' ' + w.datum)}"
|
data-wk-note-label="${UI.escape(w.titel + ' ' + w.datum)}"
|
||||||
data-wk-note-ort="${UI.escape(w.ort_name || '')}"
|
data-wk-note-ort="${UI.escape(w.ort_name || '')}"
|
||||||
title="Notiz" style="color:var(--c-text-muted);font-size:var(--text-xs)"
|
title="Notiz" style="color:var(--c-text-muted);font-size:var(--text-xs)">
|
||||||
onclick="event.stopPropagation()">
|
|
||||||
${UI.icon('note-pencil')}</button>` : ''}
|
${UI.icon('note-pencil')}</button>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
@ -554,7 +553,7 @@ window.Page_walks = (() => {
|
||||||
: (walk.photos || []).map(p => `
|
: (walk.photos || []).map(p => `
|
||||||
<div style="position:relative;aspect-ratio:1">
|
<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"
|
<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 ? `
|
${p.user_id === _appState.user?.id || isOwn ? `
|
||||||
<button type="button" class="wd-photo-del" data-photo-id="${p.id}"
|
<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>
|
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.style.cssText = 'position:relative;aspect-ratio:1';
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<img src="${UI.escape(photo.url)}" style="width:100%;height:100%;object-fit:cover;border-radius:var(--radius-sm);cursor:pointer"
|
<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}"
|
<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>`;
|
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);
|
grid.appendChild(div);
|
||||||
|
|
@ -1108,7 +1107,7 @@ window.Page_walks = (() => {
|
||||||
<div class="challenge-sub-card">
|
<div class="challenge-sub-card">
|
||||||
<img src="${UI.escape(s.foto_url)}" alt="Challenge-Foto" loading="lazy"
|
<img src="${UI.escape(s.foto_url)}" alt="Challenge-Foto" loading="lazy"
|
||||||
onerror="this.src='/icons/icon-192.png'"
|
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-info">
|
||||||
<div class="challenge-sub-user">${UI.icon('user')} ${UI.escape(s.user_name || 'Anonym')}
|
<div class="challenge-sub-user">${UI.icon('user')} ${UI.escape(s.user_name || 'Anonym')}
|
||||||
${s.dog_name ? ` · 🐕 ${UI.escape(s.dog_name)}` : ''}</div>
|
${s.dog_name ? ` · 🐕 ${UI.escape(s.dog_name)}` : ''}</div>
|
||||||
|
|
@ -1169,7 +1168,7 @@ window.Page_walks = (() => {
|
||||||
</form>
|
</form>
|
||||||
`,
|
`,
|
||||||
footer: `
|
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>
|
<button class="btn btn-primary" id="challenge-submit-ok">Einreichen</button>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
@ -1366,7 +1365,7 @@ window.Page_walks = (() => {
|
||||||
</form>
|
</form>
|
||||||
`,
|
`,
|
||||||
footer: `
|
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>
|
<button class="btn btn-primary" id="gz-save-btn">Speichern</button>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ window.Page_widget = (() => {
|
||||||
icon: UI.icon('dog'),
|
icon: UI.icon('dog'),
|
||||||
title: 'Kein Hund angelegt',
|
title: 'Kein Hund angelegt',
|
||||||
text: 'Erstelle zuerst ein Hundeprofil.',
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,19 @@ window.Page_wiki = (() => {
|
||||||
async function init(container, appState) {
|
async function init(container, appState) {
|
||||||
_container = container;
|
_container = container;
|
||||||
_appState = appState;
|
_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();
|
await _render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,11 +193,11 @@ window.Page_wiki = (() => {
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
||||||
<button class="btn btn-primary btn-sm flex-1"
|
<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
|
${UI.icon('check')} Freischalten
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-ghost btn-sm flex-1"
|
<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
|
${UI.icon('x')} Ablehnen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1400,7 +1413,7 @@ window.Page_wiki = (() => {
|
||||||
</p>
|
</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>` : ''}
|
${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>`,
|
</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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,9 @@ window.Page_zucht_profil = (() => {
|
||||||
<div style="text-align:center;padding:var(--space-10) var(--space-4)">
|
<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>
|
<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>
|
<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>`;
|
</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 style="font-size:4rem;margin-bottom:12px">🐾</div>
|
||||||
<div class="world-info-title">Dein Hund wartet</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>
|
<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>`;
|
</div>`;
|
||||||
|
el.querySelector('#world-login-btn')?.addEventListener('click', () => navigateTo('settings'));
|
||||||
return;
|
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 charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="color-scheme" content="light dark">
|
<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>
|
<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="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">
|
<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>
|
<section>
|
||||||
<div class="section-label">Über Ban Yaro — Kurztext für Redaktionen</div>
|
<div class="section-label">Über Ban Yaro — Kurztext für Redaktionen</div>
|
||||||
<div class="boilerplate" id="boilerplate-text">
|
<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>
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -386,16 +386,7 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script src="/js/presse.js"></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>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
// ← 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_VERSION = `by-v${VER}`;
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
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">VDH-kompatibel</span>
|
||||||
<span class="badge">Kein App Store nötig</span>
|
<span class="badge">Kein App Store nötig</span>
|
||||||
</div>
|
</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>
|
<a href="#funktionen" class="cta-btn-secondary">Alle Features ansehen ↓</a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -217,8 +217,8 @@
|
||||||
<a href="#vergleich">Vergleich</a>
|
<a href="#vergleich">Vergleich</a>
|
||||||
<a href="#vorteil">Alleinstellung</a>
|
<a href="#vorteil">Alleinstellung</a>
|
||||||
<a href="#start">Loslegen</a>
|
<a href="#start">Loslegen</a>
|
||||||
<a href="/" onclick="sessionStorage.setItem('by_stay_in_app','1')">Zur App</a>
|
<a href="/" data-stay-in-app>Zur App</a>
|
||||||
<a href="/#register?rolle=breeder" class="nav-cta" onclick="sessionStorage.setItem('by_stay_in_app','1')">Registrieren</a>
|
<a href="/#register?rolle=breeder" class="nav-cta" data-stay-in-app>Registrieren</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
@ -580,7 +580,7 @@
|
||||||
<p style="font-size:.9rem;opacity:.7;margin-bottom:1.75rem">
|
<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
|
39 €/Jahr für die ersten 20 Gründer-Züchter · danach 49 €/Jahr · kein App Store · keine Kreditkarte zum Start
|
||||||
</p>
|
</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>
|
<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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -599,5 +599,6 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="/js/zuechter.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue