From 97bd9ed100e20768cb7c3a2740e9474c77bdb483 Mon Sep 17 00:00:00 2001 From: Gato Date: Sat, 30 May 2026 08:41:37 +0200 Subject: [PATCH] Lien Gantt --- src/app/issues/issue-detail/issue-detail.ts | 15 ++++++++++- .../milestone-detail/milestone-detail.ts | 15 ++++++++++- src/app/milestones/milestones.ts | 27 ++++++++++++++++--- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/app/issues/issue-detail/issue-detail.ts b/src/app/issues/issue-detail/issue-detail.ts index 79ad155..f4cdcca 100644 --- a/src/app/issues/issue-detail/issue-detail.ts +++ b/src/app/issues/issue-detail/issue-detail.ts @@ -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; diff --git a/src/app/milestones/milestone-detail/milestone-detail.ts b/src/app/milestones/milestone-detail/milestone-detail.ts index bcf58d6..39e98f5 100644 --- a/src/app/milestones/milestone-detail/milestone-detail.ts +++ b/src/app/milestones/milestone-detail/milestone-detail.ts @@ -140,15 +140,28 @@ export class MilestoneDetail { protected get milestoneGanttTasks(): GanttTask[] { const tasks: GanttTask[] = []; - for (const issue of this.linkedIssues) { + const taskIds = new Set(); + 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; } diff --git a/src/app/milestones/milestones.ts b/src/app/milestones/milestones.ts index 18f3d8f..836214e 100644 --- a/src/app/milestones/milestones.ts +++ b/src/app/milestones/milestones.ts @@ -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(); - 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 {