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
}
|