generated from coulomb/repo-seed
feat(tasks): adopt canonical task statuses
This commit is contained in:
@@ -50,7 +50,7 @@ const _ts = taskState.ts;
|
||||
```js
|
||||
import {MultiSelect} from "./components/multiselect.js";
|
||||
|
||||
const STATUSES = ["todo", "in_progress", "blocked", "done", "cancelled"];
|
||||
const STATUSES = ["wait", "todo", "progress", "done", "cancel"];
|
||||
const PRIORITIES = ["critical", "high", "medium", "low"];
|
||||
const _domainsResp = await fetch(`${API}/domains/?status=active`).catch(() => null);
|
||||
const DOMAINS = _domainsResp?.ok
|
||||
@@ -95,11 +95,11 @@ import {openEntityModal, buildEntityTable} from "./components/entity-modal.js";
|
||||
import {statusControl, TASK_STATUSES} from "./components/status-control.js";
|
||||
|
||||
// ── KPI sidebar card ─────────────────────────────────────────────────────────
|
||||
const _open = data.filter(t => ["todo", "in_progress", "blocked"].includes(t.status));
|
||||
const _blocked = data.filter(t => t.status === "blocked");
|
||||
const _inProg = data.filter(t => t.status === "in_progress");
|
||||
const _open = data.filter(t => ["wait", "todo", "progress"].includes(t.status));
|
||||
const _waiting = data.filter(t => t.status === "wait");
|
||||
const _inProg = data.filter(t => t.status === "progress");
|
||||
const _done = data.filter(t => t.status === "done");
|
||||
const _total = data.filter(t => t.status !== "cancelled").length;
|
||||
const _total = data.filter(t => t.status !== "cancel").length;
|
||||
const _donePct = _total > 0 ? Math.round(_done.length / _total * 100) : 0;
|
||||
|
||||
const _kpiBox = html`<div class="kpi-infobox">
|
||||
@@ -111,13 +111,13 @@ const _kpiBox = html`<div class="kpi-infobox">
|
||||
</div>
|
||||
</div>
|
||||
<div class="kpi-row">
|
||||
<span class="kpi-row-label">blocked</span>
|
||||
<span class="kpi-row-label">waiting</span>
|
||||
<div class="kpi-row-right">
|
||||
<div class="kpi-row-value" style="color:${_blocked.length > 0 ? '#dc2626' : 'inherit'}">${_blocked.length}</div>
|
||||
<div class="kpi-row-value" style="color:${_waiting.length > 0 ? '#d97706' : 'inherit'}">${_waiting.length}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="kpi-row">
|
||||
<span class="kpi-row-label">in progress</span>
|
||||
<span class="kpi-row-label">progress</span>
|
||||
<div class="kpi-row-right">
|
||||
<div class="kpi-row-value">${_inProg.length}</div>
|
||||
</div>
|
||||
@@ -154,11 +154,11 @@ injectTocTop("live-indicator", _liveEl);
|
||||
import * as Plot from "npm:@observablehq/plot";
|
||||
|
||||
const STATUS_COLOR = {
|
||||
wait: "#f59e0b",
|
||||
todo: "#94a3b8",
|
||||
in_progress: "#3b82f6",
|
||||
blocked: "#ef4444",
|
||||
progress: "#8b5cf6",
|
||||
done: "#22c55e",
|
||||
cancelled: "#cbd5e1",
|
||||
cancel: "#cbd5e1",
|
||||
};
|
||||
|
||||
const byStatus = STATUSES
|
||||
@@ -178,16 +178,16 @@ display(byStatus.length === 0
|
||||
);
|
||||
```
|
||||
|
||||
## Blocked Tasks
|
||||
## Waiting Tasks
|
||||
|
||||
```js
|
||||
const _blockedInFilter = filtered.filter(t => t.status === "blocked");
|
||||
const _waitingInFilter = filtered.filter(t => t.status === "wait");
|
||||
|
||||
if (_blockedInFilter.length === 0) {
|
||||
display(html`<p class="dim">No blocked tasks in current filter. ✓</p>`);
|
||||
if (_waitingInFilter.length === 0) {
|
||||
display(html`<p class="dim">No waiting tasks in current filter. ✓</p>`);
|
||||
} else {
|
||||
display(html`<div class="task-blocked-list">${_blockedInFilter.map(t => html`
|
||||
<div class="task-blocked-item entity-row" onclick=${() => openEntityModal(t, "task")}>
|
||||
display(html`<div class="task-waiting-list">${_waitingInFilter.map(t => html`
|
||||
<div class="task-waiting-item entity-row" onclick=${() => openEntityModal(t, "task")}>
|
||||
<div class="task-item-header">
|
||||
<span class="task-badge task-priority-${t.priority}">${t.priority}</span>
|
||||
<span class="task-context">${t.domain}</span>
|
||||
@@ -196,7 +196,7 @@ if (_blockedInFilter.length === 0) {
|
||||
${t.assignee ? html`<span class="task-assignee">@${t.assignee}</span>` : ""}
|
||||
</div>
|
||||
<div class="task-title">${t.title}</div>
|
||||
${t.blocking_reason ? html`<div class="task-blocking-reason">⊘ ${t.blocking_reason}</div>` : ""}
|
||||
${t.blocking_reason ? html`<div class="task-wait-reason">⊘ ${t.blocking_reason}</div>` : ""}
|
||||
</div>
|
||||
`)}</div>`);
|
||||
}
|
||||
@@ -209,7 +209,7 @@ display(_filtersForm);
|
||||
display(html`<p><strong>${filtered.length}</strong> tasks shown.</p>`);
|
||||
|
||||
const PRIORITY_ORDER = {critical: 0, high: 1, medium: 2, low: 3};
|
||||
const STATUS_ORDER = {blocked: 0, in_progress: 1, todo: 2, done: 3, cancelled: 4};
|
||||
const STATUS_ORDER = {wait: 0, progress: 1, todo: 2, done: 3, cancel: 4};
|
||||
|
||||
const sorted = [...filtered].sort((a, b) => {
|
||||
const sd = (STATUS_ORDER[a.status] ?? 9) - (STATUS_ORDER[b.status] ?? 9);
|
||||
@@ -246,9 +246,9 @@ display(buildEntityTable(
|
||||
|
||||
/* ── Filters ──────────────────────────────────────────────────────────────── */
|
||||
|
||||
/* ── Blocked task cards ───────────────────────────────────────────────────── */
|
||||
.task-blocked-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.task-blocked-item { border-left: 3px solid #ef4444; border-radius: 0 6px 6px 0; background: var(--theme-background-alt); padding: 0.65rem 0.9rem; }
|
||||
/* ── Waiting task cards ───────────────────────────────────────────────────── */
|
||||
.task-waiting-list { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.task-waiting-item { border-left: 3px solid #f59e0b; border-radius: 0 6px 6px 0; background: var(--theme-background-alt); padding: 0.65rem 0.9rem; }
|
||||
.task-item-header { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-bottom: 0.3rem; font-size: 0.75rem; }
|
||||
.task-badge { display: inline-block; padding: 0.1rem 0.45rem; border-radius: 10px; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.task-priority-critical { background: #fee2e2; color: #991b1b; }
|
||||
@@ -260,7 +260,7 @@ display(buildEntityTable(
|
||||
.task-due { color: #dc2626; font-weight: 600; }
|
||||
.task-assignee { color: var(--theme-foreground-muted, #888); }
|
||||
.task-title { font-weight: 600; font-size: 0.95rem; margin-bottom: 0.15rem; }
|
||||
.task-blocking-reason { font-size: 0.8rem; color: #b45309; background: #fef3c7; border-radius: 4px; padding: 0.2rem 0.5rem; margin-top: 0.25rem; }
|
||||
.task-wait-reason { font-size: 0.8rem; color: #b45309; background: #fef3c7; border-radius: 4px; padding: 0.2rem 0.5rem; margin-top: 0.25rem; }
|
||||
|
||||
/* ── Utility ──────────────────────────────────────────────────────────────── */
|
||||
.dim { color: gray; font-style: italic; }
|
||||
|
||||
Reference in New Issue
Block a user