Replaces the per-deadline reminder model (overdue / tomorrow /
due_today_evening / weekly templates and four per-kind send paths) with
one bundled digest per (user, slot, local-date) — owner + project leads +
global_admins as audience tiers, three category sections per email.
Service rewrite (internal/services/reminder_service.go):
- RunOnce iterates users, evaluates morning/evening slot per user's tz,
calls runSlotForUser for each match.
- runSlotForUser checks the slot+date dedup (migration 025), fetches the
three pending-deadline categories visible to u (overdue / due_today /
due_warning at u.reminder_warning_offset_days), composes a digest, and
inserts the dedup row only on successful send.
- Audience filter applied per row in Go: due_warning to owner/lead,
due_today to owner/lead (+global_admin in evening), overdue to
owner/global_admin (NOT lead — system failure escalates past the team).
- Subject ladder: ÜBERFÄLLIG / SYSTEMAUSFALL when overdues are in the
bundle; DRINGEND on evening when due_today still pending; "Frist-
Erinnerung: N offen" otherwise. EN equivalents.
- Retired sendPerFrist, sendWeekly, deliverFristReminder, deliverWeekly,
buildSubject, slotForKind, matchesLocalDueDate.
Templates:
- Added deadline_digest.html with three category sections (red/amber/
neutral), DRINGEND wording on evening, IsOtherOwner attribution row.
- Removed deadline_reminder.html, deadline_due_today.html, deadline_weekly.html.
User schema (Go side):
- models.User gains ReminderWarningOffsetDays (int, default 7) and
EscalationContactID (*uuid.UUID, nullable).
- userColumns SELECT updated; UpdateProfileInput accepts the new offset
with 1..30 validation.
Settings → Notifications UI (PR-4):
- New reminder categories: overdue / due_today / due_warning. Legacy
toggles (tomorrow, due_today_evening, weekly) removed and the legacy
pref keys are explicitly deleted from the email_preferences object on
next save so they don't linger.
- New "Vorwarnung (Tage vorher)" input (1..30, required), wired into the
PATCH /api/me payload as reminder_warning_offset_days.
- Times-section copy refreshed: "Morgen-Slot" / "Abend-Slot (Eskalation)"
with new hint text reflecting the bundled-digest model.
- DE + EN i18n strings added/updated.
Tests:
- TestCategorize, TestVisibleForCategory, TestBuildDigestSubject lock
the boundary, recipient-rule, and subject-ladder logic.
- TestRunSlotForUser (live DB, skipped without TEST_DATABASE_URL) covers
the morning/evening flow, slot+date dedup, and off-slot tick.
- TestRunSlotForUser_EmptyDigest enforces the no-spam rule.
- TestDeliverDigest_RendersTemplate runs the new template on the
digestRow shape so a typo would fail before any SMTP I/O.
- TestRenderTemplateDeadlineDigest replaces the deleted reminder/weekly
template tests.
go build/vet/test + bun run build all clean.