diff --git a/cmd/task/add.go b/cmd/task/add.go index e3981d0136c998b2064df447c81aaf1439da87a9..fc104fdf10aeac137575166b86ff749f45b09f78 100644 --- a/cmd/task/add.go +++ b/cmd/task/add.go @@ -172,7 +172,7 @@ func applyOptionalFlags(cmd *cobra.Command, builder *lunatask.TaskBuilder) error } if priority, _ := cmd.Flags().GetInt("priority"); priority != 0 { - builder.WithPriority(priority) + builder.Priority(lunatask.Priority(priority)) } if estimate, _ := cmd.Flags().GetInt("estimate"); estimate != 0 { diff --git a/cmd/task/show.go b/cmd/task/show.go index 781465809a018d09aa4f505c51364722ca81748f..f8f3f46c06bfb3ecd7b3a0ce1907ddfd07116802 100644 --- a/cmd/task/show.go +++ b/cmd/task/show.go @@ -5,8 +5,14 @@ package task import ( + "encoding/json" "fmt" + "git.secluded.site/go-lunatask" + "git.secluded.site/lune/internal/client" + "git.secluded.site/lune/internal/config" + "git.secluded.site/lune/internal/deeplink" + "git.secluded.site/lune/internal/ui" "git.secluded.site/lune/internal/validate" "github.com/spf13/cobra" ) @@ -15,20 +21,147 @@ import ( var ShowCmd = &cobra.Command{ Use: "show ID", Short: "Show task details", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - id, err := validate.Reference(args[0]) - if err != nil { - return err - } + Long: `Show detailed information for a task. - // TODO: implement task show - fmt.Fprintf(cmd.OutOrStdout(), "Showing task %s (not yet implemented)\n", id) +Accepts a UUID or lunatask:// deep link. - return nil - }, +Note: Due to end-to-end encryption, task name and notes +are not available through the API. Only metadata is shown.`, + Args: cobra.ExactArgs(1), + RunE: runShow, } func init() { ShowCmd.Flags().Bool("json", false, "Output as JSON") } + +func runShow(cmd *cobra.Command, args []string) error { + id, err := validate.Reference(args[0]) + if err != nil { + return err + } + + apiClient, err := client.New() + if err != nil { + return err + } + + task, err := apiClient.GetTask(cmd.Context(), id) + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), ui.Error.Render("Failed to get task")) + + return err + } + + if mustGetBoolFlag(cmd, "json") { + return outputTaskJSON(cmd, task) + } + + return printTaskDetails(cmd, task) +} + +func outputTaskJSON(cmd *cobra.Command, task *lunatask.Task) error { + enc := json.NewEncoder(cmd.OutOrStdout()) + enc.SetIndent("", " ") + + if err := enc.Encode(task); err != nil { + return fmt.Errorf("encoding JSON: %w", err) + } + + return nil +} + +func printTaskDetails(cmd *cobra.Command, task *lunatask.Task) error { + link, _ := deeplink.Build(deeplink.Task, task.ID) + + status := "unknown" + if task.Status != nil { + status = string(*task.Status) + } + + fmt.Fprintf(cmd.OutOrStdout(), "%s\n", ui.H1.Render("Task: "+status)) + fmt.Fprintf(cmd.OutOrStdout(), " ID: %s\n", task.ID) + fmt.Fprintf(cmd.OutOrStdout(), " Link: %s\n", link) + + printAreaGoal(cmd, task) + + if task.ScheduledOn != nil { + fmt.Fprintf(cmd.OutOrStdout(), " Scheduled: %s\n", ui.FormatDate(task.ScheduledOn.Time)) + } + + if task.CompletedAt != nil { + fmt.Fprintf(cmd.OutOrStdout(), " Completed: %s\n", ui.FormatDate(*task.CompletedAt)) + } + + printTaskAttributes(cmd, task) + + fmt.Fprintf(cmd.OutOrStdout(), " Created: %s\n", ui.FormatDate(task.CreatedAt)) + fmt.Fprintf(cmd.OutOrStdout(), " Updated: %s\n", ui.FormatDate(task.UpdatedAt)) + + return nil +} + +func printAreaGoal(cmd *cobra.Command, task *lunatask.Task) { + cfg, _ := config.Load() + + if task.AreaID != nil { + areaDisplay := *task.AreaID + if cfg != nil { + if area := cfg.AreaByID(*task.AreaID); area != nil { + areaDisplay = fmt.Sprintf("%s (%s)", area.Name, area.Key) + } + } + + fmt.Fprintf(cmd.OutOrStdout(), " Area: %s\n", areaDisplay) + } + + if task.GoalID != nil { + goalDisplay := *task.GoalID + if cfg != nil { + if match := cfg.GoalByID(*task.GoalID); match != nil { + goalDisplay = fmt.Sprintf("%s (%s)", match.Goal.Name, match.Goal.Key) + } + } + + fmt.Fprintf(cmd.OutOrStdout(), " Goal: %s\n", goalDisplay) + } +} + +func printTaskAttributes(cmd *cobra.Command, task *lunatask.Task) { + if task.Estimate != nil { + fmt.Fprintf(cmd.OutOrStdout(), " Estimate: %d min\n", *task.Estimate) + } + + if task.Priority != nil && *task.Priority != lunatask.PriorityNormal { + fmt.Fprintf(cmd.OutOrStdout(), " Priority: %s\n", task.Priority) + } + + if task.Progress != nil { + fmt.Fprintf(cmd.OutOrStdout(), " Progress: %d%%\n", *task.Progress) + } + + if task.Motivation != nil && *task.Motivation != lunatask.MotivationUnknown { + fmt.Fprintf(cmd.OutOrStdout(), " Motivation: %s\n", *task.Motivation) + } + + if task.Eisenhower != nil && *task.Eisenhower != lunatask.EisenhowerUncategorized { + fmt.Fprintf(cmd.OutOrStdout(), " Eisenhower: %s\n", formatEisenhower(*task.Eisenhower)) + } +} + +func formatEisenhower(e lunatask.Eisenhower) string { + switch e { + case lunatask.EisenhowerUncategorized: + return "uncategorized" + case lunatask.EisenhowerDoNow: + return "do now (important + urgent)" + case lunatask.EisenhowerDoLater: + return "do later (important)" + case lunatask.EisenhowerDelegate: + return "delegate (urgent)" + case lunatask.EisenhowerEliminate: + return "eliminate" + default: + return "unknown" + } +} diff --git a/go.mod b/go.mod index fd386f84583dae82ddcbf33f5c90ad4177f007ce..9d59632f4271ace6d5a9589fa02cd31efb97a249 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.secluded.site/lune go 1.25.5 require ( - git.secluded.site/go-lunatask v0.1.0-rc8 + git.secluded.site/go-lunatask v0.1.0-rc9 github.com/BurntSushi/toml v1.6.0 github.com/charmbracelet/fang v0.4.4 github.com/charmbracelet/huh v0.8.0 diff --git a/go.sum b/go.sum index ace299c61faa045b7e4340246291517924846b36..3d9e80eb5d49a4374737b40e462be67e0fd4f559 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXy al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYpB4nj+d6HTWr1onlmlyuGVNfL9gAi8iB3k= charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU= -git.secluded.site/go-lunatask v0.1.0-rc8 h1:gfL5VII4KTbv83P87VUhPcN5gmUR7a6XKkokBSgDGRs= -git.secluded.site/go-lunatask v0.1.0-rc8/go.mod h1:sWUQxme1z7qfsfS59nU5hqPvsRCt+HBmT/yBeIn6Fmc= +git.secluded.site/go-lunatask v0.1.0-rc9 h1:ri8PDl7Xzg3mGStvHBxvL5PKOlBSZGxKDBzkqurLpEw= +git.secluded.site/go-lunatask v0.1.0-rc9/go.mod h1:sWUQxme1z7qfsfS59nU5hqPvsRCt+HBmT/yBeIn6Fmc= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= diff --git a/internal/config/config.go b/internal/config/config.go index a51c4ad7a240f52495b1093bf03d003a42b03d99..41a794f47ab7d07ac36be931938c8886597794b4 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -194,3 +194,32 @@ func (c *Config) FindGoalsByKey(key string) []GoalMatch { return matches } + +// AreaByID finds an area by its ID. +func (c *Config) AreaByID(id string) *Area { + for i := range c.Areas { + if c.Areas[i].ID == id { + return &c.Areas[i] + } + } + + return nil +} + +// GoalByID finds a goal by its ID across all areas. +func (c *Config) GoalByID(id string) *GoalMatch { + for i := range c.Areas { + area := &c.Areas[i] + + for j := range area.Goals { + if area.Goals[j].ID == id { + return &GoalMatch{ + Goal: &area.Goals[j], + Area: area, + } + } + } + } + + return nil +}