diff --git a/src/app/issues/issue-detail/issue-detail.html b/src/app/issues/issue-detail/issue-detail.html index 3b10b40..191a19c 100644 --- a/src/app/issues/issue-detail/issue-detail.html +++ b/src/app/issues/issue-detail/issue-detail.html @@ -162,7 +162,7 @@ [class.is-invalid]="!!dateValidationError" type="date" [(ngModel)]="issue.startDate" - (blur)="saveIssue()" + (blur)="onStartDateBlur()" />
@@ -170,10 +170,9 @@
@if (dateValidationError) { diff --git a/src/app/issues/issue-detail/issue-detail.spec.ts b/src/app/issues/issue-detail/issue-detail.spec.ts index 67e1ef1..b6d00d6 100644 --- a/src/app/issues/issue-detail/issue-detail.spec.ts +++ b/src/app/issues/issue-detail/issue-detail.spec.ts @@ -299,6 +299,64 @@ describe('IssueDetail — existing issue', () => { }); }); + describe('recalculateEndDate (via estimatedTimeValue setter)', () => { + it('sets endDate to startDate when estimatedTime is 8h or less', () => { + (component as any).issue.startDate = '2026-06-01'; + (component as any).estimatedTimeValue = 8; + expect((component as any).issue.endDate).toBe('2026-06-01'); + }); + + it('adds one day for estimatedTime between 9h and 16h', () => { + (component as any).issue.startDate = '2026-06-01'; + (component as any).estimatedTimeValue = 16; + expect((component as any).issue.endDate).toBe('2026-06-02'); + }); + + it('adds two days for estimatedTime between 17h and 24h', () => { + (component as any).issue.startDate = '2026-06-01'; + (component as any).estimatedTimeValue = 24; + expect((component as any).issue.endDate).toBe('2026-06-03'); + }); + + it('clears endDate when startDate is empty', () => { + (component as any).issue.startDate = ''; + (component as any).issue.endDate = '2026-06-05'; + (component as any).estimatedTimeValue = 8; + expect((component as any).issue.endDate).toBe(''); + }); + + it('clears endDate when estimatedTime is null', () => { + (component as any).issue.startDate = '2026-06-01'; + (component as any).issue.endDate = '2026-06-05'; + (component as any).estimatedTimeValue = null; + expect((component as any).issue.endDate).toBe(''); + }); + + it('clears endDate when estimatedTime is 0', () => { + (component as any).issue.startDate = '2026-06-01'; + (component as any).issue.endDate = '2026-06-05'; + (component as any).estimatedTimeValue = 0; + expect((component as any).issue.endDate).toBe(''); + }); + }); + + describe('onStartDateBlur', () => { + it('recalculates endDate when startDate changes', () => { + (component as any).issue.estimatedTime = 16; + (component as any).issue.startDate = '2026-06-10'; + (component as any).onStartDateBlur(); + expect((component as any).issue.endDate).toBe('2026-06-11'); + }); + + it('clears endDate when startDate is cleared', () => { + (component as any).issue.estimatedTime = 16; + (component as any).issue.endDate = '2026-06-11'; + (component as any).issue.startDate = ''; + (component as any).onStartDateBlur(); + expect((component as any).issue.endDate).toBe(''); + }); + }); + describe('deleteIssue', () => { it('removes the issue and navigates to /issues', async () => { const spy = vi.spyOn(router, 'navigate').mockResolvedValue(true); diff --git a/src/app/issues/issue-detail/issue-detail.ts b/src/app/issues/issue-detail/issue-detail.ts index 8626816..1eb35b3 100644 --- a/src/app/issues/issue-detail/issue-detail.ts +++ b/src/app/issues/issue-detail/issue-detail.ts @@ -122,6 +122,24 @@ export class IssueDetail { protected set estimatedTimeValue(value: number | null) { this.issue.estimatedTime = value === null || value === undefined ? null : Number(value); + this.recalculateEndDate(); + } + + private recalculateEndDate(): void { + const { startDate, estimatedTime } = this.issue; + if (!startDate || estimatedTime === null || estimatedTime <= 0) { + this.issue.endDate = ''; + return; + } + const start = new Date(startDate); + const extraDays = Math.max(0, Math.ceil(estimatedTime / 8) - 1); + start.setDate(start.getDate() + extraDays); + this.issue.endDate = start.toISOString().split('T')[0]; + } + + protected onStartDateBlur(): void { + this.recalculateEndDate(); + this.saveIssue(); } protected get issueTypeValue(): IssueEntity['type'] {