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

@ -551,6 +551,11 @@ window.Page_settings = (() => {
UI.toast.success(`Willkommen zurück, ${_appState.user.name}!`);
// Push-Benachrichtigungen anbieten wenn noch nicht entschieden
if (typeof Notification !== 'undefined' && Notification.permission === 'default') {
_offerPushNotifications();
}
// Nach Login: Tagebuch oder Profil anlegen
if (_appState.activeDog) {
App.navigate('diary');
@ -585,13 +590,67 @@ window.Page_settings = (() => {
_appState.dogs = [];
_appState.activeDog = null;
UI.toast.success(`Willkommen bei Ban Yaro, ${_appState.user.name}! 🐕`);
// Direkt zur Profil-Anlage
App.navigate('dog-profile');
UI.toast.success(`Willkommen bei Ban Yaro, ${_appState.user.name}!`);
// Onboarding-Modal direkt zeigen (SPA — kein Reload)
App.showOnboarding();
});
});
}
// ----------------------------------------------------------
// PUSH-BENACHRICHTIGUNGEN ANBIETEN (nach Login)
// ----------------------------------------------------------
function _offerPushNotifications() {
// Kleiner Toast-Banner mit Ja-Button — nicht-invasiv
const toastEl = document.createElement('div');
toastEl.id = 'push-offer-banner';
toastEl.style.cssText = [
'position:fixed',
'bottom:calc(var(--nav-h, 64px) + var(--space-3))',
'left:50%',
'transform:translateX(-50%)',
'background:var(--c-surface)',
'border:1.5px solid var(--c-border)',
'border-radius:var(--radius-lg)',
'box-shadow:var(--shadow-lg)',
'padding:var(--space-3) var(--space-4)',
'display:flex',
'align-items:center',
'gap:var(--space-3)',
'font-size:var(--text-sm)',
'z-index:1100',
'max-width:340px',
'width:calc(100% - var(--space-8))',
].join(';');
toastEl.innerHTML = `
<svg class="ph-icon" aria-hidden="true" style="flex-shrink:0;color:var(--c-primary)">
<use href="/icons/phosphor.svg#bell-ringing"></use>
</svg>
<span style="flex:1;line-height:1.4">Push-Benachrichtigungen aktivieren?</span>
<button id="push-offer-yes" class="btn btn-primary" style="font-size:var(--text-xs);padding:var(--space-1) var(--space-3);flex-shrink:0">Ja</button>
<button id="push-offer-no" class="btn btn-ghost btn-icon" aria-label="Schließen" style="flex-shrink:0">
<svg class="ph-icon" style="width:16px;height:16px" aria-hidden="true"><use href="/icons/phosphor.svg#x"></use></svg>
</button>
`;
document.body.appendChild(toastEl);
const remove = () => toastEl.remove();
document.getElementById('push-offer-yes')?.addEventListener('click', async () => {
remove();
try {
await API.subscribeToPush();
UI.toast.success('Push-Benachrichtigungen aktiviert.');
} catch {
UI.toast.warning('Push-Benachrichtigungen konnten nicht aktiviert werden.');
}
});
document.getElementById('push-offer-no')?.addEventListener('click', remove);
// Automatisch ausblenden nach 12 Sekunden
setTimeout(remove, 12000);
}
// ----------------------------------------------------------
// HELPER
// ----------------------------------------------------------