summaryrefslogtreecommitdiff
path: root/internal/tui/timer_box.go
blob: 1a888700746703f2b0eb359ba4294f973c8b12fd (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
package tui

import (
	"fmt"
	"time"

	"punchcard/internal/queries"
)

// TimerInfo holds information about the current or most recent timer state
type TimerInfo struct {
	IsActive     bool
	EntryID      int64
	StartTime    time.Time
	Duration     time.Duration
	ClientID     int64
	ClientName   string
	ProjectID    *int64
	ProjectName  string
	Description  *string
	BillableRate *float64
}

func (ti *TimerInfo) setNames(clients []queries.Client, projects map[int64][]queries.Project) {
	for _, cl := range clients {
		if cl.ID == ti.ClientID {
			ti.ClientName = cl.Name
			break
		}
	}

	if ti.ProjectID == nil {
		return
	}
	for _, group := range projects {
		for _, proj := range group {
			if proj.ID == *ti.ProjectID {
				ti.ProjectName = proj.Name
				return
			}
		}
	}
}

// Box models for the three main components
type TimerBoxModel struct {
	timerInfo   TimerInfo
	currentTime time.Time
}

// NewTimerBoxModel creates a new timer box model
func NewTimerBoxModel() TimerBoxModel {
	return TimerBoxModel{
		currentTime: time.Now(),
	}
}

// View renders the timer box
func (m TimerBoxModel) View(width, height int, isSelected bool) string {
	var content string

	if m.timerInfo.IsActive {
		content = m.renderActiveTimer()
	} else {
		content = m.renderInactiveTimer()
	}

	// Apply box styling
	style := unselectedBoxStyle
	if isSelected {
		style = selectedBoxStyle
	}

	return style.Width(width).Height(height).Render(content)
}

// renderActiveTimer renders the active timer display
func (m TimerBoxModel) renderActiveTimer() string {
	content := "⏱ Active Timer\n\n"

	// Timer duration
	timerLine := fmt.Sprintf("Duration: %s", FormatDuration(m.currentTime.Sub(m.timerInfo.StartTime)))
	content += activeTimerStyle.Render(timerLine) + "\n\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"
	}

	// 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"

	// Description if available
	if m.timerInfo.Description != nil {
		content += "\n"
		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"
	}

	return content
}

// renderInactiveTimer renders the inactive timer display
func (m TimerBoxModel) renderInactiveTimer() string {
	content := "⚪ Last Timer (Inactive)\n\n"

	timerLine := fmt.Sprintf("Duration: %s", FormatDuration(m.timerInfo.Duration))
	content += inactiveTimerStyle.Render(timerLine) + "\n\n"

	if m.timerInfo.ProjectName != "" {
		content += inactiveTimerStyle.Render(fmt.Sprintf("Project: %s / %s", m.timerInfo.ClientName, m.timerInfo.ProjectName)) + "\n"
	} else {
		content += inactiveTimerStyle.Render(fmt.Sprintf("Client: %s", m.timerInfo.ClientName)) + "\n"
	}

	content += inactiveTimerStyle.Render(fmt.Sprintf("Started: %s", m.timerInfo.StartTime.Local().Format("3:04 PM"))) + "\n"

	if m.timerInfo.Description != nil {
		content += "\n" + inactiveTimerStyle.Render(fmt.Sprintf("Description: %s", *m.timerInfo.Description)) + "\n"
	}
	if m.timerInfo.BillableRate != nil {
		content += inactiveTimerStyle.Render(fmt.Sprintf("Rate: $%.2f/hr", *m.timerInfo.BillableRate)) + "\n"
	}

	return content
}

func (m TimerBoxModel) activeTime() time.Duration {
	if !m.timerInfo.IsActive {
		return 0
	}
	return m.currentTime.Sub(m.timerInfo.StartTime)
}