diff --git a/cmd/activities.go b/cmd/activities.go index 682fa30..89f5b51 100644 --- a/cmd/activities.go +++ b/cmd/activities.go @@ -3,27 +3,43 @@ package cmd import ( "fmt" "moco/data" + "os" "strconv" "github.com/charmbracelet/huh" + "github.com/joho/godotenv" "github.com/spf13/cobra" ) -var newCmd = &cobra.Command{ - Use: "new", +var createCmd = &cobra.Command{ + Use: "create", Short: "Create a new activity", Run: func(cmd *cobra.Command, args []string) { - projectId, _ := cmd.Flags().GetInt("project") - taskId, _ := cmd.Flags().GetInt("task") + projects, err := data.GetProjects() + if err != nil { + fmt.Println("Could not retrieve projects", err) + return + } + + // flags has highest priority + projectId, err := cmd.Flags().GetInt("project") + taskId, err := cmd.Flags().GetInt("task") + minutes, err := cmd.Flags().GetInt("minutes") description, err := cmd.Flags().GetString("description") - projects, err := data.GetProjects() - if err != nil { - fmt.Println("Could not retrieve projects", err) - } - + // env has second priority + err = godotenv.Load(".moco") + if projectId == 0 && err == nil { + projectId, err = strconv.Atoi(os.Getenv("MOCO_PROJECT_ID")) + fmt.Println("read projectId", projectId) + } + if taskId == 0 && err == nil { + taskId, err = strconv.Atoi(os.Getenv("MOCO_TASK_ID")) + fmt.Println("read taskId", taskId) + } + + // if no flags or config, prompt if projectId == 0 { - options := make([]huh.Option[int], len(projects)) for i, p := range projects { options[i] = huh.NewOption[int](p.Name, p.Id) @@ -55,11 +71,26 @@ var newCmd = &cobra.Command{ } } - if description == "" { - huh.NewInput().Title("Description:").Prompt(">").Value(&description).Run() - } - - err = data.CreateActivity(projectId, taskId, description) + if description == "" { + huh.NewInput().Title("Description:").Prompt("> ").Value(&description).Run() + } + + if minutes == 0 { + var minutesStr string + huh.NewInput().Title("Time (minutes, entering '0' or empty will start a timer):").Prompt("> ").Value(&minutesStr).Run() + if minutesStr == "" { + minutes = 0 + } else { + minutes, err = strconv.Atoi(minutesStr) + if err != nil { + fmt.Println("Invalid minutes") + return + } + } + } + + + err = data.CreateActivity(projectId, taskId, description, minutes) if err != nil { fmt.Println("Could not create activity:", err) } @@ -79,13 +110,13 @@ var editCmd = &cobra.Command{ fmt.Println("Invalid activity id") return } - seconds, err := cmd.Flags().GetInt("time") + minutes, err := cmd.Flags().GetInt("time") description, err := cmd.Flags().GetString("description") - if err != nil || (seconds == 0 && description == "") { + if err != nil || (minutes == 0 && description == "") { cmd.Help() return } - err = data.EditActivity(activityId, seconds, description) + err = data.EditActivity(activityId, minutes, description) if err != nil { fmt.Println("Could not edit activity:", err) } @@ -118,14 +149,16 @@ var activitiesCmd = &cobra.Command{ } func init() { - editCmd.Flags().IntP("time", "t", 0, "Set the time for the activity (in seconds)") + editCmd.Flags().IntP("time", "t", 0, "Set the time for the activity (in minutes)") editCmd.Flags().StringP("description", "d", "", "Set the description for the activity") - activitiesCmd.AddCommand(editCmd) - - newCmd.Flags().Bool("no-start", false, "Don't start the activity when created") - activitiesCmd.AddCommand(newCmd) + createCmd.Flags().IntP("project", "p", 0, "Set the project for the activity") + createCmd.Flags().IntP("task", "t", 0, "Set the task for the activity") + createCmd.Flags().StringP("description", "d", "", "Set the description for the activity") + createCmd.Flags().IntP("minutes", "m", 0, "Set the number of minutes for the activity") + activitiesCmd.AddCommand(editCmd) + activitiesCmd.AddCommand(createCmd) activitiesCmd.AddCommand(deleteCmd) rootCmd.AddCommand(activitiesCmd) diff --git a/cmd/login.go b/cmd/login.go index 04d5868..b3a024b 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -32,10 +32,10 @@ var loginCmd = &cobra.Command{ form := huh.NewForm( huh.NewGroup( - huh.NewInput().Title("Domain").Prompt("?").Value(&domain), - huh.NewInput().Title("First Name").Prompt("?").Value(&firstName), - huh.NewInput().Title("Last Name").Prompt("?").Value(&lastName), - huh.NewInput().Title("API Key").Prompt("?").Value(&apiKey).Password(true), + huh.NewInput().Title("Domain").Prompt("? ").Value(&domain), + huh.NewInput().Title("First Name").Prompt("? ").Value(&firstName), + huh.NewInput().Title("Last Name").Prompt("? ").Value(&lastName), + huh.NewInput().Title("API Key").Prompt("? ").Value(&apiKey).Password(true), ), ) diff --git a/cmd/track.go b/cmd/track.go new file mode 100644 index 0000000..180e808 --- /dev/null +++ b/cmd/track.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "fmt" + "moco/data" + "os" + "strconv" + + "github.com/charmbracelet/huh" + "github.com/joho/godotenv" + "github.com/spf13/cobra" +) + +var trackCmd = &cobra.Command{ + Use: "track", + Short: "Start tracking a new activity", + Run: func(cmd *cobra.Command, args []string) { + projects, err := data.GetProjects() + if err != nil { + fmt.Println("Could not retrieve projects", err) + return + } + + // flags get highest priority + projectId, _ := cmd.Flags().GetInt("project") + taskId, _ := cmd.Flags().GetInt("task") + description, _ := cmd.Flags().GetString("description") + + // env has second priority + err = godotenv.Load(".moco") + if projectId == 0 && err == nil { + projectId, err = strconv.Atoi(os.Getenv("MOCO_PROJECT_ID")) + fmt.Println("read projectId", projectId) + } + if taskId == 0 && err == nil { + taskId, err = strconv.Atoi(os.Getenv("MOCO_TASK_ID")) + fmt.Println("read taskId", taskId) + } + + // if no flags, prompt + if projectId == 0 || err != nil { + options := make([]huh.Option[int], len(projects)) + for i, p := range projects { + options[i] = huh.NewOption[int](p.Name, p.Id) + } + + pform := huh.NewSelect[int]().Options(options...).Value(&projectId) + pform.Run() + if projectId == 0 { + return + } + } + + var project data.Project + for _, p := range projects { + if p.Id == projectId { + project = p + } + } + + if taskId == 0 { + options := make([]huh.Option[int], len(project.Tasks)) + for i, t := range project.Tasks { + options[i] = huh.NewOption[int](t.Name, t.Id) + } + tform := huh.NewSelect[int]().Options(options...).Value(&taskId) + tform.Run() + if taskId == 0 { + return + } + } + + if description == "" { + huh.NewInput().Title("Description:").Prompt("> ").Value(&description).Run() + } + + err = data.StartActivity(projectId, taskId, description) + if err != nil { + fmt.Println("Could not create activity:", err) + } + }, +} + +func init() { + trackCmd.Flags().IntP("project", "p", 0, "Set the project for the activity") + trackCmd.Flags().IntP("task", "t", 0, "Set the task for the activity") + trackCmd.Flags().StringP("description", "d", "", "Set the description for the activity") + + rootCmd.AddCommand(trackCmd) +} diff --git a/data/activities.go b/data/activities.go index b8c0601..0f3a1d9 100644 --- a/data/activities.go +++ b/data/activities.go @@ -33,30 +33,6 @@ type Activity struct { } } -func StartActivity(activityId int) error { - config := config.Init() - apiKey := config.GetString("api_key") - if apiKey == "" { - return fmt.Errorf("api_key not set") - } - domain := config.GetString("domain") - if domain == "" { - return fmt.Errorf("domain not set") - } - - req, _ := http.NewRequest("PATCH", fmt.Sprintf("https://%s.mocoapp.com/api/v1/activities/%d/start_timer", domain, activityId), nil) - req.Header.Add("Authorization", fmt.Sprintf("Token token=%s", apiKey)) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil || resp.StatusCode == 422 { - return fmt.Errorf("Something went wrong") - } else if resp.StatusCode == 404 { - return fmt.Errorf("Activity not found") - } - defer resp.Body.Close() - return nil -} - func StopActivity(activityId int) error { config := config.Init() apiKey := config.GetString("api_key") @@ -79,7 +55,46 @@ func StopActivity(activityId int) error { return nil } -func CreateActivity(projectId int, taskId int, description string) error { +func CreateActivity(projectId int, taskId int, description string, minutes int) error { + config := config.Init() + apiKey := config.GetString("api_key") + if apiKey == "" { + return fmt.Errorf("api_key not set") + } + domain := config.GetString("domain") + if domain == "" { + return fmt.Errorf("domain not set") + } + + type ActivityBody struct { + ProjectId int `json:"project_id"` + TaskId int `json:"task_id"` + Description string `json:"description"` + Date string `json:"date"` + Seconds int `json:"seconds"` + } + marshaledBody, err := json.Marshal(ActivityBody{ + ProjectId: projectId, + TaskId: taskId, + Description: description, + Date: time.Now().Format("2006-01-02"), + Seconds: minutes * 60, + }) + fmt.Println(string(marshaledBody)) + + req, _ := http.NewRequest("POST", fmt.Sprintf("https://%s.mocoapp.com/api/v1/activities", domain), bytes.NewReader(marshaledBody)) + req.Header.Add("Authorization", fmt.Sprintf("Token token=%s", apiKey)) + req.Header.Add("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(req) + if err != nil || resp.StatusCode == 422 { + return fmt.Errorf("Error on response.\n[ERROR] - %s", err) + } + defer resp.Body.Close() + return nil +} + +func StartActivity(projectId int, taskId int, description string) error { config := config.Init() apiKey := config.GetString("api_key") domain := config.GetString("domain") diff --git a/go.mod b/go.mod index 8a04b00..7ee6178 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/joho/godotenv v1.5.1 github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 0d749da..df047ea 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=