From bad562c2ada086903cd8ea050a02133595e98ff1 Mon Sep 17 00:00:00 2001 From: Kevin Mulholland Date: Fri, 15 May 2026 17:09:05 -0400 Subject: [PATCH 1/2] update: add month max totals --- internal/ui/views/month/month.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/ui/views/month/month.go b/internal/ui/views/month/month.go index 5bc097a..35692c9 100644 --- a/internal/ui/views/month/month.go +++ b/internal/ui/views/month/month.go @@ -91,6 +91,18 @@ func (m Model) View() tea.View { func (m Model) renderFooter() string { monthTotal := m.calculateMonthTotal() + monthStart := time.Date(m.currentMonth.Year(), m.currentMonth.Month(), 1, 0, 0, 0, 0, time.UTC) + monthEnd := time.Date(m.currentMonth.Year(), m.currentMonth.Month()+1, 1, 0, 0, 0, 0, time.UTC).Add(-time.Second) + + workingDays := 0 + for d := monthStart; d.Before(monthEnd); d = d.AddDate(0, 0, 1) { + if d.Weekday() != time.Saturday && d.Weekday() != time.Sunday { + workingDays++ + } + } + + maxMonth := workingDays * 8 + totalStyle := lipgloss.NewStyle() label := lipgloss.NewStyle(). @@ -101,7 +113,7 @@ func (m Model) renderFooter() string { value := lipgloss.NewStyle(). Foreground(styles.Text). - Render(formatDuration(monthTotal)) + Render(fmt.Sprintf("%s / %dh", formatDuration(monthTotal), maxMonth)) content := lipgloss.JoinHorizontal( lipgloss.Left, @@ -226,6 +238,7 @@ func (m Model) setTableData() [][]string { rows := [][]string{} for _, w := range weeks { var weekTotal time.Duration + var weekMax int row := []string{} for _, day := range w.days { if day.IsZero() { @@ -234,11 +247,12 @@ func (m Model) setTableData() [][]string { } key := day.Format("2006-01-02") d := dailyTotals[key] + weekMax += 8 weekTotal += d date := day.Format("01/02") row = append(row, fmt.Sprintf("%s\n%s", date, formatDuration(d))) } - row = append(row, fmt.Sprintf("\n%s", formatDuration(weekTotal))) + row = append(row, fmt.Sprintf("\n%s/%dh", formatDuration(weekTotal), weekMax)) rows = append(rows, row) } From 43a5f2d1676ea635a7835e3534c749682c037752 Mon Sep 17 00:00:00 2001 From: Kevin Mulholland Date: Mon, 18 May 2026 11:45:14 -0400 Subject: [PATCH 2/2] test: getting tests to pass --- .../ui/components/calendar/calendar_test.go | 22 +++++++++---------- .../confirmation/confirmation_test.go | 4 ++-- .../ui/components/entryform/entryform_test.go | 16 +++++++------- internal/ui/views/projects/projects_test.go | 10 ++++----- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/internal/ui/components/calendar/calendar_test.go b/internal/ui/components/calendar/calendar_test.go index 55889db..240fa9f 100644 --- a/internal/ui/components/calendar/calendar_test.go +++ b/internal/ui/components/calendar/calendar_test.go @@ -41,7 +41,7 @@ func TestUpdate_NextDay(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'l'}} + msg := tea.KeyPressMsg{Code: 'l'} m, _ = m.Update(msg) expected := time.Date(2026, 1, 16, 0, 0, 0, 0, time.UTC) @@ -55,7 +55,7 @@ func TestUpdate_PreviousDay(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'h'}} + msg := tea.KeyPressMsg{Code: 'h'} m, _ = m.Update(msg) expected := time.Date(2026, 1, 14, 0, 0, 0, 0, time.UTC) @@ -69,7 +69,7 @@ func TestUpdate_NextWeek(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'j'}} + msg := tea.KeyPressMsg{Code: 'j'} m, _ = m.Update(msg) expected := time.Date(2026, 1, 22, 0, 0, 0, 0, time.UTC) @@ -83,7 +83,7 @@ func TestUpdate_PreviousWeek(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'k'}} + msg := tea.KeyPressMsg{Code: 'k'} m, _ = m.Update(msg) expected := time.Date(2026, 1, 8, 0, 0, 0, 0, time.UTC) @@ -97,7 +97,7 @@ func TestUpdate_NextMonth(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyPgDown, Alt: false} + msg := tea.KeyPressMsg{Code: tea.KeyPgDown} m, _ = m.Update(msg) expected := time.Date(2026, 2, 15, 0, 0, 0, 0, time.UTC) @@ -114,7 +114,7 @@ func TestUpdate_PreviousMonth(t *testing.T) { m.SelectedDate = time.Date(2026, 2, 15, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyPgUp, Alt: false} + msg := tea.KeyPressMsg{Code: tea.KeyPgUp} m, _ = m.Update(msg) expected := time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) @@ -130,7 +130,7 @@ func TestUpdate_Today(t *testing.T) { m := New() m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'t'}} + msg := tea.KeyPressMsg{Code: 't'} m, _ = m.Update(msg) now := time.Now() @@ -144,7 +144,7 @@ func TestUpdate_MonthChange(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 31, 0, 0, 0, 0, time.UTC) m.CurrentDate = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) - msg := tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'l'}} + msg := tea.KeyPressMsg{Code: 'l'} m, _ = m.Update(msg) if m.CurrentDate.Month() != time.February { @@ -154,10 +154,10 @@ func TestUpdate_MonthChange(t *testing.T) { func TestDefaultKeyMap(t *testing.T) { km := DefaultKeyMap() - if !key.Matches(tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'t'}}, km.Today) { + if !key.Matches(tea.KeyPressMsg{Code: 't'}, km.Today) { t.Error("Today key not set correctly") } - if !key.Matches(tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'l'}}, km.Next) { + if !key.Matches(tea.KeyPressMsg{Code: 'l'}, km.Next) { t.Error("Next key not set correctly") } } @@ -168,7 +168,7 @@ func TestView(t *testing.T) { m.SelectedDate = time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC) view := m.View() - if view == "" { + if view.Content == "" { t.Error("View should not be empty") } } diff --git a/internal/ui/components/confirmation/confirmation_test.go b/internal/ui/components/confirmation/confirmation_test.go index 4b342dd..a0cf4e0 100644 --- a/internal/ui/components/confirmation/confirmation_test.go +++ b/internal/ui/components/confirmation/confirmation_test.go @@ -24,12 +24,12 @@ func TestUpdate(t *testing.T) { model := New("entry1", "entry") // Test left/right arrow keys for cursor movement - updated, _ := model.Update(tea.KeyPressMsg{Type: tea.KeyRight}) + updated, _ := model.Update(tea.KeyPressMsg{Code: tea.KeyRight}) if updated.cursor != 1 { t.Errorf("Expected cursor to be 1 after right arrow, got %d", updated.cursor) } - updated, _ = updated.Update(tea.KeyPressMsg{Type: tea.KeyLeft}) + updated, _ = updated.Update(tea.KeyPressMsg{Code: tea.KeyLeft}) if updated.cursor != 0 { t.Errorf("Expected cursor to be 0 after left arrow, got %d", updated.cursor) } diff --git a/internal/ui/components/entryform/entryform_test.go b/internal/ui/components/entryform/entryform_test.go index 6fd4567..ddc9185 100644 --- a/internal/ui/components/entryform/entryform_test.go +++ b/internal/ui/components/entryform/entryform_test.go @@ -124,14 +124,14 @@ func TestDateSelectUpdate(t *testing.T) { initialDate := model.calendar.SelectedDate // Test left arrow (previous day) - msg := tea.KeyPressMsg{Type: tea.KeyLeft} + msg := tea.KeyPressMsg{Code: tea.KeyLeft} model.calendar, _ = model.calendar.Update(msg) if !model.calendar.SelectedDate.Before(initialDate) { t.Error("Left arrow should move to previous day") } // Test right arrow (next day) - msg = tea.KeyPressMsg{Type: tea.KeyRight} + msg = tea.KeyPressMsg{Code: tea.KeyRight} model.calendar, _ = model.calendar.Update(msg) if !model.calendar.SelectedDate.Equal(initialDate) { t.Error("Right arrow should move to next day") @@ -139,7 +139,7 @@ func TestDateSelectUpdate(t *testing.T) { // Test 't' key (today) model.calendar.SelectedDate = time.Date(2020, 1, 1, 0, 0, 0, 0, time.Local) - msg = tea.KeyPressMsg{Type: tea.KeyRunes, Runes: []rune{'t'}} + msg = tea.KeyPressMsg{Code: 't'} model.calendar, _ = model.calendar.Update(msg) today := time.Now() if !model.calendar.SelectedDate.Truncate(24 * time.Hour).Equal(today.Truncate(24 * time.Hour)) { @@ -151,14 +151,14 @@ func TestStepNavigation(t *testing.T) { model := New(&config.Config{}, []models.Project{}) // Test tab navigation forward - msg := tea.KeyPressMsg{Type: tea.KeyTab} + msg := tea.KeyPressMsg{Code: tea.KeyTab} updated, _ := model.Update(msg) if updated.step != stepDescriptionInput { t.Errorf("Expected step %d after tab, got %d", stepDescriptionInput, updated.step) } // Test shift+tab navigation backward - msg = tea.KeyPressMsg{Type: tea.KeyShiftTab} + msg = tea.KeyPressMsg{Code: tea.KeyTab, Mod: tea.ModShift} updated, _ = updated.Update(msg) if updated.step != stepDateSelect { t.Errorf("Expected step %d after shift+tab, got %d", stepDateSelect, updated.step) @@ -218,7 +218,7 @@ func TestMainView(t *testing.T) { for step := stepDateSelect; step <= stepComplete; step++ { model.step = step view := model.View() - if strings.TrimSpace(view) == "" { + if strings.TrimSpace(view.Content) == "" { t.Errorf("View should return non-empty string for step %d", step) } } @@ -226,7 +226,7 @@ func TestMainView(t *testing.T) { // Test unknown step model.step = 999 view := model.View() - if !strings.Contains(view, "Unknown step") { + if !strings.Contains(view.Content, "Unknown step") { t.Error("View should handle unknown step") } } @@ -256,7 +256,7 @@ func TestEscapeKey(t *testing.T) { model.step = stepProjectSelect model.cursor = 5 - msg := tea.KeyPressMsg{Type: tea.KeyEsc} + msg := tea.KeyPressMsg{Code: tea.KeyEsc} updated, _ := model.Update(msg) // Should reset to initial state diff --git a/internal/ui/views/projects/projects_test.go b/internal/ui/views/projects/projects_test.go index 4b0e749..ebcafd6 100644 --- a/internal/ui/views/projects/projects_test.go +++ b/internal/ui/views/projects/projects_test.go @@ -76,8 +76,8 @@ func TestView_NotReady(t *testing.T) { view := model.View() expected := "Loading projects..." - if view != expected { - t.Errorf("Expected '%s', got '%s'", expected, view) + if view.Content != expected { + t.Errorf("Expected '%s', got '%s'", expected, view.Content) } } @@ -90,8 +90,8 @@ func TestView_NoProjects(t *testing.T) { view := model.View() expected := "No projects found." - if view != expected { - t.Errorf("Expected '%s', got '%s'", expected, view) + if view.Content != expected { + t.Errorf("Expected '%s', got '%s'", expected, view.Content) } } @@ -110,7 +110,7 @@ func TestView_WithProjects(t *testing.T) { view := model.View() // Should not be the loading or empty message - if view == "Loading projects..." || view == "No projects found." { + if view.Content == "Loading projects..." || view.Content == "No projects found." { t.Error("View should render project list when projects are available") } }