@@ -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"
+ }
+}