feat: add colored due date indicators to list and show views
- List view: show relative due date (+12d, -3d, today) after namespace - Show view: colorize full due date based on urgency - Color scheme: red (overdue), yellow (due within 3 days), green (4+ days) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,10 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
@@ -89,13 +91,14 @@ func PrintNodes(w io.Writer, svc service.NodeService, nodes []*models.Node, json
|
||||
}
|
||||
ns_rel_node_titles = append(ns_rel_node_titles, ns_rel_node.Title)
|
||||
}
|
||||
fmt.Fprintf(w, " %s %s %s %s %s %s",
|
||||
fmt.Fprintf(w, " %s %s %s %s %s %s %s",
|
||||
cDim.Sprint(n.ID),
|
||||
render(prioRM, n.GetProperty("prio"), true),
|
||||
render(statusRM, n.GetProperty("status"), true),
|
||||
render(typeRM, n.GetProperty("type"), true),
|
||||
cTitle.Sprint(truncate(n.Title, 80)),
|
||||
cDim.Sprint("["+strings.Join(ns_rel_node_titles, ",")+"]"),
|
||||
dueDateShort(n.DueDate),
|
||||
)
|
||||
tags := n.GetDisplayTags()
|
||||
if len(tags) > 0 {
|
||||
@@ -117,7 +120,7 @@ func PrintNode(w io.Writer, svc service.NodeService, n *models.Node, jsonOut boo
|
||||
fmt.Fprintf(w, " Status: %s\n", render(statusRM, n.GetProperty("status"), false))
|
||||
fmt.Fprintf(w, " Priority: %s\n", render(prioRM, n.GetProperty("prio"), false))
|
||||
if n.DueDate != nil {
|
||||
fmt.Fprintf(w, " Due: %s %s\n", iconCalendar, n.DueDate.Format("2006-01-02"))
|
||||
fmt.Fprintf(w, " Due: %s\n", dueDateLong(n.DueDate))
|
||||
}
|
||||
fmt.Fprintf(w, " Created: %s\n", cDim.Sprint(n.CreatedAt))
|
||||
fmt.Fprintf(w, " Updated: %s\n", cDim.Sprint(n.UpdatedAt))
|
||||
@@ -202,6 +205,57 @@ func render(rm RenderMap, key string, short bool) string {
|
||||
return v.c.Sprint(v.l)
|
||||
}
|
||||
|
||||
// dueDateShort returns a "+12d" / "-3d" style string colored by urgency.
|
||||
func dueDateShort(d *models.Date) string {
|
||||
if d == nil {
|
||||
return ""
|
||||
}
|
||||
now := time.Now().UTC().Truncate(24 * time.Hour)
|
||||
due := d.UTC().Truncate(24 * time.Hour)
|
||||
days := int(math.Round(due.Sub(now).Hours() / 24))
|
||||
|
||||
var label string
|
||||
if days == 0 {
|
||||
label = "today"
|
||||
} else if days > 0 {
|
||||
label = fmt.Sprintf("+%dd", days)
|
||||
} else {
|
||||
label = fmt.Sprintf("%dd", days)
|
||||
}
|
||||
|
||||
var c *color.Color
|
||||
switch {
|
||||
case days < 0:
|
||||
c = cBad
|
||||
case days <= 3:
|
||||
c = cWarn
|
||||
default:
|
||||
c = cGood
|
||||
}
|
||||
return c.Sprint(label)
|
||||
}
|
||||
|
||||
// dueDateLong returns the full date string colored by urgency.
|
||||
func dueDateLong(d *models.Date) string {
|
||||
if d == nil {
|
||||
return ""
|
||||
}
|
||||
now := time.Now().UTC().Truncate(24 * time.Hour)
|
||||
due := d.UTC().Truncate(24 * time.Hour)
|
||||
days := int(math.Round(due.Sub(now).Hours() / 24))
|
||||
|
||||
var c *color.Color
|
||||
switch {
|
||||
case days < 0:
|
||||
c = cBad
|
||||
case days <= 3:
|
||||
c = cWarn
|
||||
default:
|
||||
c = cGood
|
||||
}
|
||||
return c.Sprintf("%s %s", iconCalendar, d.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
func truncate(s string, max int) string {
|
||||
if len(s) <= max {
|
||||
return s
|
||||
|
||||
Reference in New Issue
Block a user