summaryrefslogtreecommitdiff
path: root/internal/store
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store')
-rw-r--r--internal/store/native_tasks.go135
1 files changed, 135 insertions, 0 deletions
diff --git a/internal/store/native_tasks.go b/internal/store/native_tasks.go
new file mode 100644
index 0000000..3ec043b
--- /dev/null
+++ b/internal/store/native_tasks.go
@@ -0,0 +1,135 @@
+package store
+
+import (
+ "encoding/json"
+ "log"
+ "time"
+
+ "task-dashboard/internal/models"
+)
+
+// GetNativeTasks returns all non-completed native tasks.
+func (s *Store) GetNativeTasks() ([]models.Task, error) {
+ rows, err := s.db.Query(`
+ SELECT id, content, description, project_name, due_date, priority, completed, labels, created_at
+ FROM native_tasks
+ WHERE completed = 0
+ ORDER BY CASE WHEN due_date IS NULL THEN 1 ELSE 0 END, due_date ASC, priority DESC
+ `)
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = rows.Close() }()
+ return scanNativeTasks(rows)
+}
+
+// GetNativeTasksByDateRange returns non-completed native tasks due within the given range.
+func (s *Store) GetNativeTasksByDateRange(start, end time.Time) ([]models.Task, error) {
+ rows, err := s.db.Query(`
+ SELECT id, content, description, project_name, due_date, priority, completed, labels, created_at
+ FROM native_tasks
+ WHERE completed = 0 AND due_date IS NOT NULL AND due_date >= ? AND due_date < ?
+ ORDER BY due_date ASC, priority DESC
+ `, start, end)
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = rows.Close() }()
+ return scanNativeTasks(rows)
+}
+
+// GetUndatedNativeTasks returns non-completed native tasks with no due date.
+func (s *Store) GetUndatedNativeTasks() ([]models.Task, error) {
+ rows, err := s.db.Query(`
+ SELECT id, content, description, project_name, due_date, priority, completed, labels, created_at
+ FROM native_tasks
+ WHERE completed = 0 AND due_date IS NULL
+ ORDER BY priority DESC, created_at ASC
+ `)
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = rows.Close() }()
+ return scanNativeTasks(rows)
+}
+
+// CreateNativeTask inserts a new native task.
+func (s *Store) CreateNativeTask(task models.Task) error {
+ labelsJSON, _ := json.Marshal(task.Labels)
+ _, err := s.db.Exec(`
+ INSERT INTO native_tasks (id, content, description, project_name, due_date, priority, labels, created_at, updated_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
+ `, task.ID, task.Content, task.Description, task.ProjectName, task.DueDate, task.Priority, string(labelsJSON))
+ return err
+}
+
+// UpdateNativeTask updates a native task's content and description.
+func (s *Store) UpdateNativeTask(id, content, description string) error {
+ _, err := s.db.Exec(`
+ UPDATE native_tasks SET content = ?, description = ?, updated_at = CURRENT_TIMESTAMP
+ WHERE id = ?
+ `, content, description, id)
+ return err
+}
+
+// CompleteNativeTask marks a task as completed.
+func (s *Store) CompleteNativeTask(id string) error {
+ _, err := s.db.Exec(`
+ UPDATE native_tasks SET completed = 1, updated_at = CURRENT_TIMESTAMP WHERE id = ?
+ `, id)
+ return err
+}
+
+// UncompleteNativeTask marks a task as not completed.
+func (s *Store) UncompleteNativeTask(id string) error {
+ _, err := s.db.Exec(`
+ UPDATE native_tasks SET completed = 0, updated_at = CURRENT_TIMESTAMP WHERE id = ?
+ `, id)
+ return err
+}
+
+// ImportFromTodoist copies all incomplete tasks from the Todoist cache into native_tasks.
+// Skips tasks already present (by ID). Returns the count of newly imported tasks.
+func (s *Store) ImportFromTodoist() (int, error) {
+ result, err := s.db.Exec(`
+ INSERT OR IGNORE INTO native_tasks
+ (id, content, description, project_name, due_date, priority, completed, labels, created_at, updated_at)
+ SELECT
+ id, content, COALESCE(description, ''), COALESCE(project_name, ''),
+ due_date, priority, 0, COALESCE(labels, '[]'), created_at, CURRENT_TIMESTAMP
+ FROM tasks
+ WHERE completed = 0
+ `)
+ if err != nil {
+ return 0, err
+ }
+ n, _ := result.RowsAffected()
+ log.Printf("ImportFromTodoist: imported %d tasks", n)
+ return int(n), nil
+}
+
+func scanNativeTasks(rows interface{ Next() bool; Scan(...interface{}) error; Err() error }) ([]models.Task, error) {
+ var tasks []models.Task
+ for rows.Next() {
+ var t models.Task
+ var labelsJSON string
+ var dueDateStr *string
+ if err := rows.Scan(&t.ID, &t.Content, &t.Description, &t.ProjectName, &dueDateStr, &t.Priority, &t.Completed, &labelsJSON, &t.CreatedAt); err != nil {
+ return nil, err
+ }
+ if dueDateStr != nil {
+ if parsed, err := time.Parse(time.RFC3339, *dueDateStr); err == nil {
+ t.DueDate = &parsed
+ } else if parsed, err := time.Parse("2006-01-02 15:04:05", *dueDateStr); err == nil {
+ t.DueDate = &parsed
+ } else if parsed, err := time.Parse("2006-01-02", *dueDateStr); err == nil {
+ t.DueDate = &parsed
+ }
+ }
+ if err := json.Unmarshal([]byte(labelsJSON), &t.Labels); err != nil {
+ t.Labels = nil
+ }
+ tasks = append(tasks, t)
+ }
+ return tasks, rows.Err()
+}