Skip to content

Commit dccaea6

Browse files
committed
SF-3486 Hide completed drafts when content is not available
1 parent c3389f3 commit dccaea6

File tree

4 files changed

+87
-17
lines changed

4 files changed

+87
-17
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-list.component.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@ <h2>{{ lastCompletedBuildMessage }}</h2>
88
/>
99
}
1010

11-
@if (historicalBuilds.length > 0) {
11+
@if (savedHistoricalBuilds.length > 0) {
1212
<h2>{{ t("previously_generated_drafts") }}</h2>
13-
@for (entry of historicalBuilds; track entry.id) {
13+
@for (entry of savedHistoricalBuilds; track entry.id) {
1414
<app-draft-history-entry [entry]="entry" />
1515
}
1616
}
17+
@if (nonActiveBuilds.length > 0) {
18+
<app-notice [icon]="'summarize'" class="older-drafts">{{
19+
t("older_drafts_not_available", { date: draftHistoryCutoffDate })
20+
}}</app-notice>
21+
}
1722
</ng-container>

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-list.component.spec.ts

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('DraftHistoryListComponent', () => {
4747
it('should handle a missing build history', () => {
4848
const env = new TestEnvironment(undefined);
4949
expect(env.component.history).toEqual([]);
50-
expect(env.component.historicalBuilds).toEqual([]);
50+
expect(env.component.savedHistoricalBuilds).toEqual([]);
5151
expect(env.component.isBuildActive).toBe(false);
5252
expect(env.component.latestBuild).toBeUndefined();
5353
expect(env.component.lastCompletedBuildMessage).toBe('');
@@ -57,7 +57,7 @@ describe('DraftHistoryListComponent', () => {
5757
it('should handle an empty build history', () => {
5858
const env = new TestEnvironment([]);
5959
expect(env.component.history).toEqual([]);
60-
expect(env.component.historicalBuilds).toEqual([]);
60+
expect(env.component.savedHistoricalBuilds).toEqual([]);
6161
expect(env.component.isBuildActive).toBe(false);
6262
expect(env.component.latestBuild).toBeUndefined();
6363
expect(env.component.lastCompletedBuildMessage).toBe('');
@@ -66,10 +66,13 @@ describe('DraftHistoryListComponent', () => {
6666

6767
it('should handle completed and active builds', fakeAsync(() => {
6868
const activeBuild = { state: BuildStates.Active } as BuildDto;
69-
const completedBuild = { state: BuildStates.Completed } as BuildDto;
69+
const completedBuild = {
70+
state: BuildStates.Completed,
71+
additionalInfo: { dateGenerated: '2025-08-01T12:00:00.000Z' }
72+
} as BuildDto;
7073
const env = new TestEnvironment([completedBuild, activeBuild]);
7174
expect(env.component.history).toEqual([activeBuild, completedBuild]);
72-
expect(env.component.historicalBuilds).toEqual([completedBuild]);
75+
expect(env.component.savedHistoricalBuilds).toEqual([completedBuild]);
7376
expect(env.component.isBuildActive).toBe(true);
7477
expect(env.component.latestBuild).toBeUndefined();
7578
expect(env.component.lastCompletedBuildMessage).toBe('');
@@ -80,7 +83,7 @@ describe('DraftHistoryListComponent', () => {
8083
const buildHistory = [{ state: BuildStates.Active } as BuildDto];
8184
const env = new TestEnvironment(buildHistory);
8285
expect(env.component.history).toEqual(buildHistory);
83-
expect(env.component.historicalBuilds).toEqual([]);
86+
expect(env.component.savedHistoricalBuilds).toEqual([]);
8487
expect(env.component.isBuildActive).toBe(true);
8588
expect(env.component.latestBuild).toBeUndefined();
8689
expect(env.component.lastCompletedBuildMessage).toBe('');
@@ -92,19 +95,22 @@ describe('DraftHistoryListComponent', () => {
9295
const buildHistory = [build];
9396
const env = new TestEnvironment(buildHistory);
9497
expect(env.component.history).toEqual(buildHistory);
95-
expect(env.component.historicalBuilds).toEqual([]);
98+
expect(env.component.savedHistoricalBuilds).toEqual([]);
9699
expect(env.component.isBuildActive).toBe(false);
97100
expect(env.component.latestBuild).toBe(build);
98101
expect(env.component.lastCompletedBuildMessage).not.toBe('');
99102
expect(env.component.nonActiveBuilds).toEqual(buildHistory);
100103
}));
101104

102105
it('should handle just one completed build', fakeAsync(() => {
103-
const build = { state: BuildStates.Completed } as BuildDto;
106+
const build = {
107+
state: BuildStates.Completed,
108+
additionalInfo: { dateGenerated: '2025-08-01T12:00:00.000Z' }
109+
} as BuildDto;
104110
const buildHistory = [build];
105111
const env = new TestEnvironment(buildHistory);
106112
expect(env.component.history).toEqual(buildHistory);
107-
expect(env.component.historicalBuilds).toEqual([]);
113+
expect(env.component.savedHistoricalBuilds).toEqual([]);
108114
expect(env.component.isBuildActive).toBe(false);
109115
expect(env.component.latestBuild).toBe(build);
110116
expect(env.component.lastCompletedBuildMessage).not.toBe('');
@@ -116,7 +122,7 @@ describe('DraftHistoryListComponent', () => {
116122
const buildHistory = [build];
117123
const env = new TestEnvironment(buildHistory);
118124
expect(env.component.history).toEqual(buildHistory);
119-
expect(env.component.historicalBuilds).toEqual([]);
125+
expect(env.component.savedHistoricalBuilds).toEqual([]);
120126
expect(env.component.isBuildActive).toBe(false);
121127
expect(env.component.latestBuild).toBe(build);
122128
expect(env.component.lastCompletedBuildMessage).not.toBe('');
@@ -139,6 +145,49 @@ describe('DraftHistoryListComponent', () => {
139145
expect(env.component.history).toEqual([newBuild, build]);
140146
}));
141147

148+
it('should filter draft history and hide builds that are not saved to the database', fakeAsync(() => {
149+
const build = {
150+
state: BuildStates.Completed,
151+
additionalInfo: { dateGenerated: '2025-08-01T12:00:00.000Z' }
152+
} as BuildDto;
153+
const olderBuild = {
154+
state: BuildStates.Completed
155+
} as BuildDto;
156+
const buildHistory = [olderBuild, build];
157+
const env = new TestEnvironment(buildHistory);
158+
expect(env.component.history).toEqual(buildHistory);
159+
expect(env.component.savedHistoricalBuilds).toEqual([]);
160+
expect(env.component.isBuildActive).toBe(false);
161+
expect(env.component.latestBuild).toBe(build);
162+
expect(env.component.lastCompletedBuildMessage).not.toBe('');
163+
expect(env.component.nonActiveBuilds).toEqual(buildHistory);
164+
expect(env.olderDraftsMessage).not.toBeNull();
165+
}));
166+
167+
it('should show history with faulted and canceled builds', fakeAsync(() => {
168+
const build = {
169+
id: 'completed',
170+
state: BuildStates.Completed,
171+
additionalInfo: { dateGenerated: '2025-08-01T12:00:00.000Z' }
172+
} as BuildDto;
173+
const canceled = {
174+
id: 'canceled',
175+
state: BuildStates.Canceled
176+
} as BuildDto;
177+
const faulted = {
178+
id: 'faulted',
179+
state: BuildStates.Faulted
180+
} as BuildDto;
181+
const buildHistory = [faulted, canceled, build];
182+
const env = new TestEnvironment(buildHistory);
183+
expect(env.component.history).toEqual(buildHistory);
184+
expect(env.component.savedHistoricalBuilds).toEqual([canceled, faulted]);
185+
expect(env.component.isBuildActive).toBe(false);
186+
expect(env.component.latestBuild).toBe(build);
187+
expect(env.component.lastCompletedBuildMessage).not.toBe('');
188+
expect(env.component.nonActiveBuilds).toEqual(buildHistory);
189+
}));
190+
142191
class TestEnvironment {
143192
component: DraftHistoryListComponent;
144193
fixture: ComponentFixture<DraftHistoryListComponent>;
@@ -153,5 +202,9 @@ describe('DraftHistoryListComponent', () => {
153202
this.component = this.fixture.componentInstance;
154203
this.fixture.detectChanges();
155204
}
205+
206+
get olderDraftsMessage(): HTMLElement {
207+
return this.fixture.nativeElement.querySelector('.older-drafts');
208+
}
156209
}
157210
});

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-history-list/draft-history-list.component.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { Component, DestroyRef } from '@angular/core';
22
import { MatIconModule } from '@angular/material/icon';
33
import { TranslocoModule, TranslocoService } from '@ngneat/transloco';
44
import { take } from 'rxjs';
5+
import { NoticeComponent } from 'src/app/shared/notice/notice.component';
56
import { ActivatedProjectService } from 'xforge-common/activated-project.service';
67
import { filterNullish, quietTakeUntilDestroyed } from 'xforge-common/util/rxjs-util';
8+
import { I18nService } from '../../../../xforge-common/i18n.service';
79
import { ProjectNotificationService } from '../../../core/project-notification.service';
810
import { BuildDto } from '../../../machine-api/build-dto';
911
import { BuildStates } from '../../../machine-api/build-states';
@@ -14,19 +16,21 @@ import { DraftHistoryEntryComponent } from './draft-history-entry/draft-history-
1416
@Component({
1517
selector: 'app-draft-history-list',
1618
standalone: true,
17-
imports: [MatIconModule, DraftHistoryEntryComponent, TranslocoModule],
19+
imports: [MatIconModule, DraftHistoryEntryComponent, TranslocoModule, NoticeComponent],
1820
templateUrl: './draft-history-list.component.html',
1921
styleUrl: './draft-history-list.component.scss'
2022
})
2123
export class DraftHistoryListComponent {
2224
history: BuildDto[] = [];
25+
readonly draftHistoryCutoffDate: string = this.i18n.formatDate(new Date('2024-12-03T12:00:00.000Z'));
2326

2427
constructor(
2528
activatedProject: ActivatedProjectService,
2629
private destroyRef: DestroyRef,
2730
private readonly draftGenerationService: DraftGenerationService,
2831
projectNotificationService: ProjectNotificationService,
29-
private readonly transloco: TranslocoService
32+
private readonly transloco: TranslocoService,
33+
private readonly i18n: I18nService
3034
) {
3135
activatedProject.projectId$
3236
.pipe(quietTakeUntilDestroyed(destroyRef), filterNullish(), take(1))
@@ -75,14 +79,21 @@ export class DraftHistoryListComponent {
7579
}
7680
}
7781

78-
get historicalBuilds(): BuildDto[] {
79-
return this.latestBuild == null ? this.nonActiveBuilds : this.nonActiveBuilds.slice(1);
80-
}
81-
8282
get isBuildActive(): boolean {
8383
return this.history.some(entry => activeBuildStates.includes(entry.state)) ?? false;
8484
}
8585

86+
get savedHistoricalBuilds(): BuildDto[] {
87+
// The date generated is available if the draft has been stored in the realtime database.
88+
return this.historicalBuilds.filter(
89+
entry => entry.state !== BuildStates.Completed || entry.additionalInfo?.dateGenerated != null
90+
);
91+
}
92+
93+
private get historicalBuilds(): BuildDto[] {
94+
return this.latestBuild == null ? this.nonActiveBuilds : this.nonActiveBuilds.slice(1);
95+
}
96+
8697
loadHistory(projectId: string): void {
8798
this.draftGenerationService
8899
.getBuildHistory(projectId)

src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@
284284
"draft_canceled": "The draft was canceled",
285285
"draft_completed": "The draft is ready",
286286
"draft_faulted": "The draft failed",
287+
"older_drafts_not_available": "Older drafts generated before {{ date }} are not available.",
287288
"previously_generated_drafts": "Previously generated drafts"
288289
},
289290
"draft_preview_books": {

0 commit comments

Comments
 (0)