summaryrefslogtreecommitdiff
path: root/internal/handlers/tabs.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-21 22:53:37 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-21 22:53:37 -1000
commit583f90c5dedf0235fa45557359b0e6e7dd62b0f0 (patch)
tree304e4527b6668669197fc9ffdf2ffc87566478f0 /internal/handlers/tabs.go
parentdd4689a71de8f1c0b5a2d483827411a9645ad66a (diff)
Implement 10 UI/UX improvements and bug fixes
- Fix outdated Todoist task URL format (showTask -> app/task) - Fix quick-add date defaulting to tomorrow in evening (client-side JS) - Add tap-to-expand for task descriptions with checkbox completion - Add visual differentiation: overdue (red), future (gray), today (normal) - Sort tasks by urgency: overdue > today-timed > today-allday > future - Keep completed tasks visible with strikethrough until refresh - Add random Unsplash landscape background with content overlay - Hide future tasks behind collapsible fold with count badge - Unified modal menu for Quick Add + Bug Report (Ctrl+K shortcut) - Click task title to edit description in modal Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers/tabs.go')
-rw-r--r--internal/handlers/tabs.go87
1 files changed, 54 insertions, 33 deletions
diff --git a/internal/handlers/tabs.go b/internal/handlers/tabs.go
index bd15710..2f22c44 100644
--- a/internal/handlers/tabs.go
+++ b/internal/handlers/tabs.go
@@ -25,6 +25,25 @@ func isActionableList(name string) bool {
strings.Contains(lower, "today")
}
+// atomUrgencyTier returns the urgency tier for sorting:
+// 0: Overdue, 1: Today with time, 2: Today all-day, 3: Future, 4: No due date
+func atomUrgencyTier(a models.Atom) int {
+ if a.DueDate == nil {
+ return 4 // No due date
+ }
+ if a.IsOverdue {
+ return 0 // Overdue
+ }
+ if a.IsFuture {
+ return 3 // Future
+ }
+ // Due today
+ if a.HasSetTime {
+ return 1 // Today with specific time
+ }
+ return 2 // Today all-day
+}
+
// TabsHandler handles tab-specific rendering with Atom model
type TabsHandler struct {
store *store.Store
@@ -88,41 +107,31 @@ func (h *TabsHandler) HandleTasks(w http.ResponseWriter, r *http.Request) {
}
}
- // Compute UI fields (IsOverdue, HasSetTime)
+ // Compute UI fields (IsOverdue, IsFuture, HasSetTime)
for i := range atoms {
atoms[i].ComputeUIFields()
}
- // Sort atoms: by DueDate (earliest first), then by HasSetTime, then by Priority
+ // Sort atoms by urgency tiers:
+ // 1. Overdue (before today)
+ // 2. Today with specific time
+ // 3. Today all-day (midnight)
+ // 4. Future
+ // 5. No due date
+ // Within each tier: sort by due date/time, then by priority
sort.SliceStable(atoms, func(i, j int) bool {
- // Handle nil due dates (push to end)
- if atoms[i].DueDate == nil && atoms[j].DueDate != nil {
- return false
- }
- if atoms[i].DueDate != nil && atoms[j].DueDate == nil {
- return true
+ // Compute urgency tier (lower = more urgent)
+ tierI := atomUrgencyTier(atoms[i])
+ tierJ := atomUrgencyTier(atoms[j])
+
+ if tierI != tierJ {
+ return tierI < tierJ
}
- // Both have due dates
+ // Same tier: sort by due date/time if both have dates
if atoms[i].DueDate != nil && atoms[j].DueDate != nil {
- // Compare by date only (ignore time)
- dateI := atoms[i].DueDate.Truncate(24 * time.Hour)
- dateJ := atoms[j].DueDate.Truncate(24 * time.Hour)
-
- if !dateI.Equal(dateJ) {
- return dateI.Before(dateJ)
- }
-
- // Same day: tasks with set times come before midnight tasks
- if atoms[i].HasSetTime != atoms[j].HasSetTime {
- return atoms[i].HasSetTime
- }
-
- // Both have set times or both are midnight, sort by actual time
- if atoms[i].HasSetTime && atoms[j].HasSetTime {
- if !atoms[i].DueDate.Equal(*atoms[j].DueDate) {
- return atoms[i].DueDate.Before(*atoms[j].DueDate)
- }
+ if !atoms[i].DueDate.Equal(*atoms[j].DueDate) {
+ return atoms[i].DueDate.Before(*atoms[j].DueDate)
}
}
@@ -130,15 +139,27 @@ func (h *TabsHandler) HandleTasks(w http.ResponseWriter, r *http.Request) {
return atoms[i].Priority > atoms[j].Priority
})
+ // Partition atoms into current (overdue + today) and future
+ var currentAtoms, futureAtoms []models.Atom
+ for _, a := range atoms {
+ if a.IsFuture {
+ futureAtoms = append(futureAtoms, a)
+ } else {
+ currentAtoms = append(currentAtoms, a)
+ }
+ }
+
// Render template
data := struct {
- Atoms []models.Atom
- Boards []models.Board
- Today string
+ Atoms []models.Atom // Current tasks (overdue + today)
+ FutureAtoms []models.Atom // Future tasks (hidden by default)
+ Boards []models.Board
+ Today string
}{
- Atoms: atoms,
- Boards: boards,
- Today: time.Now().Format("2006-01-02"),
+ Atoms: currentAtoms,
+ FutureAtoms: futureAtoms,
+ Boards: boards,
+ Today: time.Now().Format("2006-01-02"),
}
if err := h.templates.ExecuteTemplate(w, "tasks-tab", data); err != nil {