show.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5package task
  6
  7import (
  8	"encoding/json"
  9	"fmt"
 10
 11	"git.secluded.site/go-lunatask"
 12	"git.secluded.site/lune/internal/client"
 13	"git.secluded.site/lune/internal/config"
 14	"git.secluded.site/lune/internal/deeplink"
 15	"git.secluded.site/lune/internal/ui"
 16	"git.secluded.site/lune/internal/validate"
 17	"github.com/spf13/cobra"
 18)
 19
 20// ShowCmd displays a task by ID. Exported for potential use by shortcuts.
 21var ShowCmd = &cobra.Command{
 22	Use:   "show ID",
 23	Short: "Show task details",
 24	Long: `Show detailed information for a task.
 25
 26Accepts a UUID or lunatask:// deep link.
 27
 28Note: Due to end-to-end encryption, task name and notes
 29are not available through the API. Only metadata is shown.`,
 30	Args: cobra.ExactArgs(1),
 31	RunE: runShow,
 32}
 33
 34func init() {
 35	ShowCmd.Flags().Bool("json", false, "Output as JSON")
 36}
 37
 38func runShow(cmd *cobra.Command, args []string) error {
 39	id, err := validate.Reference(args[0])
 40	if err != nil {
 41		return err
 42	}
 43
 44	apiClient, err := client.New()
 45	if err != nil {
 46		return err
 47	}
 48
 49	task, err := apiClient.GetTask(cmd.Context(), id)
 50	if err != nil {
 51		return err
 52	}
 53
 54	if mustGetBoolFlag(cmd, "json") {
 55		return outputTaskJSON(cmd, task)
 56	}
 57
 58	return printTaskDetails(cmd, task)
 59}
 60
 61func outputTaskJSON(cmd *cobra.Command, task *lunatask.Task) error {
 62	enc := json.NewEncoder(cmd.OutOrStdout())
 63	enc.SetIndent("", "  ")
 64
 65	if err := enc.Encode(task); err != nil {
 66		return fmt.Errorf("encoding JSON: %w", err)
 67	}
 68
 69	return nil
 70}
 71
 72func printTaskDetails(cmd *cobra.Command, task *lunatask.Task) error {
 73	link, _ := deeplink.Build(deeplink.Task, task.ID)
 74
 75	status := "unknown"
 76	if task.Status != nil {
 77		status = string(*task.Status)
 78	}
 79
 80	fmt.Fprintf(cmd.OutOrStdout(), "%s\n", ui.H1.Render("Task: "+status))
 81	fmt.Fprintf(cmd.OutOrStdout(), "  ID:   %s\n", task.ID)
 82	fmt.Fprintf(cmd.OutOrStdout(), "  Link: %s\n", link)
 83
 84	printAreaGoal(cmd, task)
 85
 86	if task.ScheduledOn != nil {
 87		fmt.Fprintf(cmd.OutOrStdout(), "  Scheduled: %s\n", ui.FormatDate(task.ScheduledOn.Time))
 88	}
 89
 90	if task.CompletedAt != nil {
 91		fmt.Fprintf(cmd.OutOrStdout(), "  Completed: %s\n", ui.FormatDate(*task.CompletedAt))
 92	}
 93
 94	printTaskAttributes(cmd, task)
 95
 96	fmt.Fprintf(cmd.OutOrStdout(), "  Created: %s\n", ui.FormatDate(task.CreatedAt))
 97	fmt.Fprintf(cmd.OutOrStdout(), "  Updated: %s\n", ui.FormatDate(task.UpdatedAt))
 98
 99	return nil
100}
101
102func printAreaGoal(cmd *cobra.Command, task *lunatask.Task) {
103	cfg, _ := config.Load()
104
105	if task.AreaID != nil {
106		areaDisplay := *task.AreaID
107		if cfg != nil {
108			if area := cfg.AreaByID(*task.AreaID); area != nil {
109				areaDisplay = fmt.Sprintf("%s (%s)", area.Name, area.Key)
110			}
111		}
112
113		fmt.Fprintf(cmd.OutOrStdout(), "  Area: %s\n", areaDisplay)
114	}
115
116	if task.GoalID != nil {
117		goalDisplay := *task.GoalID
118		if cfg != nil {
119			if match := cfg.GoalByID(*task.GoalID); match != nil {
120				goalDisplay = fmt.Sprintf("%s (%s)", match.Goal.Name, match.Goal.Key)
121			}
122		}
123
124		fmt.Fprintf(cmd.OutOrStdout(), "  Goal: %s\n", goalDisplay)
125	}
126}
127
128func printTaskAttributes(cmd *cobra.Command, task *lunatask.Task) {
129	if task.Estimate != nil {
130		fmt.Fprintf(cmd.OutOrStdout(), "  Estimate: %d min\n", *task.Estimate)
131	}
132
133	if task.Priority != nil && *task.Priority != lunatask.PriorityNormal {
134		fmt.Fprintf(cmd.OutOrStdout(), "  Priority: %s\n", task.Priority)
135	}
136
137	if task.Progress != nil {
138		fmt.Fprintf(cmd.OutOrStdout(), "  Progress: %d%%\n", *task.Progress)
139	}
140
141	if task.Motivation != nil && *task.Motivation != lunatask.MotivationUnknown {
142		fmt.Fprintf(cmd.OutOrStdout(), "  Motivation: %s\n", *task.Motivation)
143	}
144
145	if task.Eisenhower != nil && *task.Eisenhower != lunatask.EisenhowerUncategorized {
146		fmt.Fprintf(cmd.OutOrStdout(), "  Eisenhower: %s\n", formatEisenhower(*task.Eisenhower))
147	}
148}
149
150func formatEisenhower(e lunatask.Eisenhower) string {
151	switch e {
152	case lunatask.EisenhowerUncategorized:
153		return "uncategorized"
154	case lunatask.EisenhowerDoNow:
155		return "do now (important + urgent)"
156	case lunatask.EisenhowerDoLater:
157		return "do later (important)"
158	case lunatask.EisenhowerDelegate:
159		return "delegate (urgent)"
160	case lunatask.EisenhowerEliminate:
161		return "eliminate"
162	default:
163		return "unknown"
164	}
165}