diff --git a/VERSION b/VERSION
index 9c6266b..a624bd7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1132
\ No newline at end of file
+1133
\ No newline at end of file
diff --git a/backend/routes/jobs.py b/backend/routes/jobs.py
index 59c73c2..0b678e5 100644
--- a/backend/routes/jobs.py
+++ b/backend/routes/jobs.py
@@ -268,14 +268,20 @@ async def update_application(
"UPDATE users SET is_social_media=1 WHERE id=?",
(row["user_id"],)
)
- founder_count = conn.execute(
- "SELECT COUNT(*) FROM users WHERE is_founder=1"
- ).fetchone()[0]
- if founder_count < 100:
- conn.execute(
- "UPDATE users SET is_founder=1 WHERE id=? AND is_founder=0",
- (row["user_id"],)
- )
+ # Atomare Gründer-Vergabe inkl. founder_number — Race-frei via Sub-Query
+ # (konsistent mit dogs.py / partner.py).
+ conn.execute(
+ """UPDATE users
+ SET is_founder = 1,
+ founder_number = (
+ 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
try:
diff --git a/backend/static/index.html b/backend/static/index.html
index 45ab38a..1e0a5d0 100644
--- a/backend/static/index.html
+++ b/backend/static/index.html
@@ -86,14 +86,14 @@
Ban Yaro
-
+
-
-
-
-
-
+
+
+
+
+
@@ -617,11 +617,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -631,7 +631,7 @@
-
+
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index f957ae0..a7b5e90 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,7 +3,7 @@
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
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
window.APP_VERSION = APP_VERSION;
diff --git a/backend/static/js/pages/diary.js b/backend/static/js/pages/diary.js
index f2137d8..653b117 100644
--- a/backend/static/js/pages/diary.js
+++ b/backend/static/js/pages/diary.js
@@ -782,7 +782,7 @@ window.Page_diary = (() => {
const id = parseInt(btn.dataset.entryId);
const label = btn.dataset.label || '';
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 => {
e.stopPropagation();
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
@@ -1953,83 +1953,6 @@ window.Page_diary = (() => {
// ----------------------------------------------------------
// 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 = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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
diff --git a/backend/static/js/pages/erste-hilfe.js b/backend/static/js/pages/erste-hilfe.js
index e8f1582..780356b 100644
--- a/backend/static/js/pages/erste-hilfe.js
+++ b/backend/static/js/pages/erste-hilfe.js
@@ -461,7 +461,7 @@ window.Page_erste_hilfe = (() => {
const titel = btn.dataset.titel;
const kat = KATEGORIEN.find(k => k.id === katId);
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)
// ----------------------------------------------------------------
- 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,'"') : '';
-
- overlay.innerHTML = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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
diff --git a/backend/static/js/pages/events.js b/backend/static/js/pages/events.js
index afb7e62..62c2d23 100644
--- a/backend/static/js/pages/events.js
+++ b/backend/static/js/pages/events.js
@@ -643,7 +643,7 @@ window.Page_events = (() => {
const noteBtn = e.target.closest('.ev-note-btn');
if (noteBtn) {
e.stopPropagation();
- _openNoteModal(
+ UI.noteModal(
'event',
parseInt(noteBtn.dataset.evNoteId),
noteBtn.dataset.evNoteLabel,
@@ -660,55 +660,6 @@ window.Page_events = (() => {
// ----------------------------------------------------------
// 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 = `
-
-
-
- Notiz — ${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
- `;
- 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 };
diff --git a/backend/static/js/pages/friends.js b/backend/static/js/pages/friends.js
index 9c68959..ea4405c 100644
--- a/backend/static/js/pages/friends.js
+++ b/backend/static/js/pages/friends.js
@@ -442,7 +442,7 @@ window.Page_friends = (() => {
e.stopPropagation();
const id = parseInt(btn.dataset.frNoteId);
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
// ----------------------------------------------------------
- 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 = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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 };
diff --git a/backend/static/js/pages/health.js b/backend/static/js/pages/health.js
index ff59b94..0cf9b93 100644
--- a/backend/static/js/pages/health.js
+++ b/backend/static/js/pages/health.js
@@ -998,7 +998,7 @@ window.Page_health = (() => {
e.stopPropagation();
const id = parseInt(btn.dataset.entryId);
const label = btn.dataset.label || '';
- _openNoteModal('health', id, label, null);
+ UI.noteModal('health', id, label, null);
});
});
// 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)
// ----------------------------------------------------------
- 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 = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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
diff --git a/backend/static/js/pages/lost.js b/backend/static/js/pages/lost.js
index ef4aab5..50c0a98 100644
--- a/backend/static/js/pages/lost.js
+++ b/backend/static/js/pages/lost.js
@@ -353,7 +353,7 @@ window.Page_lost = (() => {
e.stopPropagation();
const id = parseInt(btn.dataset.lostNoteId);
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
// ----------------------------------------------------------
- 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 = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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
diff --git a/backend/static/js/pages/poison.js b/backend/static/js/pages/poison.js
index f9d1151..71fafec 100644
--- a/backend/static/js/pages/poison.js
+++ b/backend/static/js/pages/poison.js
@@ -257,7 +257,7 @@ window.Page_poison = (() => {
btn.addEventListener('click', e => {
e.stopPropagation();
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
// ----------------------------------------------------------
- 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 = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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
diff --git a/backend/static/js/pages/routes.js b/backend/static/js/pages/routes.js
index acb48c6..0c2295e 100644
--- a/backend/static/js/pages/routes.js
+++ b/backend/static/js/pages/routes.js
@@ -2397,7 +2397,7 @@ window.Page_routes = (() => {
// Notiz-Button
document.getElementById('rd-note')?.addEventListener('click', () => {
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
@@ -3054,55 +3054,6 @@ window.Page_routes = (() => {
// ----------------------------------------------------------
// 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 = `
-
-
-
- Notiz — ${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
- `;
- 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 };
diff --git a/backend/static/js/pages/sitting.js b/backend/static/js/pages/sitting.js
index fd59ec9..095a8f8 100644
--- a/backend/static/js/pages/sitting.js
+++ b/backend/static/js/pages/sitting.js
@@ -714,7 +714,7 @@ window.Page_sitting = (() => {
const noteBtn = e.target.closest('.sit-note-btn');
if (noteBtn) {
e.stopPropagation();
- _openNoteModal(
+ UI.noteModal(
'sitting',
parseInt(noteBtn.dataset.sitNoteId),
noteBtn.dataset.sitNoteLabel,
@@ -763,55 +763,6 @@ window.Page_sitting = (() => {
// ----------------------------------------------------------
// 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 = `
-
-
-
- Notiz — ${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
- `;
- 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 };
diff --git a/backend/static/js/pages/trainingsplaene.js b/backend/static/js/pages/trainingsplaene.js
index 7752ea7..d7c62d2 100644
--- a/backend/static/js/pages/trainingsplaene.js
+++ b/backend/static/js/pages/trainingsplaene.js
@@ -538,7 +538,7 @@ function _icon(name) {
const planLabel = _activePlan === 'welpe' ? 'Welpe 0–6 Monate'
: _activePlan === 'junior' ? 'Junior 6–18 Monate'
: `Erwachsener Hund – ${_activeAdultTab}`;
- _openNoteModal('trainingsplan', dogId, planLabel, null);
+ UI.noteModal('trainingsplan', dogId, planLabel, null);
});
// Plan selector
@@ -768,84 +768,6 @@ function _icon(name) {
// ----------------------------------------------------------
// 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 = `
-
-
-
-
Notiz
-
${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
-
-
-
- `;
-
- 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
diff --git a/backend/static/js/pages/walks.js b/backend/static/js/pages/walks.js
index e56f569..2d2072e 100644
--- a/backend/static/js/pages/walks.js
+++ b/backend/static/js/pages/walks.js
@@ -311,7 +311,7 @@ window.Page_walks = (() => {
el.querySelectorAll('.wk-note-btn').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
- _openNoteModal(
+ UI.noteModal(
'walk',
parseInt(btn.dataset.wkNoteId),
btn.dataset.wkNoteLabel,
@@ -1211,55 +1211,6 @@ window.Page_walks = (() => {
// ----------------------------------------------------------
// 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 = `
-
-
-
- Notiz — ${UI.escape(parentLabel)}
-
-
-
-
-
-
-
-
- `;
- 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
diff --git a/backend/static/js/ui.js b/backend/static/js/ui.js
index 2c5486a..9f50342 100644
--- a/backend/static/js/ui.js
+++ b/backend/static/js/ui.js
@@ -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 = `
+
+
+
+
${_svgIcon('note-pencil')} Notiz
+
${escape(parentLabel)}
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ 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
return {
toast, modal,
+ noteModal,
setLoading, asyncButton,
formData, setFormError, clearFormErrors,
emptyState, errorState, time, text, money,
diff --git a/backend/static/landing.html b/backend/static/landing.html
index 0bc6044..06ddcf5 100644
--- a/backend/static/landing.html
+++ b/backend/static/landing.html
@@ -4,7 +4,7 @@
-
+
Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz
diff --git a/backend/static/sw.js b/backend/static/sw.js
index ded7238..b11d249 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -4,7 +4,7 @@
============================================================ */
// ← 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_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten