summaryrefslogtreecommitdiff
path: root/internal/tui/modal.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/tui/modal.go')
-rw-r--r--internal/tui/modal.go106
1 files changed, 98 insertions, 8 deletions
diff --git a/internal/tui/modal.go b/internal/tui/modal.go
index 248654b..b614d04 100644
--- a/internal/tui/modal.go
+++ b/internal/tui/modal.go
@@ -3,8 +3,10 @@ package tui
import (
"context"
"database/sql"
+ "errors"
"fmt"
"strconv"
+ "strings"
"time"
"git.tjp.lol/punchcard/internal/actions"
@@ -26,6 +28,7 @@ const (
ModalTypeHistoryFilter
ModalTypeGenerateReport
ModalTypeContractor
+ ModalTypeArchivedWarning
)
func (mt ModalType) newForm() Form {
@@ -56,6 +59,17 @@ type ModalBoxModel struct {
form Form
editedID int64
+
+ // For archived warning modal - store punch-in parameters
+ archivedPunchInParams *ArchivedPunchInParams
+}
+
+type ArchivedPunchInParams struct {
+ ClientID string
+ ProjectID string
+ Description string
+ Rate *float64
+ EntityType string // "client" or "project"
}
func (m *ModalBoxModel) HandleKeyPress(msg tea.KeyMsg) tea.Cmd {
@@ -94,6 +108,8 @@ func (m ModalBoxModel) Render() string {
return m.RenderFormModal("⏰ Time Entry")
case ModalTypeDeleteConfirmation:
return m.RenderDeleteConfirmation()
+ case ModalTypeArchivedWarning:
+ return m.RenderArchivedWarning()
case ModalTypeClient:
return m.RenderFormModal("👤 Client")
case ModalTypeProjectCreate, ModalTypeProjectEdit:
@@ -128,6 +144,21 @@ func (m ModalBoxModel) RenderDeleteConfirmation() string {
)
}
+func (m ModalBoxModel) RenderArchivedWarning() string {
+ entityType := "client"
+ if m.archivedPunchInParams != nil {
+ entityType = m.archivedPunchInParams.EntityType
+ }
+
+ return fmt.Sprintf(
+ "%s\n\nThis %s is archived.\n\nContinuing will unarchive it and start tracking time.\n\n%s Continue %s Cancel",
+ modalTitleStyle.Render("⚠️ Archived "+entityType),
+ entityType,
+ boldStyle.Render("[Enter]"),
+ boldStyle.Render("[Esc]"),
+ )
+}
+
func (m *ModalBoxModel) activateCreateProjectModal(am AppModel) {
m.activate(ModalTypeProjectCreate, 0, am)
if am.selectedBox == ProjectsBox && len(am.projectsBox.clients) > 0 {
@@ -184,7 +215,7 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
if err != nil {
return reOpenModal()
}
- return tea.Sequence(am.refreshCmd, func() tea.Msg { return recheckBounds{} })
+ return tea.Sequence(closeModal(), am.refreshCmd, func() tea.Msg { return recheckBounds{} })
case ModalTypeEntry:
if err := m.form.Error(); err != nil {
@@ -202,7 +233,7 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
return reOpenModal()
}
- return am.refreshCmd
+ return tea.Sequence(closeModal(), am.refreshCmd)
case ModalTypeClient:
if err := m.form.Error(); err != nil {
@@ -238,7 +269,7 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
}
}
- return am.refreshCmd
+ return tea.Sequence(closeModal(), am.refreshCmd)
case ModalTypeProjectCreate:
if err := m.form.Error(); err != nil {
@@ -261,7 +292,7 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
return reOpenModal()
}
- return am.refreshCmd
+ return tea.Sequence(closeModal(), am.refreshCmd)
case ModalTypeProjectEdit:
if err := m.form.Error(); err != nil {
@@ -284,7 +315,7 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
return reOpenModal()
}
- return am.refreshCmd
+ return tea.Sequence(closeModal(), am.refreshCmd)
case ModalTypeHistoryFilter:
if err := m.form.Error(); err != nil {
@@ -335,14 +366,55 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
}
// Return filter update message
- return func() tea.Msg { return updateHistoryFilter(newFilter) }
+ return tea.Sequence(closeModal(), func() tea.Msg { return updateHistoryFilter(newFilter) })
case ModalTypeGenerateReport:
if err := m.form.Error(); err != nil {
return reOpenModal()
}
- return generateReport(m, am)
+ // Validate report type
+ var genFunc func(context.Context, *queries.Queries, reports.ReportParams) (*reports.ReportResult, error)
+ switch strings.ToLower(m.form.fields[0].Value()) {
+ case "invoice":
+ genFunc = reports.GenerateInvoice
+ case "timesheet":
+ genFunc = reports.GenerateTimesheet
+ case "unified":
+ genFunc = reports.GenerateUnifiedReport
+ default:
+ m.form.fields[0].Err = errors.New("pick one of invoice, timesheet, or unified")
+ return reOpenModal()
+ }
+
+ // Parse date range
+ dateRange, err := reports.ParseDateRange(m.form.fields[1].Value())
+ if err != nil {
+ m.form.fields[1].Err = fmt.Errorf("invalid date range: %v", err)
+ return reOpenModal()
+ }
+
+ // Parse timezone
+ var tz *time.Location
+ tzstr := m.form.fields[5].Value()
+ if tzstr == "" {
+ tz = time.Local
+ } else {
+ zone, err := time.LoadLocation(tzstr)
+ if err != nil {
+ m.form.fields[5].Err = err
+ return reOpenModal()
+ }
+ tz = zone
+ }
+
+ return generateReport(am, genFunc, reports.ReportParams{
+ ClientName: m.form.fields[2].Value(),
+ ProjectName: m.form.fields[3].Value(),
+ DateRange: dateRange,
+ OutputPath: m.form.fields[4].Value(),
+ Timezone: tz,
+ })
case ModalTypeContractor:
if err := m.form.Error(); err != nil {
@@ -358,7 +430,25 @@ func (m *ModalBoxModel) SubmitForm(am AppModel) tea.Cmd {
return reOpenModal()
}
- return am.refreshCmd
+ return tea.Sequence(closeModal(), am.refreshCmd)
+
+ case ModalTypeArchivedWarning:
+ // User confirmed unarchiving - punch in with autoUnarchive=true
+ if m.archivedPunchInParams == nil {
+ return nil
+ }
+
+ a := actions.New(am.queries)
+ _, _ = a.PunchIn(
+ context.Background(),
+ m.archivedPunchInParams.ClientID,
+ m.archivedPunchInParams.ProjectID,
+ m.archivedPunchInParams.Description,
+ m.archivedPunchInParams.Rate,
+ true, // autoUnarchive
+ )
+
+ return tea.Sequence(closeModal(), am.refreshCmd)
}
return nil