summaryrefslogtreecommitdiff
path: root/internal/tui/types.go
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
}