summaryrefslogtreecommitdiff
path: root/internal/handlers/timeline.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-02-01 10:52:28 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-02-01 10:52:28 -1000
commit1c6552117038cb7c01e016dbf1ac062e1d9f9c73 (patch)
treeff65c67a40e08a14f89fe3057a8ac4886d94b75b /internal/handlers/timeline.go
parente0e0dc11195c0e0516b45975de51df1dc98f83de (diff)
Improve timeline view with dynamic bounds, now line, and overlap handling
- Add dynamic calendar clipping: show 1 hour before/after events instead of hardcoded 6am-10pm - Add "NOW" line indicator showing current time position - Improve time label readability with larger font and better contrast - Add overlap detection with column-based indentation for concurrent events - Apply calendar view to Tomorrow section (matching Today's layout) - Fix auto-refresh switching to tasks tab (default was 'tasks' instead of 'timeline') Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers/timeline.go')
-rw-r--r--internal/handlers/timeline.go87
1 files changed, 87 insertions, 0 deletions
diff --git a/internal/handlers/timeline.go b/internal/handlers/timeline.go
index 5e583d6..fa5bcec 100644
--- a/internal/handlers/timeline.go
+++ b/internal/handlers/timeline.go
@@ -21,6 +21,19 @@ type TimelineData struct {
TodayLabel string // e.g., "Today - Monday"
TomorrowLabel string // e.g., "Tomorrow - Tuesday"
LaterLabel string // e.g., "Wednesday, Jan 29"
+
+ // Calendar view bounds (1 hour before first event, 1 hour after last)
+ TodayStartHour int
+ TodayEndHour int
+ TodayHours []int // Slice of hours to render
+
+ TomorrowStartHour int
+ TomorrowEndHour int
+ TomorrowHours []int
+
+ // Current time for "now" line
+ NowHour int
+ NowMinute int
}
// HandleTimeline renders the timeline view
@@ -70,6 +83,8 @@ func (h *Handler) HandleTimeline(w http.ResponseWriter, r *http.Request) {
TodayLabel: "Today - " + now.Format("Monday"),
TomorrowLabel: "Tomorrow - " + tomorrow.Format("Monday"),
LaterLabel: dayAfterTomorrow.Format("Monday, Jan 2") + "+",
+ NowHour: now.Hour(),
+ NowMinute: now.Minute(),
}
for _, item := range items {
switch item.DaySection {
@@ -82,5 +97,77 @@ func (h *Handler) HandleTimeline(w http.ResponseWriter, r *http.Request) {
}
}
+ // Calculate calendar bounds for Today (1 hour buffer before/after timed events)
+ data.TodayStartHour, data.TodayEndHour = calcCalendarBounds(data.TodayItems, now.Hour())
+ for h := data.TodayStartHour; h <= data.TodayEndHour; h++ {
+ data.TodayHours = append(data.TodayHours, h)
+ }
+
+ // Calculate calendar bounds for Tomorrow
+ data.TomorrowStartHour, data.TomorrowEndHour = calcCalendarBounds(data.TomorrowItems, -1)
+ for h := data.TomorrowStartHour; h <= data.TomorrowEndHour; h++ {
+ data.TomorrowHours = append(data.TomorrowHours, h)
+ }
+
HTMLResponse(w, h.templates, "timeline-tab", data)
}
+
+// calcCalendarBounds returns start/end hours for calendar view based on timed events.
+// If currentHour >= 0, it's included in the range (for "now" line visibility).
+// Returns hours clamped to 0-23 with 1-hour buffer before/after events.
+func calcCalendarBounds(items []models.TimelineItem, currentHour int) (startHour, endHour int) {
+ minHour := 23
+ maxHour := 0
+ hasTimedEvents := false
+
+ for _, item := range items {
+ // Skip all-day/overdue items (midnight with no real time)
+ if item.IsAllDay || item.IsOverdue {
+ continue
+ }
+ h := item.Time.Hour()
+ // Skip midnight items unless they have an end time
+ if h == 0 && item.Time.Minute() == 0 && item.EndTime == nil {
+ continue
+ }
+ hasTimedEvents = true
+ if h < minHour {
+ minHour = h
+ }
+ endH := h
+ if item.EndTime != nil {
+ endH = item.EndTime.Hour()
+ }
+ if endH > maxHour {
+ maxHour = endH
+ }
+ }
+
+ // Include current hour if provided
+ if currentHour >= 0 {
+ hasTimedEvents = true
+ if currentHour < minHour {
+ minHour = currentHour
+ }
+ if currentHour > maxHour {
+ maxHour = currentHour
+ }
+ }
+
+ if !hasTimedEvents {
+ // Default: show 8am-6pm
+ return 8, 18
+ }
+
+ // Add 1 hour buffer, clamp to valid range
+ startHour = minHour - 1
+ if startHour < 0 {
+ startHour = 0
+ }
+ endHour = maxHour + 1
+ if endHour > 23 {
+ endHour = 23
+ }
+
+ return startHour, endHour
+}