Calcule du temps estimé des Milestone et Epic

This commit is contained in:
2026-05-30 06:59:54 +02:00
parent e81d465903
commit b1a114aaa8
6 changed files with 119 additions and 1 deletions
@@ -186,7 +186,11 @@
<div class="row g-2">
<div [class]="isEpicIssue ? 'col-12' : 'col-6'">
<label class="field-label">Temps estimé (h)</label>
<input aria-label="Temps estimé" class="form-control form-control-sm" type="number" min="0" step="0.5" [(ngModel)]="estimatedTimeValue" (blur)="saveIssue()" />
@if (isEpicIssue) {
<div class="form-control form-control-sm bg-body-secondary text-secondary">{{ epicEstimatedTime !== null ? epicEstimatedTime : '—' }}</div>
} @else {
<input aria-label="Temps estimé" class="form-control form-control-sm" type="number" min="0" step="0.5" [(ngModel)]="estimatedTimeValue" (blur)="saveIssue()" />
}
</div>
@if (!isEpicIssue) {
<div class="col-6">
@@ -580,6 +580,39 @@ describe('IssueDetail — existing issue', () => {
});
});
describe('epicEstimatedTime', () => {
beforeEach(() => {
(component as any).issue.type = 'Epic';
(component as any).issue.name = 'Test Epic';
});
it('returns null when there are no child issues', () => {
expect((component as any).epicEstimatedTime).toBeNull();
});
it('returns null when all children have null estimatedTime', () => {
store.upsert(makeIssue({ id: 200, epic: 'Test Epic', estimatedTime: null }));
expect((component as any).epicEstimatedTime).toBeNull();
});
it('returns the sum of children estimatedTime', () => {
store.upsert(makeIssue({ id: 200, epic: 'Test Epic', estimatedTime: 8 }));
store.upsert(makeIssue({ id: 201, epic: 'Test Epic', estimatedTime: 4 }));
expect((component as any).epicEstimatedTime).toBe(12);
});
it('ignores children with null estimatedTime in the sum', () => {
store.upsert(makeIssue({ id: 200, epic: 'Test Epic', estimatedTime: 8 }));
store.upsert(makeIssue({ id: 201, epic: 'Test Epic', estimatedTime: null }));
expect((component as any).epicEstimatedTime).toBe(8);
});
it('includes children linked via dependsOnIds', () => {
store.upsert(makeIssue({ id: 200, dependsOnIds: [1], estimatedTime: 6 }));
expect((component as any).epicEstimatedTime).toBe(6);
});
});
describe('create-in-epic flow', () => {
beforeEach(() => {
(component as any).issue.type = 'Epic';
@@ -150,6 +150,13 @@ export class IssueDetail {
this.issue.type = value;
}
protected get epicEstimatedTime(): number | null {
const times = this.composedIssues
.filter((i): i is IssueEntity & { estimatedTime: number } => i.estimatedTime !== null)
.map((i) => i.estimatedTime);
return times.length === 0 ? null : times.reduce((a, b) => a + b, 0);
}
protected get epicIssues(): IssueEntity[] {
return this.issues().filter((issue) => issue.type === 'Epic');
}