Detailed changes
@@ -58,7 +58,9 @@ pub fn run(root: &Path, opts: Opts) -> Result<()> {
let use_color = stdout_use_color();
let mut table = Table::new();
table.load_preset(NOTHING);
- table.set_header(vec!["ID", "STATUS", "TYPE", "PRIORITY", "EFFORT", "TITLE"]);
+ table.set_header(vec![
+ "ID", "STATUS", "TYPE", "PRIORITY", "EFFORT", "LABELS", "TITLE",
+ ]);
for t in &tasks {
table.add_row(vec![
cell_bold(&t.id, use_color),
@@ -66,6 +68,7 @@ pub fn run(root: &Path, opts: Opts) -> Result<()> {
Cell::new(&t.task_type),
cell_priority(t.priority.as_str(), t.priority, use_color),
cell_effort(t.effort.as_str(), t.effort, use_color),
+ Cell::new(t.labels.join(", ")),
Cell::new(&t.title),
]);
}
@@ -1,7 +1,7 @@
use anyhow::{bail, Result};
use comfy_table::presets::NOTHING;
use comfy_table::{Cell, Table};
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
use std::path::Path;
use crate::color::{cell_bold, stdout_use_color};
@@ -60,6 +60,12 @@ pub fn run(root: &Path, mode_str: &str, verbose: bool, limit: usize, json: bool)
limit,
);
+ // Build a lookup from task ID to labels for display.
+ let labels_by_id: HashMap<&str, &[String]> = all
+ .iter()
+ .map(|t| (t.id.as_str(), t.labels.as_slice()))
+ .collect();
+
if scored.is_empty() {
if json {
println!("[]");
@@ -94,14 +100,19 @@ pub fn run(root: &Path, mode_str: &str, verbose: bool, limit: usize, json: bool)
let use_color = stdout_use_color();
let mut table = Table::new();
table.load_preset(NOTHING);
- table.set_header(vec!["#", "ID", "SCORE", "TITLE"]);
+ table.set_header(vec!["#", "ID", "SCORE", "LABELS", "TITLE"]);
for (i, s) in scored.iter().enumerate() {
let short = TaskId::display_id(&s.id);
+ let labels = labels_by_id
+ .get(s.id.as_str())
+ .map(|ls| ls.join(", "))
+ .unwrap_or_default();
table.add_row(vec![
Cell::new(i + 1),
cell_bold(&short, use_color),
Cell::new(format!("{:.2}", s.score)),
+ Cell::new(labels),
Cell::new(&s.title),
]);
}
@@ -284,6 +284,7 @@ fn build_section(
priority: t.priority.as_str().to_string(),
effort: t.effort.as_str().to_string(),
title: t.title.clone(),
+ labels: t.labels.clone(),
created_at_display: friendly_date(&t.created_at),
created_at: t.created_at.clone(),
}
@@ -393,6 +394,12 @@ pub(in crate::cmd::webui) async fn project_handler(
5,
);
+ // Build a lookup from task ID to labels for the Next Up display.
+ let labels_by_id: std::collections::HashMap<&str, &[String]> = tasks
+ .iter()
+ .map(|t| (t.id.as_str(), t.labels.as_slice()))
+ .collect();
+
let next_up: Vec<ScoredEntry> = scored
.into_iter()
.map(|s| {
@@ -415,6 +422,10 @@ pub(in crate::cmd::webui) async fn project_handler(
"Unblocks: {} {} ({} directly)",
s.total_unblocked, task_word, s.direct_unblocked
);
+ let labels = labels_by_id
+ .get(s.id.as_str())
+ .map(|ls| ls.to_vec())
+ .unwrap_or_default();
ScoredEntry {
short_id: TaskId::display_id(&s.id),
id: s.id,
@@ -424,6 +435,7 @@ pub(in crate::cmd::webui) async fn project_handler(
status_display: friendly_status("open"),
equation,
unblocks_display,
+ labels,
}
})
.collect();
@@ -14,6 +14,7 @@ pub(in crate::cmd::webui) struct ScoredEntry {
pub(in crate::cmd::webui) equation: String,
/// Human-friendly unblocks summary for the score tooltip.
pub(in crate::cmd::webui) unblocks_display: String,
+ pub(in crate::cmd::webui) labels: Vec<String>,
}
/// Minimal view-model for a task row in the project task table.
@@ -26,6 +27,7 @@ pub(in crate::cmd::webui) struct TaskRow {
pub(in crate::cmd::webui) priority: String,
pub(in crate::cmd::webui) effort: String,
pub(in crate::cmd::webui) title: String,
+ pub(in crate::cmd::webui) labels: Vec<String>,
pub(in crate::cmd::webui) created_at: String,
pub(in crate::cmd::webui) created_at_display: String,
}
@@ -62,6 +62,7 @@ pub(in crate::cmd::webui) async fn task_handler(
priority: t.priority.as_str().to_string(),
effort: t.effort.as_str().to_string(),
title: t.title.clone(),
+ labels: t.labels.clone(),
created_at_display: friendly_date(&t.created_at),
created_at: t.created_at.clone(),
}
@@ -68,6 +68,7 @@
<th scope="col"><a href="{{ section.sort_ctx.column_href("priority") }}">Priority{{ section.sort_ctx.arrow("priority") }}</a></th>
<th scope="col"><a href="{{ section.sort_ctx.column_href("effort") }}">Effort{{ section.sort_ctx.arrow("effort") }}</a></th>
<th scope="col"><a href="{{ section.sort_ctx.column_href("title") }}">Title{{ section.sort_ctx.arrow("title") }}</a></th>
+ <th scope="col">Labels</th>
<th scope="col"><a href="{{ section.sort_ctx.column_href("created") }}">Created{{ section.sort_ctx.arrow("created") }}</a></th>
<th scope="col">Change status</th>
</tr>
@@ -80,6 +81,7 @@
<td>{{ t.priority }}</td>
<td>{{ t.effort }}</td>
<td>{{ t.title }}</td>
+ <td>{% for l in t.labels %}{% if !loop.first %}, {% endif %}{{ l }}{% endfor %}</td>
<td><time datetime="{{ t.created_at }}">{{ t.created_at_display }}</time></td>
<td>
<ot-dropdown>
@@ -74,6 +74,7 @@
<th scope="col">ID</th>
<th scope="col">Score</th>
<th scope="col">Title</th>
+ <th scope="col">Labels</th>
<th scope="col">Change status</th>
</tr>
</thead>
@@ -84,6 +85,7 @@
<td><a href="/projects/{{ project_name }}/tasks/{{ s.id }}"><code>{{ s.short_id }}</code></a></td>
<td title="{{ s.equation }} {{ s.unblocks_display }}">{{ s.score }}</td>
<td>{{ s.title }}</td>
+ <td>{% for l in s.labels %}{% if !loop.first %}, {% endif %}{{ l }}{% endfor %}</td>
<td>
<ot-dropdown>
<button popovertarget="next-status-{{ s.short_id }}" class="outline small" aria-label="Change status of {{ s.short_id }}, currently {{ s.status_display }}">
@@ -170,6 +170,7 @@
<th scope="col">Priority</th>
<th scope="col">Effort</th>
<th scope="col">Title</th>
+ <th scope="col">Labels</th>
<th scope="col">Created</th>
</tr>
</thead>
@@ -182,6 +183,7 @@
<td>{{ t.priority }}</td>
<td>{{ t.effort }}</td>
<td>{{ t.title }}</td>
+ <td>{% for l in t.labels %}{% if !loop.first %}, {% endif %}{{ l }}{% endfor %}</td>
<td><time datetime="{{ t.created_at }}">{{ t.created_at_display }}</time></td>
</tr>
{% endfor %}