feat: 展示 TaskTree 分组完成度

This commit is contained in:
root
2026-07-21 15:24:51 +02:00
parent 769921f313
commit 787752bacd
5 changed files with 43 additions and 9 deletions
+1
View File
@@ -54,6 +54,7 @@ export type TaskGroupOverview = {
taskCount: number;
subtaskCount: number;
subsubtaskCount: number;
completedCount: number;
reportCount: number;
startAt: string | null;
dueAt: string | null;
+2 -1
View File
@@ -145,6 +145,7 @@ export class TaskTreeStore {
COUNT(DISTINCT t.id) FILTER (WHERE t.kind='task')::int AS task_count,
COUNT(DISTINCT t.id) FILTER (WHERE t.kind='subtask')::int AS subtask_count,
COUNT(DISTINCT t.id) FILTER (WHERE t.kind='subsubtask')::int AS subsubtask_count,
COUNT(DISTINCT t.id) FILTER (WHERE t.status='completed')::int AS completed_count,
COUNT(DISTINCT r.id)::int AS report_count,
MIN(CASE
WHEN t.id IS NULL THEN NULL
@@ -696,7 +697,7 @@ async function schemaIsCurrent(pool: pg.Pool): Promise<boolean> {
}
function groupRow(row: any): TaskGroup { return { id: row.id, name: row.name, description: row.description, createdAt: iso(row.created_at), updatedAt: iso(row.updated_at) }; }
function overviewRow(row: any): TaskGroupOverview { return { group: groupRow(row), taskCount: Number(row.task_count), subtaskCount: Number(row.subtask_count), subsubtaskCount: Number(row.subsubtask_count), reportCount: Number(row.report_count), startAt: nullableIso(row.start_at), dueAt: nullableIso(row.due_at) }; }
function overviewRow(row: any): TaskGroupOverview { return { group: groupRow(row), taskCount: Number(row.task_count), subtaskCount: Number(row.subtask_count), subsubtaskCount: Number(row.subsubtask_count), completedCount: Number(row.completed_count), reportCount: Number(row.report_count), startAt: nullableIso(row.start_at), dueAt: nullableIso(row.due_at) }; }
function taskRow(row: any): TaskItem { return { id: row.id, groupId: row.group_id, parentId: row.parent_id, kind: row.kind, title: row.title, description: row.description, status: row.status, startAt: nullableIso(row.start_at), dueAt: nullableIso(row.due_at), sortOrder: row.sort_order, createdAt: iso(row.created_at), updatedAt: iso(row.updated_at) }; }
function milestoneRow(row: any): Milestone { return { id: row.id, groupId: row.group_id, taskId: row.task_id, title: row.title, occursAt: iso(row.occurs_at), createdAt: iso(row.created_at) }; }
function reportRow(row: any): ExecutionReport { return { id: row.id, taskId: row.task_id, title: row.title, body: row.body, status: row.status, createdAt: iso(row.created_at) }; }
+1
View File
@@ -217,6 +217,7 @@ test("native PostgreSQL store exposes overview and three task levels", async ()
assert.equal(overview?.taskCount, 1);
assert.equal(overview?.subtaskCount, 3);
assert.equal(overview?.subsubtaskCount, 1);
assert.equal(overview?.completedCount, 1);
assert.equal(overview?.reportCount, 1);
assert.equal(overview?.startAt, new Date(timelineStart).toISOString());
assert.equal(overview?.dueAt, new Date(timelineDue).toISOString());