package handlers import ( "log" "sort" "task-dashboard/internal/models" "task-dashboard/internal/store" ) // BuildUnifiedAtomList creates a list of atoms from tasks, cards, and google tasks 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 } gTasks, err := s.GetGoogleTasks() if err != nil { // Log but don't fail if gtasks fails (might be new/not configured) log.Printf("Warning: failed to fetch cached google tasks: %v", err) } atoms := make([]models.Atom, 0, len(tasks)+len(gTasks)) // Add incomplete tasks for _, task := range tasks { if !task.Completed { atoms = append(atoms, models.TaskToAtom(task)) } } // Add incomplete google tasks for _, gTask := range gTasks { if !gTask.Completed { atoms = append(atoms, models.GoogleTaskToAtom(gTask)) } } // 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)) } } } // 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 }