package tui import ( "context" "errors" "git.tjp.lol/punchcard/internal/actions" "git.tjp.lol/punchcard/internal/queries" "git.tjp.lol/punchcard/internal/reports" tea "github.com/charmbracelet/bubbletea" ) type ( navigationMsg struct{ Forward bool } selectionMsg struct{ Forward bool } selectionToEnd struct{ Top bool } drillDownMsg struct{} drillUpMsg struct{} modalClosed struct{} openTimeEntryEditor struct{} openContractorEditor struct{} openClientOrProjectEditor struct{} filterHistoryByProjectBox struct{} openModalUnchanged struct{} openDeleteConfirmation struct{} recheckBounds struct{} openCreateClientModal struct{} openCreateProjectModal struct{} openHistoryFilterModal struct{} openReportModal struct{} updateHistoryFilter HistoryFilter archiveSelectedMsg struct { clientID int64 projectID *int64 restoreClientID int64 restoreProjectID *int64 } unarchiveSelectedMsg struct { clientID int64 projectID *int64 restoreClientID int64 restoreProjectID *int64 } toggleShowArchivedMsg struct { restoreClientID int64 restoreProjectID *int64 } restoreSelectionMsg struct{ clientID int64; projectID *int64 } showArchivedWarningMsg struct{ params *ArchivedPunchInParams } reportGenerationSucceeded struct{} reportGenerationFailed struct{ err error } ) func navigate(forward bool) tea.Cmd { return func() tea.Msg { return navigationMsg{forward} } } func punchIn(m AppModel) tea.Cmd { return func() tea.Msg { a := actions.New(m.queries) _, err := a.PunchInMostRecent(context.Background(), "", nil, false) // Handle archived errors by showing modal if err != nil { if errors.Is(err, actions.ErrArchivedClient) { return showArchivedWarningMsg{ params: &ArchivedPunchInParams{ EntityType: "client", }, } } else if errors.Is(err, actions.ErrArchivedProject) { return showArchivedWarningMsg{ params: &ArchivedPunchInParams{ EntityType: "project", }, } } } return m.refreshCmd() } } func punchOut(m AppModel) tea.Cmd { return func() tea.Msg { _, _ = actions.New(m.queries).PunchOut(context.Background()) // TODO: use the returned TimerSession instead of re-querying everything return m.refreshCmd() } } func punchInOnSelection(m AppModel) tea.Cmd { return func() tea.Msg { var clientID, projectID, description string var entryRate *float64 switch m.selectedBox { case ProjectsBox: clientID, projectID, description, entryRate = m.projectsBox.selection() case HistoryBox: clientID, projectID, description, entryRate = m.historyBox.selection() } if clientID == "" { return nil } a := actions.New(m.queries) _, err := a.PunchIn(context.Background(), clientID, projectID, description, entryRate, false) // Handle archived errors by showing modal if err != nil { if errors.Is(err, actions.ErrArchivedClient) { return showArchivedWarningMsg{ params: &ArchivedPunchInParams{ ClientID: clientID, ProjectID: projectID, Description: description, Rate: entryRate, EntityType: "client", }, } } else if errors.Is(err, actions.ErrArchivedProject) { return showArchivedWarningMsg{ params: &ArchivedPunchInParams{ ClientID: clientID, ProjectID: projectID, Description: description, Rate: entryRate, EntityType: "project", }, } } } return m.refreshCmd() } } func selectHistorySummary() tea.Cmd { return func() tea.Msg { return drillDownMsg{} } } func backToHistorySummary() tea.Cmd { return func() tea.Msg { return drillUpMsg{} } } func changeSelection(forward bool) tea.Cmd { return func() tea.Msg { return selectionMsg{forward} } } func changeSelectionToTop() tea.Cmd { return func() tea.Msg { return selectionToEnd{true} } } func changeSelectionToBottom() tea.Cmd { return func() tea.Msg { return selectionToEnd{false} } } func closeModal() tea.Cmd { return func() tea.Msg { return modalClosed{} } } func editCurrentEntry() tea.Cmd { return func() tea.Msg { return openTimeEntryEditor{} } } func editContractor() tea.Cmd { return func() tea.Msg { return openContractorEditor{} } } func editClientOrProject() tea.Cmd { return func() tea.Msg { return openClientOrProjectEditor{} } } func filterHistoryFromProjectBox() tea.Cmd { return func() tea.Msg { return filterHistoryByProjectBox{} } } func reOpenModal() tea.Cmd { return func() tea.Msg { return openModalUnchanged{} } } func confirmDeleteEntry() tea.Cmd { return func() tea.Msg { return openDeleteConfirmation{} } } func createClientModal() tea.Cmd { return func() tea.Msg { return openCreateClientModal{} } } func createProjectModal() tea.Cmd { return func() tea.Msg { return openCreateProjectModal{} } } func createHistoryFilterModal() tea.Cmd { return func() tea.Msg { return openHistoryFilterModal{} } } func createReportModal() tea.Cmd { return func() tea.Msg { return openReportModal{} } } func generateReport(am AppModel, genFunc func(context.Context, *queries.Queries, reports.ReportParams) (*reports.ReportResult, error), params reports.ReportParams) tea.Cmd { return func() tea.Msg { if _, err := genFunc(context.Background(), am.queries, params); err != nil { return reportGenerationFailed{err: err} } return reportGenerationSucceeded{} } } func archiveClientOrProject(clientID int64, projectID *int64) tea.Cmd { return func() tea.Msg { return archiveSelectedMsg{ clientID: clientID, projectID: projectID, restoreClientID: clientID, restoreProjectID: projectID, } } } func unarchiveClientOrProject(clientID int64, projectID *int64) tea.Cmd { return func() tea.Msg { return unarchiveSelectedMsg{ clientID: clientID, projectID: projectID, restoreClientID: clientID, restoreProjectID: projectID, } } } func toggleShowArchived(clientID int64, projectID *int64) tea.Cmd { return func() tea.Msg { return toggleShowArchivedMsg{ restoreClientID: clientID, restoreProjectID: projectID, } } }