summaryrefslogtreecommitdiff
path: root/internal/commands/add_client.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/commands/add_client.go')
-rw-r--r--internal/commands/add_client.go90
1 files changed, 90 insertions, 0 deletions
diff --git a/internal/commands/add_client.go b/internal/commands/add_client.go
new file mode 100644
index 0000000..e35eba9
--- /dev/null
+++ b/internal/commands/add_client.go
@@ -0,0 +1,90 @@
+package commands
+
+import (
+ "database/sql"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "punchcard/internal/context"
+ "punchcard/internal/queries"
+
+ "github.com/spf13/cobra"
+)
+
+func NewAddClientCmd() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "client <name> [<email>]",
+ Short: "Add a new client",
+ Long: "Add a new client to the database. Name can include email in format 'Name <email@domain.com>'",
+ Args: cobra.RangeArgs(1, 2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ name, email := parseNameAndEmail(args)
+
+ billableRateFloat, _ := cmd.Flags().GetFloat64("hourly-rate")
+ billableRate := int64(billableRateFloat * 100)
+
+ q := context.GetDB(cmd.Context())
+ if q == nil {
+ return fmt.Errorf("database not available in context")
+ }
+
+ var emailParam sql.NullString
+ if email != "" {
+ emailParam = sql.NullString{String: email, Valid: true}
+ }
+
+ var billableRateParam sql.NullInt64
+ if billableRate > 0 {
+ billableRateParam = sql.NullInt64{Int64: billableRate, Valid: true}
+ }
+
+ client, err := q.CreateClient(cmd.Context(), queries.CreateClientParams{
+ Name: name,
+ Email: emailParam,
+ BillableRate: billableRateParam,
+ })
+ if err != nil {
+ return fmt.Errorf("failed to create client: %w", err)
+ }
+
+ output := fmt.Sprintf("Created client: %s", client.Name)
+ if client.Email.Valid {
+ output += fmt.Sprintf(" <%s>", client.Email.String)
+ }
+ output += fmt.Sprintf(" (ID: %d)\n", client.ID)
+ cmd.Print(output)
+
+ return nil
+ },
+ }
+
+ cmd.Flags().Float64P("hourly-rate", "r", 0, "Default hourly billable rate for this client")
+
+ return cmd
+}
+
+func parseNameAndEmail(args []string) (string, string) {
+ nameArg := args[0]
+ var emailArg string
+ if len(args) > 1 {
+ emailArg = args[1]
+ }
+
+ if emailArg != "" {
+ if matches := emailAndNameRegex.FindStringSubmatch(emailArg); matches != nil {
+ emailArg = strings.TrimSpace(matches[2])
+ }
+ }
+
+ if matches := emailAndNameRegex.FindStringSubmatch(nameArg); matches != nil {
+ nameArg = strings.TrimSpace(matches[1])
+ if emailArg == "" {
+ emailArg = strings.TrimSpace(matches[2])
+ }
+ }
+
+ return nameArg, emailArg
+}
+
+var emailAndNameRegex = regexp.MustCompile(`^(.+?)<([^>]+@[^>]+)>$`)