Sprint 18: Notification Center, Routen entdecken, Onboarding, Admin-Erweiterungen

- Notifications: History-Tabelle, /api/notifications Endpoints, push.py schreibt in DB
- Notifications: Page (notifications.js) mit Badge, Typen-Icons, gelesen-Markierung
- Routen: Entdecken-Modus mit Ersteller-Anzeige, Nearby-Filter, Mine/Discover Toggle
- Onboarding: Willkommens-Modal nach Registrierung, Push-Angebot nach Login
- Admin: Scheduler-Tab (Jobs anzeigen + manuell triggern), System-Health (DB/Disk/Uptime)
- Admin: Audit-Log (wer hat was wann gemacht), erweiterte Stats (Push-Abos, aktive User, Routen)
- SW: by-v152, APP_VER 125
This commit is contained in:
rene 2026-04-17 23:21:48 +02:00
parent 5927d384bf
commit 92620c2c52
14 changed files with 1035 additions and 46 deletions

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '123'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '124'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => {
@ -56,7 +56,8 @@ const App = (() => {
admin: { title: 'Admin', module: null, requiresAuth: true },
impressum: { title: 'Impressum', module: null },
datenschutz: { title: 'Datenschutz', module: null },
widget: { title: 'Widget', module: null, requiresAuth: true },
widget: { title: 'Widget', module: null, requiresAuth: true },
notifications: { title: 'Benachrichtigungen', module: null, requiresAuth: true },
};
// ----------------------------------------------------------
@ -409,6 +410,26 @@ const App = (() => {
adminItem.style.display = isMod ? '' : 'none';
}
await _loadDogs();
// Eingeloggter User ohne Hund (z.B. nach Reload) → direkt zur Hund-Anlage
if (state.dogs.length === 0) {
navigate('dog-profile');
}
_updateNotifBadge();
// Badge alle 60s aktualisieren
setInterval(_updateNotifBadge, 60_000);
}
async function _updateNotifBadge() {
if (!state.user) return;
try {
const { count } = await API.notifications.unreadCount();
const badge = document.getElementById('notif-badge');
if (!badge) return;
badge.textContent = count;
badge.style.display = count > 0 ? '' : 'none';
} catch { /* ignorieren */ }
}
function _onLoggedOut() {
@ -446,6 +467,50 @@ const App = (() => {
} catch { /* kein Hund vorhanden */ }
}
// ----------------------------------------------------------
// ONBOARDING — Willkommens-Modal für neue User
// ----------------------------------------------------------
function _showOnboardingModal() {
UI.modal.open({
title: `${UI.icon('paw-print')} Willkommen bei Ban Yaro!`,
body: `
<div style="display:flex;flex-direction:column;align-items:center;
gap:var(--space-4);text-align:center;padding:var(--space-2) 0">
<div style="width:64px;height:64px;border-radius:50%;
background:var(--c-primary-subtle);
display:flex;align-items:center;justify-content:center">
<svg style="width:32px;height:32px;color:var(--c-primary)" aria-hidden="true">
<use href="/icons/phosphor.svg#dog"></use>
</svg>
</div>
<div style="max-width:300px">
<p style="margin:0 0 var(--space-3);font-size:var(--text-sm);
color:var(--c-text-secondary);line-height:1.6">
Schön, dass du dabei bist! Ban Yaro hilft dir, alles rund um
deinen Hund im Blick zu behalten Spaziergänge, Gesundheit,
Termine und vieles mehr.
</p>
<p style="margin:0;font-size:var(--text-sm);
color:var(--c-text-secondary);line-height:1.6">
Fang jetzt an und leg ein Profil für deinen Hund an.
</p>
</div>
</div>
`,
footer: `
<button type="button" class="btn btn-primary" id="onboarding-start-btn">
${UI.icon('dog')} Meinen ersten Hund anlegen
</button>
<button type="button" class="btn btn-ghost" data-modal-close>Später</button>
`,
});
document.getElementById('onboarding-start-btn')?.addEventListener('click', () => {
UI.modal.close();
navigate('dog-profile');
});
}
function _notifyDogChange() {
Object.values(pages).forEach(p => p.module?.onDogChange?.(state.activeDog));
}
@ -646,7 +711,9 @@ const App = (() => {
// (andere Module können App.state, App.navigate etc. nutzen)
// ----------------------------------------------------------
return { init, navigate, state, setActiveDog, renderDogSwitcher: _renderDogSwitcher,
getInstallPrompt: () => _installPrompt, requireAuth };
getInstallPrompt: () => _installPrompt, requireAuth,
showOnboarding: _showOnboardingModal,
updateNotifBadge: _updateNotifBadge };
})();