Unified /einstellungen page replaces the standalone CalDAV screen. Three
tabs today (Profil / Benachrichtigungen / CalDAV); adding more is additive
(one <a> in the tab nav, one <section> panel, one loader). Tab switching
is client-side from ?tab=<name> — default tab is Profil.
Profil tab lets users fix onboarding data without admin intervention:
display name, office, role, Dezernat, language. Email is read-only (the
source of truth is auth.users and an account-level change is out of
scope for the settings page).
Benachrichtigungen tab exposes deadline reminder preferences as a master
toggle plus three per-kind sub-toggles (overdue / tomorrow / weekly).
Preferences land in paliad.users.email_preferences (JSONB); missing keys
are treated as opt-in so existing users keep the behaviour they had
before the page shipped.
CalDAV tab is the old /einstellungen/caldav screen ported inline.
/einstellungen/caldav now 301-redirects to /einstellungen?tab=caldav so
bookmarks keep working.
Backend:
- PATCH /api/me (handlers/users.go) mutates the caller's paliad.users
row. Attempts to include "email" in the body return 400 — the field is
always server-authoritative.
- UserService.UpdateProfile builds a dynamic UPDATE from the pointer
fields supplied; omitted keys are left untouched. Re-uses the
admin-bootstrap guard for role changes.
- GetByID SELECT now includes lang + email_preferences so /api/me
returns the data the settings page needs without a second round-trip.
- ReminderService consults email_preferences before sending — the helper
reminderEnabled covers the master switch and per-kind overrides; corrupt
JSON falls back to on so a bad row can't silence reminders.
- Migration 017 adds email_preferences jsonb NOT NULL DEFAULT '{}' and
upgrades lang from nullable (from 016) to NOT NULL DEFAULT 'de' with a
one-shot backfill. Down restores the nullable lang and drops
email_preferences.
Model change: User.Lang moved from *string to string — it's NOT NULL in
the DB now, so the indirection was carrying no information. Inviter.Lang
and reminder row structs followed suit; the templates and callers used
""/"en" comparisons that translate 1:1.
Sidebar: the "Einstellungen" group now links to /einstellungen (instead
of just /einstellungen/caldav); the CalDAV sub-item is folded into the
tab nav on the page itself.
Tests: reminderEnabled has table-driven coverage (master switch,
per-kind, corrupt JSON, non-bool values). DB-backed user tests still
skip without TEST_DATABASE_URL as before.
Verified: go build ./..., go vet ./..., go test ./..., bun run build —
all clean.
23 lines
996 B
SQL
23 lines
996 B
SQL
-- Settings page (t-paliad-022): per-user email notification preferences and
|
|
-- a non-null language column.
|
|
--
|
|
-- email_preferences is a free-form JSONB bag. The app reads well-known keys
|
|
-- today (deadline_reminders, deadline_reminders.overdue, .tomorrow, .weekly);
|
|
-- unknown keys are ignored. An empty object means "all reminders on" — the
|
|
-- reminder_service treats a missing key as opt-in so existing users keep the
|
|
-- behaviour they had before the settings page shipped.
|
|
--
|
|
-- lang was added nullable in 016. Now that the settings page lets every user
|
|
-- pick DE/EN, backfill any remaining NULLs to the German default and enforce
|
|
-- NOT NULL so downstream code (reminder templates) can stop dancing around
|
|
-- a *string.
|
|
|
|
ALTER TABLE paliad.users
|
|
ADD COLUMN IF NOT EXISTS email_preferences jsonb NOT NULL DEFAULT '{}'::jsonb;
|
|
|
|
UPDATE paliad.users SET lang = 'de' WHERE lang IS NULL;
|
|
|
|
ALTER TABLE paliad.users
|
|
ALTER COLUMN lang SET NOT NULL,
|
|
ALTER COLUMN lang SET DEFAULT 'de';
|