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 <rafaelfoster@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add CalDAV accounts card to Settings page with:
* List of configured accounts showing URL and last sync
* Expandable calendar list with enable/disable checkboxes
* Sync Now, Refresh Calendars, and Delete actions per account
* Add Account modal with name, URL, username, password fields
- Add CalDAV target selector to event modal:
* Dropdown showing local and all enabled CalDAV calendars
* Grouped by account using optgroups
* Pre-selects current target when editing events
* Includes target_caldav_account_id and target_caldav_calendar_url in save
- Add CalDAV component styles to settings.css:
* Account cards with header, meta, and action sections
* Expandable calendar details with checkboxes and color dots
* Empty state for no accounts
- Add missing i18n keys for calendar enable/disable, refresh, delete confirm
- Load CalDAV targets async when modal opens
- Admin-only access to account management
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two root causes fixed:
- dashboard.css: remove `overflow: visible` from the "Admin Dashboard Layout"
block that overrode the earlier `overflow: clip`, letting child content escape
the dashboard container and cause layout overflow
- layout.css: replace `overflow-x: clip` with `overflow-x: hidden` on
.app-content so layout overflow is properly contained at the scroll
container level (clip only clips painting, not layout)
Co-authored-by: Ulas Kalayci <ulas.kalayci@googlemail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Overlapping timed events in week/day views now render side-by-side using a column-layout algorithm
- Calendar events support optional file attachments (images, PDFs, documents up to 5 MB)
- Attachment images shown in event popup; other files as download links
- Birthday modal redesigned with photo/avatar side-by-side with name/date fields
- DB migration 27: adds attachment_name, attachment_mime, attachment_size, attachment_data columns to calendar_events
- Server-side MIME allowlist and size validation for attachments
- i18n: all 15 locales include new attachment keys (de properly translated)
Co-Authored-By: Rafael Foster <rafaelfoster@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the sequential fade-out → wait → fade-in pattern that causes
a visible black flash in dark mode between page transitions. Replaces
with immediate crossfade (new page fades in over old content, no wait).
Changes:
- layout.css: Add page-crossfade-in keyframe (0.18s) + prefers-reduced-motion override
- router.js: Remove outClass/inClass direction logic and oldPage fadeout wait
The new approach:
1. Old page remains visible until new page renders
2. New page fades in (0.18s) with full opacity, overlaying old content
3. No 120ms delay = no visible flash in dark mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>