summaryrefslogtreecommitdiff
path: root/internal/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handlers')
-rw-r--r--internal/handlers/timeline_logic.go28
-rw-r--r--internal/handlers/timeline_logic_test.go50
2 files changed, 77 insertions, 1 deletions
diff --git a/internal/handlers/timeline_logic.go b/internal/handlers/timeline_logic.go
index 67ba09d..e7c1279 100644
--- a/internal/handlers/timeline_logic.go
+++ b/internal/handlers/timeline_logic.go
@@ -125,7 +125,10 @@ func BuildTimeline(ctx context.Context, s *store.Store, start, end time.Time) ([
}
}
- // 6. Fetch native (doot-owned) tasks
+ // 6. Fetch native (doot-owned) tasks due within the range, plus any
+ // overdue tasks (due before the range's start) so they still surface --
+ // GetNativeTasksByDateRange's lower bound would otherwise drop them
+ // before ComputeDaySection ever gets a chance to mark them IsOverdue.
nativeDated, err := s.GetNativeTasksByDateRange(start, end)
if err != nil {
log.Printf("Warning: failed to read native tasks: %v", err)
@@ -149,6 +152,29 @@ func BuildTimeline(ctx context.Context, s *store.Store, start, end time.Time) ([
}
}
+ nativeOverdue, err := s.GetOverdueNativeTasks(start)
+ if err != nil {
+ log.Printf("Warning: failed to read overdue native tasks: %v", err)
+ } else {
+ for _, task := range nativeOverdue {
+ if task.DueDate == nil {
+ continue
+ }
+ item := models.TimelineItem{
+ ID: task.ID,
+ Type: models.TimelineItemTypeTask,
+ Title: task.Content,
+ Time: *task.DueDate,
+ Description: task.Description,
+ OriginalItem: task,
+ IsCompleted: task.Completed,
+ Source: "doot",
+ }
+ item.ComputeDaySection(now)
+ items = append(items, item)
+ }
+ }
+
nativeUndated, err := s.GetUndatedNativeTasks()
if err != nil {
log.Printf("Warning: failed to read undated native tasks: %v", err)
diff --git a/internal/handlers/timeline_logic_test.go b/internal/handlers/timeline_logic_test.go
index 0b7bc37..7d3f6b5 100644
--- a/internal/handlers/timeline_logic_test.go
+++ b/internal/handlers/timeline_logic_test.go
@@ -293,6 +293,56 @@ func TestBuildTimeline_ReadsCalendarEventsFromStore(t *testing.T) {
}
}
+// TestBuildTimeline_IncludesOverdueNativeTasks proves the 2026-07-12 fix: a
+// native task whose due_date is BEFORE the requested range's start must
+// still appear in the timeline, marked IsOverdue, instead of being silently
+// dropped. Root cause: GetNativeTasksByDateRange's SQL bound (due_date >=
+// start) excluded overdue tasks from ever being fetched, so ComputeDaySection
+// never got a chance to set IsOverdue -- both the web Timeline view and the
+// widget API (which both call BuildTimeline with a start of "today") showed
+// zero overdue tasks even though the Tasks tab (which calls the unbounded
+// GetNativeTasks) showed them fine.
+func TestBuildTimeline_IncludesOverdueNativeTasks(t *testing.T) {
+ db, cleanup := setupTestDB(t)
+ defer cleanup()
+
+ now := time.Now()
+ today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
+ overdueDate := today.AddDate(0, 0, -12) // 12 days in the past
+
+ if err := db.CreateNativeTask(models.Task{
+ ID: "overdue-1",
+ Content: "Pay the water bill",
+ DueDate: &overdueDate,
+ }); err != nil {
+ t.Fatalf("Failed to create native task: %v", err)
+ }
+
+ start := today
+ end := today.AddDate(0, 0, 2)
+
+ items, err := BuildTimeline(context.Background(), db, start, end)
+ if err != nil {
+ t.Fatalf("BuildTimeline failed: %v", err)
+ }
+
+ var found *models.TimelineItem
+ for i := range items {
+ if items[i].ID == "overdue-1" {
+ found = &items[i]
+ }
+ }
+ if found == nil {
+ t.Fatal("expected overdue native task to appear in timeline, but it was missing")
+ }
+ if !found.IsOverdue {
+ t.Error("expected overdue native task to have IsOverdue = true")
+ }
+ if found.Title != "Pay the water bill" {
+ t.Errorf("Title: got %q, want %q", found.Title, "Pay the water bill")
+ }
+}
+
func TestFetchCalendarEvents_CacheFallbackOnAPIError(t *testing.T) {
db, cleanup := setupTestDB(t)
defer cleanup()