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'] {