DRY: Notiz-Modal zentral in UI.noteModal (11 divergierte Kopien entfernt, ~750 Z. weniger); Fix: Founder-Race in jobs.py atomar + founder_number, SW v1133
This commit is contained in:
parent
a356626d39
commit
7b3041fc94
18 changed files with 124 additions and 776 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1132
|
1133
|
||||||
|
|
@ -268,14 +268,20 @@ async def update_application(
|
||||||
"UPDATE users SET is_social_media=1 WHERE id=?",
|
"UPDATE users SET is_social_media=1 WHERE id=?",
|
||||||
(row["user_id"],)
|
(row["user_id"],)
|
||||||
)
|
)
|
||||||
founder_count = conn.execute(
|
# Atomare Gründer-Vergabe inkl. founder_number — Race-frei via Sub-Query
|
||||||
"SELECT COUNT(*) FROM users WHERE is_founder=1"
|
# (konsistent mit dogs.py / partner.py).
|
||||||
).fetchone()[0]
|
conn.execute(
|
||||||
if founder_count < 100:
|
"""UPDATE users
|
||||||
conn.execute(
|
SET is_founder = 1,
|
||||||
"UPDATE users SET is_founder=1 WHERE id=? AND is_founder=0",
|
founder_number = (
|
||||||
(row["user_id"],)
|
SELECT IFNULL(MAX(founder_number), 0) + 1
|
||||||
)
|
FROM users WHERE is_founder = 1
|
||||||
|
)
|
||||||
|
WHERE id = ?
|
||||||
|
AND (is_founder IS NULL OR is_founder = 0)
|
||||||
|
AND (SELECT COUNT(*) FROM users WHERE is_founder = 1) < 100""",
|
||||||
|
(row["user_id"],)
|
||||||
|
)
|
||||||
|
|
||||||
# Status-Mail an Bewerber
|
# Status-Mail an Bewerber
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -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=1132"></script>
|
<script src="/js/boot-early.js?v=1133"></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=1132">
|
<link rel="stylesheet" href="/css/design-system.css?v=1133">
|
||||||
<link rel="stylesheet" href="/css/layout.css?v=1132">
|
<link rel="stylesheet" href="/css/layout.css?v=1133">
|
||||||
<link rel="stylesheet" href="/css/components.css?v=1132">
|
<link rel="stylesheet" href="/css/components.css?v=1133">
|
||||||
<link rel="stylesheet" href="/css/utilities.css?v=1132">
|
<link rel="stylesheet" href="/css/utilities.css?v=1133">
|
||||||
<link rel="stylesheet" href="/css/lists.css?v=1132">
|
<link rel="stylesheet" href="/css/lists.css?v=1133">
|
||||||
</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=1132"></script>
|
<script src="/js/api.js?v=1133"></script>
|
||||||
<script src="/js/ui.js?v=1132"></script>
|
<script src="/js/ui.js?v=1133"></script>
|
||||||
<script src="/js/app.js?v=1132"></script>
|
<script src="/js/app.js?v=1133"></script>
|
||||||
<script src="/js/worlds.js?v=1132"></script>
|
<script src="/js/worlds.js?v=1133"></script>
|
||||||
<script src="/js/offline-indicator.js?v=1132"></script>
|
<script src="/js/offline-indicator.js?v=1133"></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=1132"></script>
|
<script src="/js/boot.js?v=1133"></script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '1132'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '1133'; // ← 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;
|
||||||
|
|
|
||||||
|
|
@ -782,7 +782,7 @@ window.Page_diary = (() => {
|
||||||
const id = parseInt(btn.dataset.entryId);
|
const id = parseInt(btn.dataset.entryId);
|
||||||
const label = btn.dataset.label || '';
|
const label = btn.dataset.label || '';
|
||||||
const location = btn.dataset.location || null;
|
const location = btn.dataset.location || null;
|
||||||
_openNoteModal('diary', id, label, location || null);
|
UI.noteModal('diary', id, label, location || null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1190,7 +1190,7 @@ window.Page_diary = (() => {
|
||||||
view.querySelector('#diary-dv-note')?.addEventListener('click', e => {
|
view.querySelector('#diary-dv-note')?.addEventListener('click', e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const label = entry.titel || entry.datum || String(entry.id);
|
const label = entry.titel || entry.datum || String(entry.id);
|
||||||
_openNoteModal('diary', entry.id, label, entry.location_name || null);
|
UI.noteModal('diary', entry.id, label, entry.location_name || null);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bearbeiten
|
// Bearbeiten
|
||||||
|
|
@ -1953,83 +1953,6 @@ window.Page_diary = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
|
// NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, parentId);
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName, client_time: API.clientNow() };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, parentId, payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// PUBLIC
|
// PUBLIC
|
||||||
|
|
|
||||||
|
|
@ -461,7 +461,7 @@ window.Page_erste_hilfe = (() => {
|
||||||
const titel = btn.dataset.titel;
|
const titel = btn.dataset.titel;
|
||||||
const kat = KATEGORIEN.find(k => k.id === katId);
|
const kat = KATEGORIEN.find(k => k.id === katId);
|
||||||
const label = kat ? `${kat.label} — ${titel}` : titel;
|
const label = kat ? `${kat.label} — ${titel}` : titel;
|
||||||
_openNoteModal('erste_hilfe', katId, label, null);
|
UI.noteModal('erste_hilfe', katId, label, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -469,85 +469,6 @@ window.Page_erste_hilfe = (() => {
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
|
// NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
const _esc = s => s ? String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"') : '';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, parentId);
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, parentId, payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// PUBLIC
|
// PUBLIC
|
||||||
|
|
|
||||||
|
|
@ -643,7 +643,7 @@ window.Page_events = (() => {
|
||||||
const noteBtn = e.target.closest('.ev-note-btn');
|
const noteBtn = e.target.closest('.ev-note-btn');
|
||||||
if (noteBtn) {
|
if (noteBtn) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
_openNoteModal(
|
UI.noteModal(
|
||||||
'event',
|
'event',
|
||||||
parseInt(noteBtn.dataset.evNoteId),
|
parseInt(noteBtn.dataset.evNoteId),
|
||||||
noteBtn.dataset.evNoteLabel,
|
noteBtn.dataset.evNoteLabel,
|
||||||
|
|
@ -660,55 +660,6 @@ window.Page_events = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
let existingNote = null;
|
|
||||||
try { existingNote = await API.notes.get(parentType, String(parentId)); } catch {}
|
|
||||||
|
|
||||||
const ovl = document.createElement('div');
|
|
||||||
ovl.style.cssText = 'position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,0.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
ovl.innerHTML = `
|
|
||||||
<div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
|
|
||||||
padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
|
|
||||||
<svg class="ph-icon" aria-hidden="true" class="text-primary"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
|
||||||
<span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
|
|
||||||
<button id="ev-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<textarea id="ev-note-text" rows="5"
|
|
||||||
style="width:100%;box-sizing:border-box;padding:var(--space-3);
|
|
||||||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
|
||||||
font-size:var(--text-sm);font-family:inherit;
|
|
||||||
background:var(--c-bg);color:var(--c-text);resize:vertical;flex:1"
|
|
||||||
placeholder="Deine Notiz zu diesem Event…">${UI.escape(existingNote?.text || '')}</textarea>
|
|
||||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
|
||||||
<button id="ev-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button id="ev-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(ovl);
|
|
||||||
|
|
||||||
const close = () => ovl.remove();
|
|
||||||
ovl.querySelector('#ev-note-close')?.addEventListener('click', close);
|
|
||||||
ovl.querySelector('#ev-note-cancel')?.addEventListener('click', close);
|
|
||||||
ovl.addEventListener('click', e => { if (e.target === ovl) close(); });
|
|
||||||
|
|
||||||
ovl.querySelector('#ev-note-save')?.addEventListener('click', async () => {
|
|
||||||
const text = ovl.querySelector('#ev-note-text')?.value?.trim() || '';
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName || null };
|
|
||||||
try {
|
|
||||||
if (existingNote?.id) {
|
|
||||||
await API.notes.update(existingNote.id, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
close();
|
|
||||||
} catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { init, refresh, openNew, _openDetail: _showDetail };
|
return { init, refresh, openNew, _openDetail: _showDetail };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -442,7 +442,7 @@ window.Page_friends = (() => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const id = parseInt(btn.dataset.frNoteId);
|
const id = parseInt(btn.dataset.frNoteId);
|
||||||
const name = btn.dataset.frNoteName || '';
|
const name = btn.dataset.frNoteName || '';
|
||||||
_openNoteModal('friends', id, name, null);
|
UI.noteModal('friends', id, name, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -866,83 +866,6 @@ window.Page_friends = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// NOTIZ-MODAL
|
// NOTIZ-MODAL
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, String(parentId));
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
return { init, refresh, onDogChange, _accept, _decline, _cancel, _removeFriend, _openChat };
|
return { init, refresh, onDogChange, _accept, _decline, _cancel, _removeFriend, _openChat };
|
||||||
|
|
|
||||||
|
|
@ -998,7 +998,7 @@ window.Page_health = (() => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const id = parseInt(btn.dataset.entryId);
|
const id = parseInt(btn.dataset.entryId);
|
||||||
const label = btn.dataset.label || '';
|
const label = btn.dataset.label || '';
|
||||||
_openNoteModal('health', id, label, null);
|
UI.noteModal('health', id, label, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// Praxis öffnen → Detail-Modal mit Bewertungen
|
// Praxis öffnen → Detail-Modal mit Bewertungen
|
||||||
|
|
@ -2975,85 +2975,6 @@ function _showPoiKorrekturModal(osmId, poiName, currentOh) {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
|
// NOTIZ-MODAL (custom DOM, kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
// Vorhandenes Modal entfernen falls noch offen
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
// Vorhandene Notiz laden
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, parentId);
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, parentId, payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// KI-TIERARZTFRAGEN
|
// KI-TIERARZTFRAGEN
|
||||||
|
|
|
||||||
|
|
@ -353,7 +353,7 @@ window.Page_lost = (() => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const id = parseInt(btn.dataset.lostNoteId);
|
const id = parseInt(btn.dataset.lostNoteId);
|
||||||
const name = btn.dataset.lostNoteName || '';
|
const name = btn.dataset.lostNoteName || '';
|
||||||
_openNoteModal('lost', id, name, null);
|
UI.noteModal('lost', id, name, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -804,83 +804,6 @@ function _emptyState(icon, title, text, cta = '') {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// NOTIZ-MODAL
|
// NOTIZ-MODAL
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, String(parentId));
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// PUBLIC
|
// PUBLIC
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ window.Page_poison = (() => {
|
||||||
btn.addEventListener('click', e => {
|
btn.addEventListener('click', e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const id = parseInt(btn.dataset.poisonNoteId);
|
const id = parseInt(btn.dataset.poisonNoteId);
|
||||||
_openNoteModal('poison', id, 'Giftköder-Meldung ' + id, null);
|
UI.noteModal('poison', id, 'Giftköder-Meldung ' + id, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -650,83 +650,6 @@ window.Page_poison = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// NOTIZ-MODAL
|
// NOTIZ-MODAL
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, String(parentId));
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// PUBLIC
|
// PUBLIC
|
||||||
|
|
|
||||||
|
|
@ -2397,7 +2397,7 @@ window.Page_routes = (() => {
|
||||||
// Notiz-Button
|
// Notiz-Button
|
||||||
document.getElementById('rd-note')?.addEventListener('click', () => {
|
document.getElementById('rd-note')?.addEventListener('click', () => {
|
||||||
const label = route.name || (route.distanz_km ? route.distanz_km.toFixed(1) + ' km' : 'Route');
|
const label = route.name || (route.distanz_km ? route.distanz_km.toFixed(1) + ' km' : 'Route');
|
||||||
_openNoteModal('route', route.id, label, null);
|
UI.noteModal('route', route.id, label, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mini-Map
|
// Mini-Map
|
||||||
|
|
@ -3054,55 +3054,6 @@ window.Page_routes = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
let existingNote = null;
|
|
||||||
try { existingNote = await API.notes.get(parentType, String(parentId)); } catch {}
|
|
||||||
|
|
||||||
const ovl = document.createElement('div');
|
|
||||||
ovl.style.cssText = 'position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,0.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
ovl.innerHTML = `
|
|
||||||
<div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
|
|
||||||
padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
|
|
||||||
<svg class="ph-icon" aria-hidden="true" class="text-primary"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
|
||||||
<span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
|
|
||||||
<button id="rk-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<textarea id="rk-note-text" rows="5"
|
|
||||||
style="width:100%;box-sizing:border-box;padding:var(--space-3);
|
|
||||||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
|
||||||
font-size:var(--text-sm);font-family:inherit;
|
|
||||||
background:var(--c-bg);color:var(--c-text);resize:vertical;flex:1"
|
|
||||||
placeholder="Deine Notiz zu dieser Route…">${UI.escape(existingNote?.text || '')}</textarea>
|
|
||||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
|
||||||
<button id="rk-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button id="rk-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(ovl);
|
|
||||||
|
|
||||||
const close = () => ovl.remove();
|
|
||||||
ovl.querySelector('#rk-note-close')?.addEventListener('click', close);
|
|
||||||
ovl.querySelector('#rk-note-cancel')?.addEventListener('click', close);
|
|
||||||
ovl.addEventListener('click', e => { if (e.target === ovl) close(); });
|
|
||||||
|
|
||||||
ovl.querySelector('#rk-note-save')?.addEventListener('click', async () => {
|
|
||||||
const text = ovl.querySelector('#rk-note-text')?.value?.trim() || '';
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName || null, client_time: API.clientNow() };
|
|
||||||
try {
|
|
||||||
if (existingNote?.id) {
|
|
||||||
await API.notes.update(existingNote.id, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
close();
|
|
||||||
} catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { init, refresh, onDogChange };
|
return { init, refresh, onDogChange };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -714,7 +714,7 @@ window.Page_sitting = (() => {
|
||||||
const noteBtn = e.target.closest('.sit-note-btn');
|
const noteBtn = e.target.closest('.sit-note-btn');
|
||||||
if (noteBtn) {
|
if (noteBtn) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
_openNoteModal(
|
UI.noteModal(
|
||||||
'sitting',
|
'sitting',
|
||||||
parseInt(noteBtn.dataset.sitNoteId),
|
parseInt(noteBtn.dataset.sitNoteId),
|
||||||
noteBtn.dataset.sitNoteLabel,
|
noteBtn.dataset.sitNoteLabel,
|
||||||
|
|
@ -763,55 +763,6 @@ window.Page_sitting = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
let existingNote = null;
|
|
||||||
try { existingNote = await API.notes.get(parentType, String(parentId)); } catch {}
|
|
||||||
|
|
||||||
const ovl = document.createElement('div');
|
|
||||||
ovl.style.cssText = 'position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,0.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
ovl.innerHTML = `
|
|
||||||
<div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
|
|
||||||
padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
|
|
||||||
<svg class="ph-icon" aria-hidden="true" class="text-primary"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
|
||||||
<span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
|
|
||||||
<button id="sit-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<textarea id="sit-note-text" rows="5"
|
|
||||||
style="width:100%;box-sizing:border-box;padding:var(--space-3);
|
|
||||||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
|
||||||
font-size:var(--text-sm);font-family:inherit;
|
|
||||||
background:var(--c-bg);color:var(--c-text);resize:vertical;flex:1"
|
|
||||||
placeholder="Deine Notiz zu diesem Sitter…">${UI.escape(existingNote?.text || '')}</textarea>
|
|
||||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
|
||||||
<button id="sit-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button id="sit-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(ovl);
|
|
||||||
|
|
||||||
const close = () => ovl.remove();
|
|
||||||
ovl.querySelector('#sit-note-close')?.addEventListener('click', close);
|
|
||||||
ovl.querySelector('#sit-note-cancel')?.addEventListener('click', close);
|
|
||||||
ovl.addEventListener('click', e => { if (e.target === ovl) close(); });
|
|
||||||
|
|
||||||
ovl.querySelector('#sit-note-save')?.addEventListener('click', async () => {
|
|
||||||
const text = ovl.querySelector('#sit-note-text')?.value?.trim() || '';
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName || null };
|
|
||||||
try {
|
|
||||||
if (existingNote?.id) {
|
|
||||||
await API.notes.update(existingNote.id, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
close();
|
|
||||||
} catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { init, refresh };
|
return { init, refresh };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -538,7 +538,7 @@ function _icon(name) {
|
||||||
const planLabel = _activePlan === 'welpe' ? 'Welpe 0–6 Monate'
|
const planLabel = _activePlan === 'welpe' ? 'Welpe 0–6 Monate'
|
||||||
: _activePlan === 'junior' ? 'Junior 6–18 Monate'
|
: _activePlan === 'junior' ? 'Junior 6–18 Monate'
|
||||||
: `Erwachsener Hund – ${_activeAdultTab}`;
|
: `Erwachsener Hund – ${_activeAdultTab}`;
|
||||||
_openNoteModal('trainingsplan', dogId, planLabel, null);
|
UI.noteModal('trainingsplan', dogId, planLabel, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Plan selector
|
// Plan selector
|
||||||
|
|
@ -768,84 +768,6 @@ function _icon(name) {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// NOTIZ-MODAL
|
// NOTIZ-MODAL
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
// Vorhandenes Modal entfernen falls noch offen
|
|
||||||
document.getElementById('by-note-modal')?.remove();
|
|
||||||
|
|
||||||
const overlay = document.createElement('div');
|
|
||||||
overlay.id = 'by-note-modal';
|
|
||||||
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
|
|
||||||
overlay.innerHTML = `
|
|
||||||
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
|
||||||
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
|
||||||
padding-bottom:env(safe-area-inset-bottom,0px)">
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
|
||||||
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
||||||
<div>
|
|
||||||
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz</div>
|
|
||||||
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${UI.escape(parentLabel)}</div>
|
|
||||||
</div>
|
|
||||||
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
|
||||||
<form id="by-note-form">
|
|
||||||
<textarea id="by-note-text" class="form-control" rows="5"
|
|
||||||
placeholder="Notiz eingeben…"
|
|
||||||
style="width:100%;resize:vertical"></textarea>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
|
||||||
display:flex;gap:var(--space-2);flex-shrink:0">
|
|
||||||
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
|
|
||||||
const textarea = document.getElementById('by-note-text');
|
|
||||||
const saveBtn = document.getElementById('by-note-save');
|
|
||||||
const cancelBtn = document.getElementById('by-note-cancel');
|
|
||||||
const closeBtn = document.getElementById('by-note-close');
|
|
||||||
|
|
||||||
let existingNoteId = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const existing = await API.notes.get(parentType, String(parentId));
|
|
||||||
if (existing?.id) {
|
|
||||||
existingNoteId = existing.id;
|
|
||||||
textarea.value = existing.text || '';
|
|
||||||
}
|
|
||||||
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
|
||||||
|
|
||||||
setTimeout(() => textarea.focus(), 100);
|
|
||||||
|
|
||||||
const _close = () => overlay.remove();
|
|
||||||
closeBtn.addEventListener('click', _close);
|
|
||||||
cancelBtn.addEventListener('click', _close);
|
|
||||||
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
|
||||||
|
|
||||||
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
|
||||||
e.preventDefault();
|
|
||||||
const text = textarea.value.trim();
|
|
||||||
UI.setLoading(saveBtn, true);
|
|
||||||
try {
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName };
|
|
||||||
if (existingNoteId) {
|
|
||||||
await API.notes.update(existingNoteId, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
_close();
|
|
||||||
} catch (err) {
|
|
||||||
UI.toast.error(err.message || 'Fehler beim Speichern.');
|
|
||||||
UI.setLoading(saveBtn, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// PUBLIC API
|
// PUBLIC API
|
||||||
|
|
|
||||||
|
|
@ -311,7 +311,7 @@ window.Page_walks = (() => {
|
||||||
el.querySelectorAll('.wk-note-btn').forEach(btn => {
|
el.querySelectorAll('.wk-note-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', e => {
|
btn.addEventListener('click', e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
_openNoteModal(
|
UI.noteModal(
|
||||||
'walk',
|
'walk',
|
||||||
parseInt(btn.dataset.wkNoteId),
|
parseInt(btn.dataset.wkNoteId),
|
||||||
btn.dataset.wkNoteLabel,
|
btn.dataset.wkNoteLabel,
|
||||||
|
|
@ -1211,55 +1211,6 @@ window.Page_walks = (() => {
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
// Notiz-Modal (Custom DOM — kein UI.modal um Konflikte zu vermeiden)
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
async function _openNoteModal(parentType, parentId, parentLabel, locationName) {
|
|
||||||
let existingNote = null;
|
|
||||||
try { existingNote = await API.notes.get(parentType, String(parentId)); } catch {}
|
|
||||||
|
|
||||||
const ovl = document.createElement('div');
|
|
||||||
ovl.style.cssText = 'position:fixed;inset:0;z-index:1200;background:rgba(0,0,0,0.55);display:flex;align-items:flex-end;justify-content:center';
|
|
||||||
ovl.innerHTML = `
|
|
||||||
<div style="width:100%;max-width:600px;background:var(--c-surface);border-radius:var(--radius-lg) var(--radius-lg) 0 0;
|
|
||||||
padding:var(--space-4);box-sizing:border-box;max-height:80vh;display:flex;flex-direction:column">
|
|
||||||
<div style="display:flex;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3)">
|
|
||||||
<svg class="ph-icon" aria-hidden="true" class="text-primary"><use href="/icons/phosphor.svg#note-pencil"></use></svg>
|
|
||||||
<span style="font-weight:600;flex:1"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#note-pencil"></use></svg> Notiz — ${UI.escape(parentLabel)}</span>
|
|
||||||
<button id="wk-note-close" style="background:none;border:none;cursor:pointer;color:var(--c-text-muted);padding:4px">
|
|
||||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<textarea id="wk-note-text" rows="5"
|
|
||||||
style="width:100%;box-sizing:border-box;padding:var(--space-3);
|
|
||||||
border:1.5px solid var(--c-border);border-radius:var(--radius-md);
|
|
||||||
font-size:var(--text-sm);font-family:inherit;
|
|
||||||
background:var(--c-bg);color:var(--c-text);resize:vertical;flex:1"
|
|
||||||
placeholder="Deine Notiz zu diesem Gassi-Treffen…">${UI.escape(existingNote?.text || '')}</textarea>
|
|
||||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
|
|
||||||
<button id="wk-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
|
||||||
<button id="wk-note-save" class="btn btn-primary flex-1">Speichern</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.appendChild(ovl);
|
|
||||||
|
|
||||||
const close = () => ovl.remove();
|
|
||||||
ovl.querySelector('#wk-note-close')?.addEventListener('click', close);
|
|
||||||
ovl.querySelector('#wk-note-cancel')?.addEventListener('click', close);
|
|
||||||
ovl.addEventListener('click', e => { if (e.target === ovl) close(); });
|
|
||||||
|
|
||||||
ovl.querySelector('#wk-note-save')?.addEventListener('click', async () => {
|
|
||||||
const text = ovl.querySelector('#wk-note-text')?.value?.trim() || '';
|
|
||||||
const payload = { text, parent_label: parentLabel, location_name: locationName || null };
|
|
||||||
try {
|
|
||||||
if (existingNote?.id) {
|
|
||||||
await API.notes.update(existingNote.id, payload);
|
|
||||||
} else {
|
|
||||||
await API.notes.create(parentType, String(parentId), payload);
|
|
||||||
}
|
|
||||||
UI.toast.success('Notiz gespeichert.');
|
|
||||||
close();
|
|
||||||
} catch (err) { UI.toast.error(err.message || 'Fehler beim Speichern.'); }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==============================================================
|
// ==============================================================
|
||||||
// FEATURE 1: Foto-Challenge der Woche
|
// FEATURE 1: Foto-Challenge der Woche
|
||||||
|
|
|
||||||
|
|
@ -1327,9 +1327,91 @@ const UI = (() => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
// NOTE-MODAL — Notiz zu einem beliebigen Objekt (parentType/parentId)
|
||||||
|
// erstellen/bearbeiten. Zentral, damit nicht jede Seite eine eigene Kopie hat.
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
async function noteModal(parentType, parentId, parentLabel, locationName) {
|
||||||
|
document.getElementById('by-note-modal')?.remove();
|
||||||
|
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.id = 'by-note-modal';
|
||||||
|
overlay.style.cssText = 'position:fixed;inset:0;z-index:2000;background:rgba(0,0,0,.55);display:flex;align-items:flex-end;justify-content:center';
|
||||||
|
|
||||||
|
overlay.innerHTML = `
|
||||||
|
<div style="background:var(--c-surface);border-radius:var(--radius-xl) var(--radius-xl) 0 0;
|
||||||
|
width:100%;max-width:640px;max-height:90vh;display:flex;flex-direction:column;
|
||||||
|
padding-bottom:env(safe-area-inset-bottom,0px)">
|
||||||
|
<div style="padding:var(--space-4) var(--space-5);border-bottom:1px solid var(--c-border);
|
||||||
|
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight:var(--weight-semibold);font-size:var(--text-base)">${_svgIcon('note-pencil')} Notiz</div>
|
||||||
|
<div style="font-size:var(--text-xs);color:var(--c-text-muted);margin-top:2px">${escape(parentLabel)}</div>
|
||||||
|
</div>
|
||||||
|
<button id="by-note-close" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--c-text-muted);padding:4px 8px" aria-label="Schließen">×</button>
|
||||||
|
</div>
|
||||||
|
<div style="padding:var(--space-4) var(--space-5);flex:1;overflow-y:auto">
|
||||||
|
<form id="by-note-form">
|
||||||
|
<textarea id="by-note-text" class="form-control" rows="5"
|
||||||
|
placeholder="Notiz eingeben…"
|
||||||
|
style="width:100%;resize:vertical"></textarea>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div style="padding:var(--space-3) var(--space-5);border-top:1px solid var(--c-border);
|
||||||
|
display:flex;gap:var(--space-2);flex-shrink:0">
|
||||||
|
<button type="button" id="by-note-cancel" class="btn btn-secondary flex-1">Abbrechen</button>
|
||||||
|
<button type="submit" form="by-note-form" id="by-note-save" class="btn btn-primary flex-1">Speichern</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
|
||||||
|
const textarea = document.getElementById('by-note-text');
|
||||||
|
const saveBtn = document.getElementById('by-note-save');
|
||||||
|
const cancelBtn = document.getElementById('by-note-cancel');
|
||||||
|
const closeBtn = document.getElementById('by-note-close');
|
||||||
|
|
||||||
|
let existingNoteId = null;
|
||||||
|
try {
|
||||||
|
const existing = await API.notes.get(parentType, parentId);
|
||||||
|
if (existing?.id) {
|
||||||
|
existingNoteId = existing.id;
|
||||||
|
textarea.value = existing.text || '';
|
||||||
|
}
|
||||||
|
} catch (_) { /* keine Notiz vorhanden — ok */ }
|
||||||
|
|
||||||
|
setTimeout(() => textarea.focus(), 100);
|
||||||
|
|
||||||
|
const _close = () => overlay.remove();
|
||||||
|
closeBtn.addEventListener('click', _close);
|
||||||
|
cancelBtn.addEventListener('click', _close);
|
||||||
|
overlay.addEventListener('click', e => { if (e.target === overlay) _close(); });
|
||||||
|
|
||||||
|
document.getElementById('by-note-form').addEventListener('submit', async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const text = textarea.value.trim();
|
||||||
|
setLoading(saveBtn, true);
|
||||||
|
try {
|
||||||
|
const payload = { text, parent_label: parentLabel, location_name: locationName || null, client_time: API.clientNow() };
|
||||||
|
if (existingNoteId) {
|
||||||
|
await API.notes.update(existingNoteId, payload);
|
||||||
|
} else {
|
||||||
|
await API.notes.create(parentType, parentId, payload);
|
||||||
|
}
|
||||||
|
toast.success('Notiz gespeichert.');
|
||||||
|
_close();
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(err.message || 'Fehler beim Speichern.');
|
||||||
|
setLoading(saveBtn, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Öffentliche API
|
// Öffentliche API
|
||||||
return {
|
return {
|
||||||
toast, modal,
|
toast, modal,
|
||||||
|
noteModal,
|
||||||
setLoading, asyncButton,
|
setLoading, asyncButton,
|
||||||
formData, setFormError, clearFormErrors,
|
formData, setFormError, clearFormErrors,
|
||||||
emptyState, errorState, time, text, money,
|
emptyState, errorState, time, text, money,
|
||||||
|
|
|
||||||
|
|
@ -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=1132"></script>
|
<script src="/js/landing-init.js?v=1133"></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">
|
||||||
|
|
|
||||||
|
|
@ -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 = '1132';
|
const VER = '1133';
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue