feat(tasks): due dates with optional time; alarms at the right moment

Due dates can carry a time (YYYY-MM-DDTHH:MM). Alarms fire at that time,
or 09:00 for all-day tasks, minus the user's lead time, in TZ (default
Europe/Berlin, tzdata embedded). A new due date or reopening a task
re-arms the alarm. Recurrence keeps the time; Markdown export/import
carry it. The UI shows relative labels ("Heute, 14:30"), marks overdue
tasks to the minute and groups by local day instead of the UTC date.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 07:48:37 +01:00
1 parent 300fd6a7be
commit 344f5e4ab2
21 files changed
+391 -50

No files matched your search

+7 -5
View File
@@ -4,13 +4,13 @@ Code: [TasksView.tsx](../../frontend/src/features/triage/TasksView.tsx), [TaskDe
## Datenmodell
Eine Aufgabe ist ein `thought` ([database.md](../database.md)) mit `kanbanStatus`, `priority` (`low`/`normal`/`high`/`urgent`), `dueDate` (nur Datum), `notes` (Markdown), `subtasks` (`[{id, title, isCompleted}]`), `tags`, `taskListId`, `sortOrder` und `assignees` (Benutzer-IDs). Aufgaben ohne Liste sind persönlich.
Eine Aufgabe ist ein `thought` ([database.md](../database.md)) mit `kanbanStatus`, `priority` (`low`/`normal`/`high`/`urgent`), `dueDate` (`YYYY-MM-DD` oder mit Uhrzeit `YYYY-MM-DDTHH:MM`; die API nimmt auch `YYYY-MM-DD HH:MM` an), `notes` (Markdown), `subtasks` (`[{id, title, isCompleted}]`), `tags`, `taskListId`, `sortOrder` und `assignees` (Benutzer-IDs). Aufgaben ohne Liste sind persönlich.
## Ansichten
- **Liste** mit Filtern (offen, erledigt, Papierkorb), Gruppierung (z. B. nach Status oder Fälligkeit), Sortierung und Hashtag-Filter.
- **Kanban:** Spalten pro Liste frei konfigurierbar (Name, Farbe, „Erledigt“-Spalte). Standard: *Zu erledigen*, *In Bearbeitung*, *Wartet auf andere*, *Erledigt*. Eine Karte in eine „Erledigt“-Spalte zu ziehen setzt `isCompleted`.
- Drag & Drop sortiert in der Liste und zwischen Spalten; ein Klick öffnet das Detail-Fenster (Titel, Status, Priorität, Fälligkeit mit Schnellwahl, Liste, Hashtags, Unteraufgaben mit Fortschrittsbalken, Notizen).
- Drag & Drop sortiert in der Liste und zwischen Spalten; ein Klick öffnet das Detail-Fenster (Titel, Status, Priorität, Fälligkeit mit optionaler Uhrzeit und Schnellwahl, Liste, Hashtags, Unteraufgaben mit Fortschrittsbalken, Notizen).
- Aufgaben lassen sich in eine Notiz umwandeln („Als Notiz vertiefen“). Aufgaben aus Notiz-Checkboxen (`enhanced-task`) bleiben verknüpft: Abhaken wirkt in beide Richtungen (`SyncDocBlockAndTask`), „In Notiz öffnen“ springt zum Block.
## Aufgabenlisten und Freigabe
@@ -31,14 +31,14 @@ Eine Aufgabe ist ein `thought` ([database.md](../database.md)) mit `kanbanStatus
- Im Detail-Fenster unter der Fälligkeit: *Keine Wiederholung*, täglich, wöchentlich, monatlich oder jährlich (`thoughts.recurrence`, `PATCH /thoughts/{id}` mit `recurrence`). Zeilen und Kanban-Karten zeigen dann ein 🔁-Symbol.
- **Abhaken erzeugt die nächste Aufgabe** ([recurrence.go](../../internal/storage/recurrence.go)): egal ob im Detail, in der Liste, per Ziehen in „Erledigt“ im Kanban oder über die Checkbox in einer Notiz. Die erledigte Aufgabe bleibt erledigt (Historie); die neue übernimmt Titel, Tags, Priorität, Notizen, Liste, Zuweisungen und die Regel, Unteraufgaben kommen unerledigt mit, sie steht in der ersten offenen Spalte.
- **Fälligkeit:** das bisherige Datum plus ein Intervall (ohne Datum ab heute). Monate und Jahre behalten den Tag oder rutschen auf das Monatsende (31.1. → 28./29.2.).
- **Fälligkeit:** das bisherige Datum plus ein Intervall (ohne Datum ab heute); eine Uhrzeit bleibt erhalten. Monate und Jahre behalten den Tag oder rutschen auf das Monatsende (31.1. → 28./29.2.).
- Jede Aufgabe erzeugt höchstens eine Nachfolgerin (`recurrence_spawned`): Wieder öffnen und erneut abhaken legt keine zweite an.
- Die neue Aufgabe geht per `TASK_CREATED` an alle Beteiligten, sie erscheint ohne Neuladen.
- Export/Import: im Markdown-Archiv als `🔁 every week` (Format des Obsidian-Tasks-Plugins), im JSON-Backup als Feld; beides wird beim Import wiederhergestellt.
## Alarme
Der Ticker läuft jede Minute ([architecture.md](../architecture.md)). Für offene Aufgaben mit `due_date <= heute` und `alarm_sent = 0` sendet er an die Kanäle des Aufgaben-**Eigentümers**:
Der Ticker läuft jede Minute ([architecture.md](../architecture.md)). Für offene Aufgaben mit `alarm_sent = 0` sendet er, sobald der Alarmzeitpunkt erreicht ist (Uhrzeit der Fälligkeit, ganztägig 9:00, jeweils minus `notifyBeforeMinutes`, Zeitzone `TZ`), an die Kanäle des Aufgaben-**Eigentümers**:
| Kanal | Ziel | Konfiguration |
|---|---|---|
@@ -57,4 +57,6 @@ Danach wird `alarm_sent = 1` gesetzt, sobald mindestens ein Kanal erfolgreich wa
- `SMTP_SKIP_VERIFY` / Schalter „Zertifikatsprüfung deaktivieren“ akzeptiert selbstsignierte Zertifikate (Verbindung bleibt verschlüsselt).
- Fehler nennen den Schritt, an dem der Server abgelehnt hat (z. B. `SMTP-Schritt RCPT TO fehlgeschlagen: 451 …`), im Log als `smtp_step`. Test: `internal/api/mail_test.go` mit einem Fake-Server (STARTTLS, 451).
Einschränkungen ([FLAW-12](../flaws.md)): Fälligkeiten haben keine Uhrzeit, `notifyBeforeMinutes` wird ignoriert, die Serverzeit gilt, und ein verschobener Termin löst nicht erneut aus. URLs werden nicht geprüft ([FLAW-05](../flaws.md)).
Eine neue Fälligkeit oder das Wiederöffnen einer Aufgabe setzt `alarm_sent` zurück, der Alarm kommt dann erneut. Offen ([FLAW-12](../flaws.md)): Schlagen alle Kanäle fehl, wird jede Minute erneut versucht. URLs werden geprüft ([FLAW-05](../flaws.md)).
Die Oberfläche zeigt Fälligkeiten relativ („Heute, 14:30“, „Morgen“, „Fr., 30.10.“); überfällige offene Aufgaben (mit Uhrzeit minutengenau) sind rot. Gruppieren und Sortieren nach Fälligkeit berücksichtigen die Uhrzeit, ganztägige Aufgaben stehen vor denen mit Uhrzeit am selben Tag.