Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/utils/tasktree-time-range.ts
T
2026-07-21 15:49:48 +02:00

48 lines
1.9 KiB
TypeScript

// SPEC: PJ2026-010404 project management draft-2026-06-25-p0-mdtodo-web-active-editing-hwpod-source.
// 职责:根据节点自身及全部后代推导只用于展示的 TaskTree 父级时间范围。
export type TaskTreeTimedNode = {
id: string;
parentId: string | null;
startAt: string | null;
dueAt: string | null;
};
export type TaskTreeTimeRange = Pick<TaskTreeTimedNode, "startAt" | "dueAt">;
export function deriveEffectiveTaskTimeRanges(tasks: TaskTreeTimedNode[]): Map<string, TaskTreeTimeRange> {
const tasksById = new Map(tasks.map((task) => [task.id, task]));
const childrenByParent = new Map<string, TaskTreeTimedNode[]>();
for (const task of tasks) {
if (!task.parentId || !tasksById.has(task.parentId)) continue;
childrenByParent.set(task.parentId, [...(childrenByParent.get(task.parentId) ?? []), task]);
}
const ranges = new Map<string, TaskTreeTimeRange>();
const resolving = new Set<string>();
const resolve = (task: TaskTreeTimedNode): TaskTreeTimeRange => {
const cached = ranges.get(task.id);
if (cached) return cached;
if (resolving.has(task.id)) return { startAt: task.startAt, dueAt: task.dueAt };
resolving.add(task.id);
const values = [task.startAt, task.dueAt];
for (const child of childrenByParent.get(task.id) ?? []) {
const childRange = resolve(child);
values.push(childRange.startAt, childRange.dueAt);
}
const timestamps = values
.filter((value): value is string => Boolean(value))
.map((value) => new Date(value).getTime())
.filter(Number.isFinite);
const range = timestamps.length > 0
? { startAt: new Date(Math.min(...timestamps)).toISOString(), dueAt: new Date(Math.max(...timestamps)).toISOString() }
: { startAt: null, dueAt: null };
resolving.delete(task.id);
ranges.set(task.id, range);
return range;
};
for (const task of tasks) resolve(task);
return ranges;
}