summaryrefslogtreecommitdiff
path: root/internal/tui/timer.go
blob: 827951d42239bce197300eaa2b61c16584108477 (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
package tui

import (
	"context"
	"fmt"
	"time"

	"punchcard/internal/queries"

	tea "github.com/charmbracelet/bubbletea"
)

// TimerModel represents the timer view model
type TimerModel struct {
	ctx       context.Context
	queries   *queries.Queries
	timerInfo TimerInfo
	stats     TimeStats
	lastTick  time.Time
}

// NewTimerModel creates a new timer model
func NewTimerModel(ctx context.Context, q *queries.Queries) TimerModel {
	return TimerModel{
		ctx:     ctx,
		queries: q,
	}
}

// Init initializes the timer model
func (m TimerModel) Init() tea.Cmd {
	return tea.Batch(
		m.updateData(),
		m.tickCmd(),
	)
}

// Update handles messages for the timer model
func (m TimerModel) Update(msg tea.Msg) (TimerModel, tea.Cmd) {
	switch msg := msg.(type) {
	case TickMsg:
		// Update timer duration if active
		if m.timerInfo.IsActive {
			m.timerInfo.Duration = time.Since(m.timerInfo.StartTime)
		}
		m.lastTick = time.Time(msg)
		return m, m.tickCmd()
	}
	
	return m, nil
}

// View renders the timer view
func (m TimerModel) View(width, height int) string {
	var content string
	
	if m.timerInfo.IsActive {
		content += m.renderActiveTimer()
	} else {
		content += m.renderInactiveTimer()
	}
	
	return content
}

// renderActiveTimer renders the active timer display
func (m TimerModel) renderActiveTimer() string {
	var content string
	
	// Timer status
	timerLine := fmt.Sprintf("⏱ Tracking: %s", FormatDuration(m.timerInfo.Duration))
	content += activeTimerStyle.Render(timerLine) + "\n"
	
	// Project/Client info
	if m.timerInfo.ProjectName != "" {
		projectLine := fmt.Sprintf("Project: %s / %s", m.timerInfo.ClientName, m.timerInfo.ProjectName)
		content += projectLine + "\n"
	} else {
		clientLine := fmt.Sprintf("Client: %s", m.timerInfo.ClientName)
		content += clientLine + "\n"
	}
	
	// Description if available
	if m.timerInfo.Description != "" {
		descLine := fmt.Sprintf("Description: %s", m.timerInfo.Description)
		content += descLine + "\n"
	}
	
	// Billable rate if available
	if m.timerInfo.BillableRate != nil {
		rateLine := fmt.Sprintf("Rate: $%.2f/hr", *m.timerInfo.BillableRate)
		content += rateLine + "\n"
	}
	
	// Start time (convert from UTC to local)
	localStartTime := m.timerInfo.StartTime.Local()
	startLine := fmt.Sprintf("Started: %s", localStartTime.Format("3:04 PM"))
	content += startLine + "\n"
	
	return content
}

// renderInactiveTimer renders the inactive timer display
func (m TimerModel) renderInactiveTimer() string {
	var content string
	
	content += inactiveTimerStyle.Render("⚪ No active timer") + "\n"
	content += "\n"
	content += "Ready to start tracking time.\n"
	
	return content
}

// updateData fetches fresh data from the database
func (m TimerModel) updateData() tea.Cmd {
	return func() tea.Msg {
		// Get timer info
		timerInfo, err := GetTimerInfo(m.ctx, m.queries)
		if err != nil {
			// Handle error silently for now
			return nil
		}
		
		// Get time stats
		stats, err := GetTimeStats(m.ctx, m.queries)
		if err != nil {
			// Handle error silently for now
			return nil
		}
		
		return dataUpdatedMsg{
			timerInfo: timerInfo,
			stats:     stats,
		}
	}
}

// tickCmd returns a command that sends a tick message every second
func (m TimerModel) tickCmd() tea.Cmd {
	return tea.Tick(time.Second, func(t time.Time) tea.Msg {
		return TickMsg(t)
	})
}

// UpdateData updates the model with fresh data
func (m TimerModel) UpdateData(timerInfo TimerInfo, stats TimeStats) TimerModel {
	m.timerInfo = timerInfo
	m.stats = stats
	return m
}