Lien Gantt

This commit is contained in:
2026-05-30 08:41:37 +02:00
parent 16a39ca5e7
commit 97bd9ed100
3 changed files with 52 additions and 5 deletions
+14 -1
View File
@@ -324,15 +324,28 @@ export class IssueDetail {
});
}
for (const child of this.composedIssues) {
const taskIds = new Set(tasks.map((t) => t.id));
const sortedChildren = [...this.composedIssues].sort((a, b) => {
if (!a.startDate && !b.startDate) return 0;
if (!a.startDate) return 1;
if (!b.startDate) return -1;
return a.startDate.localeCompare(b.startDate);
});
for (const child of sortedChildren) {
if (!child.startDate || !child.endDate) continue;
const deps = child.dependsOnIds
.map((id) => `issue-${id}`)
.filter((id) => taskIds.has(id))
.join(',');
tasks.push({
id: `issue-${child.id}`,
name: `#${child.id} ${child.name}`,
start: child.startDate,
end: child.endDate,
progress: child.progress,
...(deps ? { dependencies: deps } : {}),
});
taskIds.add(`issue-${child.id}`);
}
return tasks;
@@ -140,15 +140,28 @@ export class MilestoneDetail {
protected get milestoneGanttTasks(): GanttTask[] {
const tasks: GanttTask[] = [];
for (const issue of this.linkedIssues) {
const taskIds = new Set<string>();
const sorted = [...this.linkedIssues].sort((a, b) => {
if (!a.startDate && !b.startDate) return 0;
if (!a.startDate) return 1;
if (!b.startDate) return -1;
return a.startDate.localeCompare(b.startDate);
});
for (const issue of sorted) {
if (!issue.startDate || !issue.endDate) continue;
const deps = issue.dependsOnIds
.map((id) => `issue-${id}`)
.filter((id) => taskIds.has(id))
.join(',');
tasks.push({
id: `issue-${issue.id}`,
name: `#${issue.id} ${issue.name}`,
start: issue.startDate,
end: issue.endDate,
progress: issue.progress,
...(deps ? { dependencies: deps } : {}),
});
taskIds.add(`issue-${issue.id}`);
}
return tasks;
}
+24 -3
View File
@@ -28,8 +28,16 @@ export class Milestones {
protected get ganttTasks(): GanttTask[] {
const today = new Date().toISOString().slice(0, 10);
const tasks: GanttTask[] = [];
const taskIds = new Set<string>();
for (const milestone of this.milestones()) {
const sorted = [...this.milestones()].sort((a, b) => {
if (!a.startDate && !b.startDate) return 0;
if (!a.startDate) return 1;
if (!b.startDate) return -1;
return a.startDate.localeCompare(b.startDate);
});
for (const milestone of sorted) {
const end = milestone.endDate || milestone.dueDate;
if (!end) continue;
@@ -45,13 +53,19 @@ export class Milestones {
}
const clampedEnd = end < start ? start : end;
const deps = milestone.dependsOnIds
.map((id) => `milestone-${id}`)
.filter((id) => taskIds.has(id))
.join(',');
tasks.push({
id: `milestone-${milestone.id}`,
name: milestone.name,
start,
end: clampedEnd,
progress: this.getProgress(milestone),
...(deps ? { dependencies: deps } : {}),
});
taskIds.add(`milestone-${milestone.id}`);
}
return tasks;
@@ -59,8 +73,15 @@ export class Milestones {
protected get filteredMilestones(): MilestoneEntity[] {
const q = this.searchQuery.trim().toLowerCase();
if (!q) return this.milestones();
return this.milestones().filter((m) => m.name.toLowerCase().includes(q));
const list = q
? this.milestones().filter((m) => m.name.toLowerCase().includes(q))
: this.milestones();
return [...list].sort((a, b) => {
if (!a.startDate && !b.startDate) return 0;
if (!a.startDate) return 1;
if (!b.startDate) return -1;
return a.startDate.localeCompare(b.startDate);
});
}
protected getProgress(milestone: MilestoneEntity): number {