blob: 2fcf55c20c6a1fafd7351a772bfbfa1e343565c7 (
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
161
162
163
164
165
|
package tui
import (
"context"
"time"
"punchcard/internal/queries"
)
// BoxType represents the different boxes that can be selected
type BoxType int
const (
TimerBox BoxType = iota
ClientsProjectsBox
HistoryBox
)
func (b BoxType) String() string {
switch b {
case TimerBox:
return "Timer"
case ClientsProjectsBox:
return "Clients & Projects"
case HistoryBox:
return "History"
default:
return "Unknown"
}
}
// AppModel is the main model for the TUI application
type AppModel struct {
ctx context.Context
queries *queries.Queries
selectedBox BoxType
timerBoxModel TimerBoxModel
clientsProjectsModel ClientsProjectsModel
historyBoxModel HistoryBoxModel
width int
height int
// Cached data to avoid DB queries in View()
stats TimeStats
runningTimerStart *time.Time // UTC timestamp when timer started, nil if not active
// Modal state
showModal bool
modalType ModalType
textInputModel TextInputModel
}
// ModalType represents different types of modals
type ModalType int
const (
ModalDescribeTimer ModalType = iota
)
// TextInputModel represents a text input modal
type TextInputModel struct {
prompt string
value string
placeholder string
cursorPos int
}
// TimerInfo holds information about the current timer state
type TimerInfo struct {
IsActive bool
Duration time.Duration
StartTime time.Time
ClientName string
ProjectName string
Description string
BillableRate *float64
}
// TimeStats holds time statistics for display
type TimeStats struct {
TodayTotal time.Duration
WeekTotal time.Duration
}
// TickMsg is sent every second to update the timer
type TickMsg time.Time
// KeyBinding represents the available key bindings for a view
type KeyBinding struct {
Key string
Description string
}
// HistoryViewLevel represents the level of detail in history view
type HistoryViewLevel int
const (
HistoryLevelSummary HistoryViewLevel = iota // Level 1: Date/project summaries
HistoryLevelDetails // Level 2: Individual entries
)
// Box models for the three main components
type TimerBoxModel struct {
timerInfo TimerInfo
}
type ClientsProjectsModel struct {
clients []queries.Client
projects []queries.ListAllProjectsRow
selectedIndex int // Index of selected row (client or project)
selectedIsClient bool // True if selected row is a client, false if project
}
type HistoryBoxModel struct {
entries []queries.TimeEntry
clients []queries.Client // For looking up client names
projects []queries.ListAllProjectsRow // For looking up project names
viewLevel HistoryViewLevel
selectedIndex int // Index of selected row
// Cached running timer data to avoid recalculating in View()
runningTimerStart *time.Time // UTC timestamp when timer started, nil if not active
// Summary view data (level 1)
summaryItems []HistorySummaryItem
// Details view data (level 2)
detailsEntries []queries.TimeEntry
selectedSummaryItem *HistorySummaryItem // Which summary item we drilled down from
}
// HistorySummaryItem represents a date + client/project combination with total duration
type HistorySummaryItem struct {
Date time.Time
ClientID int64
ClientName string
ProjectID *int64 // nil if no project
ProjectName *string // nil if no project
TotalDuration time.Duration
EntryCount int
}
// HistoryDisplayItem represents an item in the history view (either date header or summary/detail item)
type HistoryDisplayItem struct {
Type HistoryDisplayItemType
DateHeader *string // Set if Type is DateHeader
Summary *HistorySummaryItem // Set if Type is Summary
Entry *queries.TimeEntry // Set if Type is Entry
IsSelectable bool
}
type HistoryDisplayItemType int
const (
HistoryItemDateHeader HistoryDisplayItemType = iota
HistoryItemSummary
HistoryItemEntry
)
// ProjectsDisplayItem represents an item in the projects display order (either client or project)
type ProjectsDisplayItem struct {
IsClient bool
ClientIndex int // Index in m.clients
ProjectIndex int // Index in m.projects, only used when IsClient=false
Client *queries.Client
Project *queries.ListAllProjectsRow
}
|