summaryrefslogtreecommitdiff
path: root/internal/models/atom.go
blob: 824faafb13f7bf284e8b0911337c2d98d2a4719f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package models

import (
	"time"

	"task-dashboard/internal/config"
)

type AtomSource string

const (
	SourceTrello      AtomSource = "trello"
	SourceGTasks      AtomSource = "gtasks"
	SourceClaudomator AtomSource = "claudomator"
	SourceDoot        AtomSource = "doot"
)

type AtomType string

const (
	TypeTask AtomType = "task"
)

// Atom represents a unified unit of work or information
type Atom struct {
	ID          string
	Title       string
	Description string
	Source      AtomSource
	Type        AtomType

	// Metadata
	URL       string
	DueDate   *time.Time
	CreatedAt time.Time

	Priority int // Normalized: 1 (Low) to 4 (Urgent)

	// ProjectName is set for doot-native tasks with a project assigned
	// (empty for every other source, and for unassigned native tasks).
	ProjectName string

	// UI Helpers (to be populated by mappers)
	SourceIcon string // e.g., "trello-icon.svg" or emoji
	ColorClass string // e.g., "border-blue-500"
	IsOverdue  bool   // True if due date is before today
	IsFuture   bool   // True if due date is after today
	HasSetTime bool   // True if due time is not midnight (has specific time)

	// Original Data (for write operations)
	Raw interface{}
}

// ComputeUIFields calculates IsOverdue, IsFuture, and HasSetTime based on DueDate
func (a *Atom) ComputeUIFields() {
	if a.DueDate == nil {
		return
	}

	tz := config.GetDisplayTimezone()
	now := config.Now()
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, tz)

	// Check if overdue (due date is before today)
	dueInTZ := a.DueDate.In(tz)
	dueDay := time.Date(dueInTZ.Year(), dueInTZ.Month(), dueInTZ.Day(), 0, 0, 0, 0, tz)
	a.IsOverdue = dueDay.Before(today)

	// Check if future (due date is 7+ days out — collapse in tasks tab)
	sevenDaysOut := today.AddDate(0, 0, 7)
	a.IsFuture = !dueDay.Before(sevenDaysOut)

	// Check if has set time (not midnight)
	a.HasSetTime = dueInTZ.Hour() != 0 || dueInTZ.Minute() != 0
}

// NativeTaskToAtom converts a native doot Task to an Atom.
func NativeTaskToAtom(t Task) Atom {
	priority := t.Priority
	if priority < 1 {
		priority = 1
	}
	if priority > 4 {
		priority = 4
	}

	return Atom{
		ID:          t.ID,
		Title:       t.Content,
		Description: t.Description,
		Source:      SourceDoot,
		Type:        TypeTask,
		DueDate:     t.DueDate,
		CreatedAt:   t.CreatedAt,
		Priority:    priority,
		ProjectName: t.ProjectName,
		SourceIcon:  "✅",
		ColorClass:  "border-green-500",
		Raw:         t,
	}
}

// CardToAtom converts a Trello Card to an Atom
func CardToAtom(c Card) Atom {
	// Trello doesn't have explicit priority, default to medium (2)
	// Can be enhanced later with label-based priority detection
	priority := 2

	return Atom{
		ID:          c.ID,
		Title:       c.Name,
		Description: c.ListName, // Use list name as description
		Source:      SourceTrello,
		Type:        TypeTask,
		URL:         c.URL,
		DueDate:     c.DueDate,
		CreatedAt:   time.Time{}, // Trello ID contains timestamp, can be parsed later
		Priority:    priority,
		SourceIcon:  "📋", // Clipboard emoji for boards
		ColorClass:  "border-blue-500",
		Raw:         c,
	}
}

// StoryToAtom converts a ClaudomatorStory to an Atom
func StoryToAtom(s ClaudomatorStory) Atom {
	return Atom{
		ID:          s.ID,
		Title:       s.Title,
		Description: s.Description + " [" + s.ProjectID + "]",
		Source:      SourceClaudomator,
		Type:        TypeTask,
		Priority:    3,
		SourceIcon:  "🤖",
		ColorClass:  "border-purple-500",
		CreatedAt:   s.CreatedAt,
		Raw:         s,
	}
}

// GoogleTaskToAtom converts a Google Task to an Atom
func GoogleTaskToAtom(t GoogleTask) Atom {
	// Google Tasks don't have explicit priority, default to medium (2)
	priority := 2

	return Atom{
		ID:          t.ID,
		Title:       t.Title,
		Description: t.Notes,
		Source:      SourceGTasks,
		Type:        TypeTask,
		URL:         t.URL,
		DueDate:     t.DueDate,
		CreatedAt:   t.UpdatedAt,
		Priority:    priority,
		SourceIcon:  "🔵", // Blue circle for Google Tasks
		ColorClass:  "border-blue-400",
		Raw:         t,
	}
}