package store import ( "encoding/json" "task-dashboard/internal/models" ) // SetTaskLabels replaces a task's label set entirely (not an add/remove // single-label API), matching UpdateNativeTask's replace-whole-value style. // Returns ErrNativeTaskNotFound if id doesn't match any row. func (s *Store) SetTaskLabels(id string, labels []string) error { labelsJSON, _ := json.Marshal(labels) result, err := s.db.Exec(` UPDATE native_tasks SET labels = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ? `, string(labelsJSON), id) if err != nil { return err } return checkRowsAffected(result) } // GetLabelColors returns every label that has been assigned a color. func (s *Store) GetLabelColors() ([]models.LabelColor, error) { rows, err := s.db.Query(`SELECT name, color, budget_tracked FROM labels ORDER BY name ASC`) if err != nil { return nil, err } defer func() { _ = rows.Close() }() var colors []models.LabelColor for rows.Next() { var c models.LabelColor if err := rows.Scan(&c.Name, &c.Color, &c.BudgetTracked); err != nil { return nil, err } colors = append(colors, c) } return colors, rows.Err() } // SetLabelColor assigns (or reassigns) a label's display color, preserving // any existing budget_tracked flag -- a plain INSERT OR REPLACE would // delete-and-reinsert the row, silently resetting budget_tracked to 0. func (s *Store) SetLabelColor(name, color string) error { _, err := s.db.Exec(` INSERT INTO labels (name, color, budget_tracked) VALUES (?, ?, 0) ON CONFLICT(name) DO UPDATE SET color = excluded.color `, name, color) return err } // SetLabelBudgetTracked marks a label as opted in (or out) of budget // tracking, preserving any existing color the same way SetLabelColor // preserves budget_tracked. func (s *Store) SetLabelBudgetTracked(name string, tracked bool) error { _, err := s.db.Exec(` INSERT INTO labels (name, color, budget_tracked) VALUES (?, '', ?) ON CONFLICT(name) DO UPDATE SET budget_tracked = excluded.budget_tracked `, name, tracked) return err } // GetBudgetTrackedLabelNames returns the set of label names opted into budget tracking. func (s *Store) GetBudgetTrackedLabelNames() (map[string]bool, error) { rows, err := s.db.Query(`SELECT name FROM labels WHERE budget_tracked = 1`) if err != nil { return nil, err } defer func() { _ = rows.Close() }() names := make(map[string]bool) for rows.Next() { var name string if err := rows.Scan(&name); err != nil { return nil, err } names[name] = true } return names, rows.Err() }