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 fmt.Fprintln(cmd.ErrOrStderr(), ui.Error.Render("Failed to get task"))
52
53 return err
54 }
55
56 if mustGetBoolFlag(cmd, "json") {
57 return outputTaskJSON(cmd, task)
58 }
59
60 return printTaskDetails(cmd, task)
61}
62
63func outputTaskJSON(cmd *cobra.Command, task *lunatask.Task) error {
64 enc := json.NewEncoder(cmd.OutOrStdout())
65 enc.SetIndent("", " ")
66
67 if err := enc.Encode(task); err != nil {
68 return fmt.Errorf("encoding JSON: %w", err)
69 }
70
71 return nil
72}
73
74func printTaskDetails(cmd *cobra.Command, task *lunatask.Task) error {
75 link, _ := deeplink.Build(deeplink.Task, task.ID)
76
77 status := "unknown"
78 if task.Status != nil {
79 status = string(*task.Status)
80 }
81
82 fmt.Fprintf(cmd.OutOrStdout(), "%s\n", ui.H1.Render("Task: "+status))
83 fmt.Fprintf(cmd.OutOrStdout(), " ID: %s\n", task.ID)
84 fmt.Fprintf(cmd.OutOrStdout(), " Link: %s\n", link)
85
86 printAreaGoal(cmd, task)
87
88 if task.ScheduledOn != nil {
89 fmt.Fprintf(cmd.OutOrStdout(), " Scheduled: %s\n", ui.FormatDate(task.ScheduledOn.Time))
90 }
91
92 if task.CompletedAt != nil {
93 fmt.Fprintf(cmd.OutOrStdout(), " Completed: %s\n", ui.FormatDate(*task.CompletedAt))
94 }
95
96 printTaskAttributes(cmd, task)
97
98 fmt.Fprintf(cmd.OutOrStdout(), " Created: %s\n", ui.FormatDate(task.CreatedAt))
99 fmt.Fprintf(cmd.OutOrStdout(), " Updated: %s\n", ui.FormatDate(task.UpdatedAt))
100
101 return nil
102}
103
104func printAreaGoal(cmd *cobra.Command, task *lunatask.Task) {
105 cfg, _ := config.Load()
106
107 if task.AreaID != nil {
108 areaDisplay := *task.AreaID
109 if cfg != nil {
110 if area := cfg.AreaByID(*task.AreaID); area != nil {
111 areaDisplay = fmt.Sprintf("%s (%s)", area.Name, area.Key)
112 }
113 }
114
115 fmt.Fprintf(cmd.OutOrStdout(), " Area: %s\n", areaDisplay)
116 }
117
118 if task.GoalID != nil {
119 goalDisplay := *task.GoalID
120 if cfg != nil {
121 if match := cfg.GoalByID(*task.GoalID); match != nil {
122 goalDisplay = fmt.Sprintf("%s (%s)", match.Goal.Name, match.Goal.Key)
123 }
124 }
125
126 fmt.Fprintf(cmd.OutOrStdout(), " Goal: %s\n", goalDisplay)
127 }
128}
129
130func printTaskAttributes(cmd *cobra.Command, task *lunatask.Task) {
131 if task.Estimate != nil {
132 fmt.Fprintf(cmd.OutOrStdout(), " Estimate: %d min\n", *task.Estimate)
133 }
134
135 if task.Priority != nil && *task.Priority != lunatask.PriorityNormal {
136 fmt.Fprintf(cmd.OutOrStdout(), " Priority: %s\n", task.Priority)
137 }
138
139 if task.Progress != nil {
140 fmt.Fprintf(cmd.OutOrStdout(), " Progress: %d%%\n", *task.Progress)
141 }
142
143 if task.Motivation != nil && *task.Motivation != lunatask.MotivationUnknown {
144 fmt.Fprintf(cmd.OutOrStdout(), " Motivation: %s\n", *task.Motivation)
145 }
146
147 if task.Eisenhower != nil && *task.Eisenhower != lunatask.EisenhowerUncategorized {
148 fmt.Fprintf(cmd.OutOrStdout(), " Eisenhower: %s\n", formatEisenhower(*task.Eisenhower))
149 }
150}
151
152func formatEisenhower(e lunatask.Eisenhower) string {
153 switch e {
154 case lunatask.EisenhowerUncategorized:
155 return "uncategorized"
156 case lunatask.EisenhowerDoNow:
157 return "do now (important + urgent)"
158 case lunatask.EisenhowerDoLater:
159 return "do later (important)"
160 case lunatask.EisenhowerDelegate:
161 return "delegate (urgent)"
162 case lunatask.EisenhowerEliminate:
163 return "eliminate"
164 default:
165 return "unknown"
166 }
167}