summaryrefslogtreecommitdiff
path: root/internal/commands/in.go
blob: e7847f65d9d0f675189b3f00cf0be2500e3712c2 (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
package commands

import (
	"fmt"

	"punchcard/internal/actions"
	punchctx "punchcard/internal/context"

	"github.com/spf13/cobra"
)

func NewInCmd() *cobra.Command {
	var clientFlag, projectFlag string

	cmd := &cobra.Command{
		Use:     "in [<description>]",
		Aliases: []string{"i"},
		Short:   "Start a timer",
		Long: `Start tracking time for the current work session.
	
If no flags are provided, copies the most recent time entry.
If -p/--project is provided without -c/--client, uses the project's client.
If a timer is already active:
  - Same parameters: no-op
  - Different parameters: stops current timer and starts new one

Examples:
  punch in                                               # Copy most recent entry
  punch in "Working on website redesign"                 # Copy most recent but change description
  punch in -c "Acme Corp" "Client meeting"               # Specific client
  punch in -p "Website Redesign" "Frontend development"  # Project (client auto-selected)
  punch in --client 1 --project "Website Redesign"       # Explicit client and project`,
		Args: cobra.MaximumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			var description string
			if len(args) > 0 {
				description = args[0]
			}

			billableRateFloat, _ := cmd.Flags().GetFloat64("hourly-rate")
			var billableRate *float64
			if billableRateFloat > 0 {
				billableRate = &billableRateFloat
			}

			q := punchctx.GetDB(cmd.Context())
			if q == nil {
				return fmt.Errorf("database not available in context")
			}

			a := actions.New(q)

			// Use the actions package based on what flags were provided
			var session *actions.TimerSession
			var err error

			if clientFlag == "" && projectFlag == "" {
				// Use most recent entry
				session, err = a.PunchInMostRecent(cmd.Context(), description, billableRate)
			} else {
				// Use specified client/project
				session, err = a.PunchIn(cmd.Context(), clientFlag, projectFlag, description, billableRate)
			}

			if err != nil {
				return err
			}

			// Handle different response types
			if session.WasNoOp {
				cmd.Printf("Timer already active with same parameters (ID: %d)\n", session.ID)
				return nil
			}

			// Print stopped timer message if we stopped one
			if session.StoppedEntryID != nil {
				cmd.Printf("Stopped previous timer (ID: %d)\n", *session.StoppedEntryID)
			}

			// Build output message
			output := fmt.Sprintf("Started timer (ID: %d) for client: %s", session.ID, session.ClientName)

			if session.ProjectName != "" {
				output += fmt.Sprintf(", project: %s", session.ProjectName)
			}

			if session.Description != "" {
				output += fmt.Sprintf(", description: %s", session.Description)
			}

			cmd.Print(output + "\n")
			return nil
		},
	}

	cmd.Flags().StringVarP(&clientFlag, "client", "c", "", "Client name or ID")
	cmd.Flags().StringVarP(&projectFlag, "project", "p", "", "Project name or ID")
	cmd.Flags().Float64("hourly-rate", 0, "Override hourly billable rate for this time entry")

	return cmd
}