summaryrefslogtreecommitdiff
path: root/internal/handlers/atoms.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-26 08:10:27 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-26 08:10:27 -1000
commit2e739638477e87a1b1df662740f191c86db60186 (patch)
tree302916e1e0c99ae47213128fa79133752203a271 /internal/handlers/atoms.go
parentaff60af8ba24c8d5330c706ddf26927d81436d79 (diff)
Phase 5: Extract functions to reduce complexity
- Create atoms.go with BuildUnifiedAtomList, SortAtomsByUrgency, PartitionAtomsByTime - Create helpers.go with parseFormOr400, requireFormValue - Refactor HandleTabTasks from 95 lines to 25 lines using extracted functions - Remove duplicate atomUrgencyTier function - Update handlers to use parseFormOr400 helper Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/handlers/atoms.go')
-rw-r--r--internal/handlers/atoms.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/internal/handlers/atoms.go b/internal/handlers/atoms.go
new file mode 100644
index 0000000..7bc4465
--- /dev/null
+++ b/internal/handlers/atoms.go
@@ -0,0 +1,112 @@
+package handlers
+
+import (
+ "sort"
+
+ "task-dashboard/internal/models"
+ "task-dashboard/internal/store"
+)
+
+// BuildUnifiedAtomList creates a list of atoms from tasks, cards, and bugs
+func BuildUnifiedAtomList(s *store.Store) ([]models.Atom, []models.Board, error) {
+ tasks, err := s.GetTasks()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ boards, err := s.GetBoards()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ bugs, _ := s.GetUnresolvedBugs() // Ignore error, bugs are optional
+
+ atoms := make([]models.Atom, 0, len(tasks)+len(bugs))
+
+ // Add incomplete tasks
+ for _, task := range tasks {
+ if !task.Completed {
+ atoms = append(atoms, models.TaskToAtom(task))
+ }
+ }
+
+ // Add cards with due dates or from actionable lists
+ for _, board := range boards {
+ for _, card := range board.Cards {
+ if card.DueDate != nil || isActionableList(card.ListName) {
+ atoms = append(atoms, models.CardToAtom(card))
+ }
+ }
+ }
+
+ // Add unresolved bugs
+ for _, bug := range bugs {
+ atoms = append(atoms, models.BugToAtom(models.Bug{
+ ID: bug.ID,
+ Description: bug.Description,
+ CreatedAt: bug.CreatedAt,
+ }))
+ }
+
+ // Compute UI fields for all atoms
+ for i := range atoms {
+ atoms[i].ComputeUIFields()
+ }
+
+ return atoms, boards, nil
+}
+
+// SortAtomsByUrgency sorts atoms by urgency tier, then due date, then priority
+func SortAtomsByUrgency(atoms []models.Atom) {
+ sort.SliceStable(atoms, func(i, j int) bool {
+ tierI := atomUrgencyTier(atoms[i])
+ tierJ := atomUrgencyTier(atoms[j])
+
+ if tierI != tierJ {
+ return tierI < tierJ
+ }
+
+ if atoms[i].DueDate != nil && atoms[j].DueDate != nil {
+ if !atoms[i].DueDate.Equal(*atoms[j].DueDate) {
+ return atoms[i].DueDate.Before(*atoms[j].DueDate)
+ }
+ }
+
+ return atoms[i].Priority > atoms[j].Priority
+ })
+}
+
+// PartitionAtomsByTime separates atoms into current and future lists
+// Recurring tasks that are future are excluded entirely
+func PartitionAtomsByTime(atoms []models.Atom) (current, future []models.Atom) {
+ for _, a := range atoms {
+ // Don't show recurring tasks until the day they're due
+ if a.IsRecurring && a.IsFuture {
+ continue
+ }
+ if a.IsFuture {
+ future = append(future, a)
+ } else {
+ current = append(current, a)
+ }
+ }
+ return
+}
+
+// atomUrgencyTier returns an urgency tier for sorting:
+// 0 = overdue, 1 = today with specific time, 2 = today no time, 3 = future, 4 = no due date
+func atomUrgencyTier(a models.Atom) int {
+ if a.DueDate == nil {
+ return 4
+ }
+ if a.IsOverdue {
+ return 0
+ }
+ if a.IsFuture {
+ return 3
+ }
+ if a.HasSetTime {
+ return 1
+ }
+ return 2
+}