From 82a1f2c2398e8956e5316c5275e05d0d2406da83 Mon Sep 17 00:00:00 2001 From: Ulas Kalayci Date: Mon, 4 May 2026 20:31:42 +0200 Subject: [PATCH] feat: add flexible reminder options for birthdays Add support for customizable birthday reminders with preset offsets (none, at time, 15min, 1h, 1d, 2d, 1w, 2w) and custom intervals. Users can now configure when to be reminded of upcoming birthdays. - Add migration 31: reminder_offset, reminder_custom_amount, reminder_custom_unit to birthdays table - Update POST/PUT /birthdays routes to accept reminder fields - Add getOffsetMinutes() helper in birthday service - Update birthdayReminderAt() to calculate reminder time with offset - Modify syncBirthdayReminder() to handle empty offset (no reminder) - Add renderBirthdayReminderSection() UI component - Move reminder-custom CSS from calendar.css to reminders.css - Add protocol check to service worker (non-http protocol guard) All translations already present in de.json. Tests: 109 passing, 0 failing. Co-Authored-By: Rafael Foster Co-Authored-By: Claude Sonnet 4.5 --- .../content/ux-analysis-overview.html | 777 ++++++++++++++++++ .../947899-1777303942/state/server-stopped | 1 + .../947899-1777303942/state/server.pid | 1 + .../content/01-overview.html | 179 ++++ .../content/02-solutions.html | 629 ++++++++++++++ .../958518-1777309033/state/server-stopped | 1 + .../958518-1777309033/state/server.pid | 1 + public/pages/birthdays.js | 55 ++ public/styles/calendar.css | 8 - public/styles/reminders.css | 8 + public/sw.js | 1 + server/db.js | 9 + server/routes/birthdays.js | 21 +- server/services/birthdays.js | 28 +- 14 files changed, 1705 insertions(+), 14 deletions(-) create mode 100644 .superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html create mode 100644 .superpowers/brainstorm/947899-1777303942/state/server-stopped create mode 100644 .superpowers/brainstorm/947899-1777303942/state/server.pid create mode 100644 .superpowers/brainstorm/958518-1777309033/content/01-overview.html create mode 100644 .superpowers/brainstorm/958518-1777309033/content/02-solutions.html create mode 100644 .superpowers/brainstorm/958518-1777309033/state/server-stopped create mode 100644 .superpowers/brainstorm/958518-1777309033/state/server.pid diff --git a/.superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html b/.superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html new file mode 100644 index 0000000..ef53c8d --- /dev/null +++ b/.superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html @@ -0,0 +1,777 @@ + + + + + +Oikos UX/UI Analyse + + + + +
+
UX/UI Analyse · Oikos · April 2026
+

Was funktioniert gut —
und was bremst Oikos aus

+

70 % Mobile-PWA · 30 % Desktop · 11 Module · Vanilla JS · Kein Build-Step

+
+ + +
+
+
+
4
+
Kritische Probleme
(sofort beheben)
+
+
+
5
+
Hohe Priorität
(nächster Sprint)
+
+
+
4
+
Mittlere Priorität
(Backlog)
+
+
+
+ +
+ + +
+ +
+ +
+
+
🧭
+
Sidebar 1024–1279 px: Icons ohne Labels oder Tooltips
+
+
+ Bei der häufigsten Desktop-Auflösung zeigt die Sidebar nur Icons — kein Tooltip, kein Label. + Neue Nutzer können die 11 Module nicht erkennen. Das verletzt das Grundprinzip + «nav-label-icon» (HIG/Material). +
+
Keine Discoverability
+
+ +
+
+
👆
+
Modal-Close: 40 px statt 44 px Minimum
+
+
+ .modal-panel__close nutzt --target-md (40 px). + Das iOS-Minimum liegt bei 44 pt. Auf kleinen Displays — gerade beim + Schließen tippend — ist das ein spürbares Frustrationspotenzial. +
+
Apple HIG Violation
+
+ +
+
+
🔗
+
Widget-Links: kein Min-Height-Tap-Target
+
+
+ .widget__link hat 12 px Text, kein explizites min-height. + Der effektive Tippbereich ist ~16–18 px — weit unter 44 px. + Auf dem Dashboard ist dieser Link auf jedem Widget sichtbar. +
+
Tap-Target < 44 px
+
+ +
+
+
🔁
+
Doppelter FAB: .fab vs .page-fab
+
+
+ In layout.css existieren zwei nahezu identische FAB-Klassen (.fab + und .page-fab) mit unterschiedlicher bottom-Berechnung. + Das erzeugt inkonsistente Positionierung auf verschiedenen Seiten. +
+
Inkonsistente UI
+
+ +
+
+ + +
+ +
+
+
+ + Ist-Zustand: 1024–1279 px (nur Icons) +
+
+
+
+
+
+
+
+
+
+
+

Welcher Icon ist "Geburtstage"?

+
+
+
+
+ + Soll-Zustand: Tooltip bei Hover (min) +
+
+
+
+
+
Dashboard
+
+
+
+
+
+
+

Sofort klar durch title-Tooltip

+
+
+
+
+ + +
+ +
+
+
+ + Ist-Zustand: Modal-Close 40 × 40 px +
+
+
+
+
+
40 × 40 px — 4 px unter iOS-Minimum
+
+
+
+ Fingertipp ~44–50 px → Fehlklick-Rate steigt +
+
+
+
+
+
+
+ + Fix: --target-base (44 px) +
+
+
+
+
+
44 × 44 px — Apple HIG Minimum
+
+
+
+ Einzeiler-Fix: --target-base statt --target-md +
+
+
+
+
+
+
+ +
+ + +
+ +
+ +
+
+
🗂️
+
Onboarding: 3 Schritte für 11 Module
+
+
+ Neue Nutzer sehen einmalig 3 generische Onboarding-Screens. + Module wie Budget, RRule-Wiederholungen oder Google Calendar-Sync + werden nie erklärt. Kein Feature-Discovery-Mechanismus danach. +
+
Discoverability
+
+ +
+
+
📊
+
Dashboard-Widgets: keine Reihenfolge-Anpassung
+
+
+ Widgets können ein-/ausgeblendet werden, aber nicht umsortiert. + Die Widget-Config speichert nur { id, visible } — kein + Reihenfolge-Feld. Für Familien mit verschiedenen Prioritäten ist das stark limitierend. +
+
Personalisierung
+
+ +
+
+
↩️
+
Inkonsistentes Undo-Verhalten
+
+
+ Einige Aktionen zeigen einen Undo-Toast, andere nicht. + Es gibt kein zentrales Undo-System. Bei destruktiven Aktionen + (z. B. Kontakt löschen) fehlt der Undo-Pfad komplett. +
+
Fehlererholung
+
+ +
+
+
📡
+
Kein sichtbarer Offline-Indikator
+
+
+ Der Service Worker existiert, aber der App-Shell fehlt ein + Offline-Banner. Nutzer bemerken den Offline-Zustand erst, + wenn eine API-Anfrage fehlschlägt — zu spät. +
+
PWA-Erfahrung
+
+ +
+
+
⌨️
+
Desktop: keine Keyboard Shortcuts
+
+
+ Bei 30 % Desktop-Nutzung fehlen globale Shortcuts. + Kein „N" für neue Aufgabe, kein „/" für Suche, kein + Escape-Verhalten im globalem Kontext. Power-User müssen + alles mit der Maus bedienen. +
+
Desktop-Ergonomie
+
+ +
+
+ +
+ + +
+ +
+ +
+
+
💬
+
reminders.css global geladen
+
+
+ reminders.css wird laut Observations global geladen, + nicht lazy. Auf Seiten ohne Reminder-UI werden unnötige Styles + geparst. Kein Blocking-Problem, aber vermeidbare CSS-Last. +
+
Performance
+
+ +
+
+
🃏
+
Swipe-Geste: nur Tasks & Shopping
+
+
+ Swipe-Reveal ist für Tasks und Shopping implementiert, + fehlt aber bei Kontakten, Notizen und Geburtstagen — obwohl + die Interaktion dort genauso wertvoll wäre. + Inkonsistente Erwartungshaltung. +
+
Interaktions-Konsistenz
+
+ +
+
+
🎨
+
11 Modulfarben gleichzeitig im Dashboard
+
+
+ Wenn alle Widgets sichtbar sind, treffen 11 verschiedene + Akzentfarben aufeinander. Das Dashboard wirkt farblich + überladen. Weniger Kontrast zwischen den Modulen würde + die Ruhewirkung verbessern. +
+
Visuelle Ruhe
+
+ +
+
+
📱
+
Toast: kein Swipe-to-Dismiss auf Mobile
+
+
+ Toasts können nicht weggewischt werden — nur auto-dismiss + oder Undo-Button. Auf Mobile ist Swipe-to-Dismiss eine + etablierte Konvention (iOS, Android), deren Fehlen auffällt. +
+
Mobile-Konvention
+
+ +
+
+ +
+ + +
+ +
+
+
🌙 Dark Mode
+
Private-/Public-Token-Architektur ist mustergültig. Alle Kontrastverhältnisse WCAG-geprüft, Toast-Texte passen sich an.
+
+
+
♿ Accessibility-Schichten
+
prefers-reduced-motion, prefers-reduced-transparency, prefers-contrast, forced-colors — alle implementiert. Selten in selbstgehosteten Apps.
+
+
+
🍎 iOS-PWA-Bewusstsein
+
100dvh + -webkit-fill-available Fallback, safe-area-inset-*, Flex-Kind statt fixed für Bottom-Nav — solide PWA-Grundlage.
+
+
+
💎 Glass Design System
+
@supports-basiert mit korrekten webkit-Fallbacks, opake Fallbacks für reduced-transparency. Konsistente Token-Hierarchie.
+
+
+
🎭 Modul-Theming
+
--active-module-accent + --module-accent System ist elegant. FAB, Nav-Item und Toggles reflektieren automatisch die aktive Seite.
+
+
+
📐 Design Tokens
+
Vollständige Skala für Farben, Radien, Schatten, Spacing, Typografie. Konsistente Anwendung — kaum Hardcoding gefunden.
+
+
+
+ + + + diff --git a/.superpowers/brainstorm/947899-1777303942/state/server-stopped b/.superpowers/brainstorm/947899-1777303942/state/server-stopped new file mode 100644 index 0000000..6058001 --- /dev/null +++ b/.superpowers/brainstorm/947899-1777303942/state/server-stopped @@ -0,0 +1 @@ +{"reason":"idle timeout","timestamp":1777305983821} diff --git a/.superpowers/brainstorm/947899-1777303942/state/server.pid b/.superpowers/brainstorm/947899-1777303942/state/server.pid new file mode 100644 index 0000000..47b52d0 --- /dev/null +++ b/.superpowers/brainstorm/947899-1777303942/state/server.pid @@ -0,0 +1 @@ +947907 diff --git a/.superpowers/brainstorm/958518-1777309033/content/01-overview.html b/.superpowers/brainstorm/958518-1777309033/content/01-overview.html new file mode 100644 index 0000000..8db331e --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/content/01-overview.html @@ -0,0 +1,179 @@ + + + + + +Oikos UX/UI Analyse + + + +
+
Oikos UX/UI Analyse · April 2026
+

Was funktioniert — was bremst Oikos

+

70 % Mobile-PWA · 30 % Desktop · 11 Module · Vanilla JS

+
+ +
+
4
Kritisch
sofort beheben
+
5
Hohe Priorität
nächster Sprint
+
4
Mittlere Priorität
Backlog
+
+ +
+ +
+
🔴 Kritisch
+
+
+
🧭
+
Sidebar 1024–1279 px: Icons ohne Labels/Tooltips
+
Bei häufigster Desktop-Auflösung sind Modul-Icons ohne jede Beschriftung — kein Tooltip, kein Label. 11 Module sind nicht erkennbar.
+
Keine Discoverability
+
+
+
👆
+
Modal-Close: 40 px statt 44 px Minimum
+
.modal-panel__close nutzt --target-md (40 px). iOS-Minimum ist 44 pt. Auf kleinen Screens spürbares Frustrationspotenzial.
+
Apple HIG Violation
+
+
+
🔗
+
Widget-Links: Tap-Target < 44 px
+
.widget__link: 12 px Text, kein min-height. Effektiver Tippbereich ~16–18 px. Auf jedem Dashboard-Widget vorhanden.
+
Tap-Target Violation
+
+
+
🔁
+
Doppelter FAB: .fab vs .page-fab
+
Zwei nahezu identische FAB-Klassen mit unterschiedlicher bottom-Berechnung erzeugen inkonsistente Positionierung auf verschiedenen Seiten.
+
Inkonsistente UI
+
+
+
+ +
+
🟡 Hoch
+
+
+
🗂️
+
Onboarding: 3 Screens für 11 Module
+
3 generische Steps erklären nicht Budget, RRule-Wiederholungen oder Calendar-Sync. Kein Feature-Discovery danach.
+
Discoverability
+
+
+
📊
+
Widget-Reihenfolge nicht anpassbar
+
Config speichert nur { id, visible } — kein order-Feld. Familien mit verschiedenen Prioritäten können das Dashboard nicht sinnvoll anpassen.
+
Personalisierung
+
+
+
↩️
+
Inkonsistentes Undo
+
Manche Aktionen zeigen Undo-Toast, andere nicht. Kein zentrales Undo-System. Bei Kontakt/Notiz löschen fehlt der Weg zurück.
+
Fehlererholung
+
+
+
📡
+
Kein sichtbarer Offline-Indikator
+
Service Worker existiert, aber kein Offline-Banner in der App-Shell. Nutzer merken Offline erst beim API-Fehler — zu spät.
+
PWA-Erfahrung
+
+
+
⌨️
+
Keine Keyboard Shortcuts
+
30 % Desktop-Nutzung ohne globale Shortcuts. Kein „N" für neu, kein „/" für Suche. Power-User auf Maus angewiesen.
+
Desktop-Ergonomie
+
+
+
+ +
+
🟢 Mittel
+
+
+
📦
+
reminders.css global geladen
+
Wird auf allen Seiten geparst, nicht lazy. Kein Blocking-Problem, aber vermeidbare CSS-Last auf Seiten ohne Reminder-UI.
+
Performance
+
+
+
👉
+
Swipe nur Tasks & Shopping
+
Swipe-Reveal fehlt bei Kontakten, Notizen, Geburtstagen. Inkonsistente Erwartungshaltung im selben App-Kontext.
+
Interaktions-Konsistenz
+
+
+
🎨
+
11 Modulfarben gleichzeitig im Dashboard
+
Wenn alle Widgets sichtbar sind, treffen 11 Akzentfarben aufeinander. Das Dashboard kann farblich überladen wirken.
+
Visuelle Ruhe
+
+
+
💬
+
Toast: kein Swipe-to-Dismiss
+
Toasts sind nicht wegwischbar. Auf iOS/Android ist Swipe-to-Dismiss eine fest etablierte Konvention — ihr Fehlen fällt auf.
+
Mobile-Konvention
+
+
+
+ +
+ +
+
✨ Was bereits ausgezeichnet ist
+
+
🌙 Dark Mode Architektur
Private/Public-Token-Pattern ist mustergültig. Alle Kontraste WCAG-geprüft.
+
♿ 4 A11y-Schichten
reduced-motion, reduced-transparency, prefers-contrast, forced-colors — alle implementiert.
+
🍎 iOS-PWA-Bewusstsein
100dvh + webkit-Fallback, safe-area-inset, Flex-Kind statt fixed nav.
+
💎 Glass Design System
@supports-basiert, opake Fallbacks für reduced-transparency, konsistente Token.
+
🎭 Modul-Theming
--active-module-accent System elegant — FAB, Nav, Toggles spiegeln aktive Seite.
+
📐 Design Tokens
Vollständige Skala, kaum Hardcoding. Basis für alles weitere.
+
+
+ + + diff --git a/.superpowers/brainstorm/958518-1777309033/content/02-solutions.html b/.superpowers/brainstorm/958518-1777309033/content/02-solutions.html new file mode 100644 index 0000000..49d0938 --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/content/02-solutions.html @@ -0,0 +1,629 @@ + + + + + +Oikos — Lösungsvorschläge + + + + +
+
Lösungsvorschläge · Oikos · April 2026
+

Konkrete Optimierungen — sortiert nach Impact

+

13 Punkte · 4 kritisch · 5 hoch · 4 mittel

+
+ + + + +
+
+
1
+
+
Kritisch · 1–2 h
+
Sidebar: title-Tooltips für icon-only Modus
+
Nutzer bei 1024–1279 px sehen 11 Icons ohne jede Beschriftung. Ein einfaches title-Attribut auf jedem Nav-Item genügt als sofortiger Fix. Langfristig: expanded sidebar ab 1280 px früher aktivieren.
+
+
+
+
+
Ist-Zustand
+
+
+
+
+
+
+
+
+

Kein Hinweis
was die Icons bedeuten

+
+
+
+
Soll-Zustand
+
+
+
+
+
Dashboard
+
+
+
+
+
+
+

Tooltip bei
Hover/Focus sichtbar

+
+
+
+
+
router.js — Nav-Item Rendering+1 Zeile
+
+// In renderAppShell() oder wo nav-items erzeugt werden: +- a.setAttribute('aria-label', label); ++ a.setAttribute('aria-label', label); ++ a.setAttribute('title', label); // Tooltip für collapsed sidebar +
+/* Optional: CSS-Tooltip statt native title (für Styling) */ ++ .nav-sidebar .nav-item[title]:hover::after { ++ content: attr(title); ++ position: absolute; ++ left: calc(100% + 10px); ++ /* ... Tooltip-Styles */ ++ } +
+
+
+ +
+ + +
+
+
2
+
+
Kritisch · 5 min
+
Modal-Close: von 40 px auf 44 px
+
Ein Einzeiler in layout.css. Der Fehler liegt darin, dass --target-md (40 px) statt --target-base (44 px) verwendet wird. Das Token existiert bereits — es muss nur gewechselt werden.
+
+
+
+
+
Problem: 40 × 40 px
+
+
+
+
+
40 × 40 px–target-md
4 px unter Minimum
+
+
+ Ein durchschnittlicher Fingertipp belegt 44–50 px. Beim Schließen unter Stress (mit einer Hand, unterwegs) erhöht sich die Fehlklickrate spürbar. +
+
+
+
+
+
Fix: 44 × 44 px
+
+
+
+
+
44 × 44 px--target-base
Apple HIG ✓
+
+
+
+
+
+
+
layout.css · .modal-panel__close1 Zeile
+
+- width: var(--target-md); /* 40px */ +- height: var(--target-md); /* 40px */ ++ width: var(--target-base); /* 44px — Apple HIG Minimum */ ++ height: var(--target-base); /* 44px */ +
+
+
+ +
+ + +
+
+
3
+
+
Kritisch · 15 min
+
Widget-Links: Tap-Target auf 44 px bringen
+
Der „Alle anzeigen →"-Link in jedem Dashboard-Widget hat keinen definierten min-height. Bei 12 px Text liegt der tatsächliche Klickbereich bei etwa 16–18 px — weit unter iOS-Minimum. Fix: padding + min-height in dashboard.css.
+
+
+
+
+
Ist-Zustand
+
+
+
+
Aufgaben
+ Alle → +
+
+
+

Tap-Target ≈ 16 px Höhe

+
+
+
+
Soll-Zustand
+
+
+
+
Aufgaben
+ Alle → +
+
+
+

Tap-Target ≥ 32 px mit Padding

+
+
+
+
+
dashboard.css · .widget__link
+
++ .widget__link { ++ min-height: var(--target-base); /* 44px */ ++ display: inline-flex; ++ align-items: center; ++ padding: 0 var(--space-2); ++ border-radius: var(--radius-sm); ++ } ++ .widget__link:hover { ++ background: color-mix(in srgb, var(--widget-accent) 10%, transparent); ++ } +
+
+
+ +
+ + +
+
+
4
+
+
Kritisch · 30 min
+
FAB konsolidieren: .fab + .page-fab → eine Klasse
+
Beide Klassen definieren fast denselben Button mit leicht abweichender bottom-Berechnung. Das führt zu inkonsistenter Positionierung. Die Lösung: eine Klasse, ein Token, alle Seiten konsistent.
+
+
+
+
+
Ist-Zustand: 2 Klassen
+
+
+
+
+
+
.fab
+
bottom: nav-height + safe-area + space-4
+
+
+
+
+
+
.page-fab
+
bottom: nav-bottom-height + 24px + safe-area
+
+
+
+

Verschiedene bottom-Werte → visuell inkonsistent je nach Seite

+
+
+
+
Soll-Zustand: 1 Klasse
+
+
+
+
+
+
.page-fab (Canonical)
+
Einheitliche bottom-Formel, alle Seiten
+
+
+
+

Alle Seiten nutzen .page-fab, .fab wird entfernt oder als Alias gesetzt

+
+
+
+
+ +
+ + + +
+
+
5
+
+
Hoch · 2–4 h
+
Dashboard: Widget-Reihenfolge anpassbar machen
+
Die Widget-Config speichert aktuell nur { id, visible }. Ein order-Feld hinzufügen und im Customization-Modal drag-sortierbar machen (per touch-action: none + pointer events, kein externes Drag-Library nötig).
+
+
+
+
+
Ist-Zustand: Feste Reihenfolge
+
+
+
+
+
Dashboard
+
⊙ sichtbar
+
+
+
+
Aufgaben
+
⊙ sichtbar
+
+
+
+
Kalender
+
⊙ sichtbar
+
+
+

Reihenfolge fest codiert — nicht änderbar

+
+
+
+
Soll-Zustand: Drag-to-reorder
+
+
+
+
+
+
Dashboard
+
+
+
+
+
+
Kalender
+
+
+
+
+
+
Aufgaben
+
+
+
+

⠿ Handle zum Sortieren — gespeichert in localStorage

+
+
+
+
+
dashboard.js — Config-Schema
+
+- const DEFAULT_WIDGET_CONFIG = WIDGET_IDS.map((id) => ({ id, visible: true })); ++ const DEFAULT_WIDGET_CONFIG = WIDGET_IDS.map((id, i) => ({ id, visible: true, order: i })); +
+// Beim Laden: nach order sortieren ++ config.sort((a, b) => a.order - b.order); +
+
+
+ +
+ + +
+
+
6
+
+
Hoch · 1 h
+
Offline-Banner in App-Shell
+
Der Service Worker ist vorhanden, aber die App gibt kein visuelles Feedback zum Offline-Zustand. Ein kleines Banner direkt unter der Navigation (wenn navigator.onLine false ist) ist die robusteste Lösung — kein Flickering, immer sichtbar.
+
+
+
+
Soll-Zustand: Shell-Level Banner
+
+
+ 📡 + Offline — Änderungen werden gespeichert und beim nächsten Verbindungsaufbau synchronisiert. + Offline +
+

Erscheint unter Nav-Bar, verschwindet automatisch wenn online

+
+
+
+
router.js — App-Shell Setup
+
++ function initOfflineBanner() { ++ const banner = document.getElementById('offline-banner'); ++ const update = () => banner.hidden = navigator.onLine; ++ window.addEventListener('online', update); ++ window.addEventListener('offline', update); ++ update(); ++ } +
+
+
+ +
+ + +
+
+
7
+
+
Hoch · 2–3 h
+
Globale Keyboard Shortcuts (Desktop)
+
30 % der Nutzer verwenden Desktop. Ein zentrales Keyboard-Shortcut-System im Router beschleunigt häufige Aktionen erheblich. Alle Shortcuts per ? einsehbar.
+
+
+
+
/Suche öffnen
+
NNeu (kontextabhängig)
+
G D→ Dashboard
+
G T→ Tasks
+
G C→ Kalender
+
EscModal / Sheet schließen
+
?Shortcut-Übersicht
+
⌘KCommand Palette
+
+
+ +
+ + +
+
+
8
+
+
Hoch · 3–4 h
+
Zentrales Undo-System für destruktive Aktionen
+
Aktuell haben manche Aktionen Undo-Toasts, andere nicht. Eine zentrale undoStack-Utility mit standardisiertem Toast-Muster schafft konsistente Sicherheit über alle Module hinweg.
+
+
+
+
Soll-Zustand: konsistente Undo-Abdeckung
+
+
+
Aufgabe löschen
✓ Undo vorhanden
+
Eintrag Einkaufsliste löschen
✓ Undo vorhanden
+
Kontakt löschen
✗ fehlt
+
Notiz löschen
✗ fehlt
+
Geburtstag löschen
✗ fehlt
+
Mahlzeit löschen
✗ fehlt
+
+

→ Alle DELETE-Aktionen über eine zentrale deleteWithUndo(url, label) Funktion routen

+
+
+
+ +
+ + +
+
+
9
+
+
Hoch · 3 h
+
Onboarding: Modul-spezifische Erst-Nutzung
+
Statt 3 generischen Screens beim ersten Start: leere Zustände auf jeder Seite mit einem konkreten Tipp für genau dieses Modul. Zusätzlich: ein permanentes „?" / Hilfe-Icon in der Toolbar für den Onboarding-Replay.
+
+
+
+
+
📋
+
Aufgaben
+
Tippe + um deine erste Aufgabe zu erstellen. Wische links zum Löschen.
+
+
+
+
📅
+
Kalender
+
Verbinde deinen Google Kalender unter Einstellungen → Kalender-Sync.
+
+
+
+
💰
+
Budget
+
Lege Kategorien und ein Monatsbudget fest. Einnahmen und Ausgaben werden automatisch summiert.
+
+
+
+
🔔
+
Erinnerungen
+
RRule ermöglicht Wiederholungen: täglich, wöchentlich, oder komplex wie „jeden 2. Montag".
+
+
+
+
+7 weitere
+
Jedes Modul erklärt sich beim ersten Besuch selbst.
+
+
+
+ +
+ + + +
+
+
10–13
+
+
Mittel · Backlog
+
Weitere Optimierungen
+
+
+
+
+
🟢 Toast Swipe-to-Dismiss
+
+

+ pointerdown + pointermove auf .toast → bei + >40 px horizontaler Bewegung toast--out Klasse setzen und entfernen. + Entspricht iOS/Android-Konvention. Ca. 30 Zeilen JS. +

+
+
+
+
🟢 Swipe-Reveal auf alle Listen
+
+

+ Das bestehende .swipe-row Pattern auf Kontakte, Notizen und Geburtstage + ausweiten. Die Basis-CSS existiert bereits in layout.css — + nur modul-spezifische Reveal-Farben und JS-Handler ergänzen. +

+
+
+
+
🟢 Dashboard: weniger Farbrauschen
+
+

+ Widget-Icons auf neutrale --color-text-secondary setzen, Widget-Akzentfarbe + nur für Badge und Link reservieren. Reduziert visuelle Überforderung wenn alle + 11 Widgets gleichzeitig sichtbar sind. +

+
+
+
+
🟢 reminders.css lazy laden
+
+

+ reminders.css dynamisch per <link rel="stylesheet"> + nur in den Seiten laden, die Reminders anzeigen — analog zum bestehenden + Lazy-Loading-Pattern für Page-Module. +

+
+
+
+
+ + + diff --git a/.superpowers/brainstorm/958518-1777309033/state/server-stopped b/.superpowers/brainstorm/958518-1777309033/state/server-stopped new file mode 100644 index 0000000..7870285 --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/state/server-stopped @@ -0,0 +1 @@ +{"reason":"idle timeout","timestamp":1777311074842} diff --git a/.superpowers/brainstorm/958518-1777309033/state/server.pid b/.superpowers/brainstorm/958518-1777309033/state/server.pid new file mode 100644 index 0000000..1b0c8d1 --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/state/server.pid @@ -0,0 +1 @@ +958547 diff --git a/public/pages/birthdays.js b/public/pages/birthdays.js index 2d39140..aa99607 100644 --- a/public/pages/birthdays.js +++ b/public/pages/birthdays.js @@ -20,6 +20,50 @@ function initials(name) { .join('') || '?'; } +const REMINDER_OFFSETS = () => [ + { value: '', label: t('reminders.offsetNone') }, + { value: '0', label: t('reminders.offsetAtTime') }, + { value: '15', label: t('reminders.offset15min') }, + { value: '60', label: t('reminders.offset1hour') }, + { value: '1440', label: t('reminders.offset1day') }, + { value: '2880', label: t('reminders.offset2days') }, + { value: '10080', label: t('reminders.offset1week') }, + { value: '20160', label: t('reminders.offset2weeks') }, + { value: 'custom', label: t('reminders.offsetCustom') }, +]; + +function renderBirthdayReminderSection(birthday = null) { + const currentOffset = birthday?.reminder_offset ?? '0'; + const customAmount = birthday?.reminder_custom_amount || 1; + const customUnit = birthday?.reminder_custom_unit || 'days'; + return ` +
+
+ + +
+ +
`; +} + function ageNote(birthday) { if (birthday.days_until === 0) return t('birthdays.ageNoteToday', { age: birthday.next_age }); if (birthday.days_until === 1) return t('birthdays.ageNoteTomorrow', { age: birthday.next_age }); @@ -327,6 +371,7 @@ function openBirthdayModal({ mode, birthday = null }) { + ${renderBirthdayReminderSection(birthday)}
${t('birthdays.calendarHint')}