Icons were cached with immutable/30-day headers, so Chrome Android kept
serving the old placeholder even after new icons were deployed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add id field and display_override to manifest.json for reliable
Chrome Android PWA recognition
- Serve manifest.json with application/manifest+json MIME type
- Add /i18n.js and locale files to SW app shell cache (were missing)
- Bump SW cache version to v21
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Admin can now enter CalDAV URL, Apple-ID and app-specific password
directly in Settings; credentials are tested live before saving and
stored in sync_config (take precedence over .env); disconnect clears
DB-stored credentials without server restart. Auto-sync interval
(15 min, configurable via SYNC_INTERVAL_MINUTES) was already in place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
expandRecurringEvents() iterates from the event's original start date,
generating all occurrences within the requested window using the existing
nextOccurrence() service (max 1000 iterations). The SQL query is extended
to also fetch recurring events that started before the window. Event
duration is preserved across instances. Virtual instances carry
is_recurring_instance=1 and are shown with a repeat icon in the agenda
view. /upcoming expands across a 90-day forward window.
Closes BL-01.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds schema migration v3 (recurrence_parent_id column + budget_recurrence_skipped
table). On every GET /api/v1/budget, the server checks all recurring originals
(is_recurring=1, no parent) and creates missing instances for the requested month
using the same day-of-month (clamped to the last day). Deleted instances are
recorded in budget_recurrence_skipped so they are not recreated on the next visit.
Generated instances are shown with a ↩ indicator in the transaction list.
Closes BL-05.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Aufgaben-Widget zeigte nur high/urgent Tasks mit Fälligkeit ≤48h,
Pinnwand-Widget nur explizit gepinnte Notizen. Neue Einträge waren
dadurch im Dashboard unsichtbar.
- Aufgaben: alle offenen Tasks (sortiert nach Priorität), Limit 5
- Notizen: neueste 3 (gepinnte zuerst, dann nach Aktualisierung)
- Greeting-Chip zählt weiterhin nur high/urgent Tasks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Dark Mode: .form-input hatte kein Styling, Browser-Default führte zu
weißem Text auf weißem Hintergrund. Jetzt mit .input zusammengefasst.
2. Essensplan: DATE_RE fehlte im Import (ReferenceError), db.transaction()
wurde doppelt aufgerufen (3 Stellen).
3. Dashboard: Router-Guard verhinderte Re-Render bei Rücknavigation,
Widgets zeigten veraltete Daten.
4. Einkaufsliste: Mengenfeld hatte abweichende Hintergrundfarbe und
überdimensionierte min-height.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auf Desktop wird das Wetter-Widget über allen anderen Widgets platziert
mit horizontalem Layout (aktuelles Wetter links, Vorhersage rechts).
Vorhersagezeitraum skaliert mit Bildschirmbreite: 3 Tage (Mobil),
4 Tage (Tablet), 5 Tage (Desktop/Wide).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- CSP: SHA-256-Hash für Theme-Detection Inline-Script hinzugefügt
- docker-compose: SESSION_SECURE=false, damit HSTS und
upgrade-insecure-requests bei direktem HTTP-Zugriff deaktiviert sind
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dark Mode: Manueller Theme-Switch (System/Hell/Dunkel) in Einstellungen
mit localStorage-Persistenz und Flash-Prevention via data-theme Attribut.
RRULE UI: Wiederholungs-Formular in Aufgaben- und Kalender-Modals mit
Frequenz (Täglich/Wöchentlich/Monatlich), Intervall, Wochentag-Auswahl
und optionalem Enddatum. Backend-Routen für is_recurring/recurrence_rule
in POST/PUT erweitert. Repeat-Icon auf wiederkehrenden Einträgen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- helmet: upgrade-insecure-requests und HSTS nur bei SESSION_SECURE=true
- Service Worker Cache-Version auf v10 hochgezählt
- Debug-Code aus router.js entfernt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- connect-sqlite3 durch eigenen BetterSQLiteStore ersetzt (sessions-Tabelle
in der bestehenden DB, keine native Kompilierung nötig)
- db.init() vor require('./auth') gezogen damit BetterSQLiteStore-Konstruktor
db.get() erfolgreich aufrufen kann
- router.js: App-Shell und pageWrapper vor module.render() in DOM einfügen
damit document.getElementById() in Seiten-Modulen funktioniert
- Seiten-Module (meals, notes, contacts, calendar, budget): _container-Referenz
eingeführt, alle document.getElementById() auf _container.querySelector() bzw.
document.querySelector() für body-Elemente umgestellt
- login.js: User-Objekt nach erfolgreichem Login an navigate() übergeben
damit auth.me()-Roundtrip entfällt
- calendar.js: /users → /auth/users korrigiert (404-Fix)
- SW-Cache v8 (erzwingt Reload aller gecachten Seiten-Module)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- lucide.min.js v0.469.0 lokal gecacht (kein unpkg-Request mehr)
- Source-Map-Referenz aus Bundle entfernt (behebt NetworkError in DevTools)
- unpkg.com aus CSP entfernt (nicht mehr benötigt)
- preconnect zu unpkg.com aus index.html entfernt
- lucide.min.js zum SW-App-Shell-Cache hinzugefügt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- router.js: navigateTo() existierte nicht → ReferenceError nach fehlgeschlagenem
auth.me(). Ersetzt durch navigate() + currentPath-Reset damit die Weiterleitung
nicht durch den currentPath-Guard geblockt wird.
- csrf.js: SESSION_SECURE=false wurde nicht berücksichtigt → CSRF-Cookie war über
HTTP nicht für JS lesbar → alle schreibenden API-Calls nach Login scheiterten mit 403.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SESSION_SECURE=false in .env deaktiviert das Secure-Flag für Session-
und CSRF-Cookie. Notwendig wenn die App direkt per HTTP erreichbar ist
(kein Nginx/HTTPS davor). Standard bleibt secure=true in production.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LICENSE: MIT-Lizenz (bereits vorhanden, korrekt)
- package.json: "license": "MIT" ergänzt
- README.md: Lizenz-Badge auf MIT aktualisiert, Lizenz-Sektion angepasst
- .gitignore: .claude/ und *.txt hinzugefügt (verhindert versehentliches
Committen von Claude Code Einstellungen und Token-Textdateien)
- server/auth.js: Fail-Fast in Produktion wenn SESSION_SECRET fehlt;
Fallback-String auf 'dev-only-secret-not-for-production' umbenannt
(klarere Intention, kein bekannter Produktions-Wert im Public Repo)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- GET /shopping — alle Listen mit item_total/item_checked Zähler
- POST/PUT/DELETE /shopping/:listId — Listen-CRUD
- GET /shopping/:listId/items — Artikel nach Supermarkt-Gang sortiert
(Obst→Backwaren→Milch→Fleisch→Tiefkühl→Getränke→Haushalt→Drogerie→Sonstiges)
Abgehakte innerhalb der Kategorie ans Ende
- POST /shopping/:listId/items — Artikel hinzufügen
- PATCH /shopping/items/:id — Artikel aktualisieren (abhaken, umbenennen)
- DELETE /shopping/items/:id — Einzelartikel löschen
- DELETE /shopping/:listId/items/checked — nur abgehakte löschen
- GET /shopping/suggestions?q= — Autocomplete aus bisherigen Einträgen
Frontend:
- Multi-Listen-Tabs (horizontal scrollbar, Artikel-Zähler im Tab)
- Quick-Add: Name + Menge + Kategorie-Dropdown in einer Zeile
- Autocomplete-Dropdown mit Tastaturnavigation (↑↓ Enter Escape)
- Optimistisches Toggle: Checkbox reagiert sofort, Rollback bei Fehler
- "Abgehakt löschen"-Button erscheint dynamisch bei checked > 0
- Listen umbenennen/löschen direkt im Header
- Kategorie-Icons (Lucide) in Gruppen-Überschriften
Tests: 17/17 bestanden (71 gesamt)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>