Skip to content

Commit 2b8c17d

Browse files
SF-3649 Add UI to filter projects with custom serval config (#3577)
1 parent 0c5e846 commit 2b8c17d

File tree

8 files changed

+73
-3
lines changed

8 files changed

+73
-3
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-projects.component.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<mat-label>Filter projects...</mat-label>
44
<input matInput (keyup)="updateSearchTerm($event.target)" id="project-filter" />
55
</mat-form-field>
6+
<mat-checkbox [(ngModel)]="showProjectsWithCustomServalConfig" (change)="updateServalConfigFilter()">
7+
Projects with custom Serval Configuration only
8+
</mat-checkbox>
69
</div>
710
@if (!isLoading) {
811
@if (length > 0) {

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-projects.component.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@ mat-form-field {
1212
gap: 4px;
1313
white-space: nowrap;
1414
}
15+
16+
.projects-controls {
17+
display: flex;
18+
align-items: center;
19+
justify-content: space-between;
20+
}

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-projects.component.spec.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { HarnessLoader } from '@angular/cdk/testing';
2+
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
13
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
24
import { provideHttpClientTesting } from '@angular/common/http/testing';
35
import { DebugElement, getDebugNode } from '@angular/core';
46
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
7+
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
58
import { By } from '@angular/platform-browser';
69
import { provideNoopAnimations } from '@angular/platform-browser/animations';
710
import { provideRouter } from '@angular/router';
@@ -91,6 +94,23 @@ describe('ServalProjectsComponent', () => {
9194
expect(env.rows.length).toEqual(1);
9295
}));
9396

97+
it('should filter projects with custom serval config set', fakeAsync(async () => {
98+
const env = new TestEnvironment();
99+
env.setupProjectData();
100+
env.fixture.detectChanges();
101+
tick();
102+
env.fixture.detectChanges();
103+
104+
expect(env.rows.length).toEqual(3);
105+
// Toggle the checkbox to filter projects with servalConfig
106+
await env.toggleServalConfigFilter();
107+
// Only project01 has servalConfig set, so only 1 row should be displayed
108+
expect(env.rows.length).toEqual(1);
109+
110+
await env.toggleServalConfigFilter();
111+
expect(env.rows.length).toEqual(3);
112+
}));
113+
94114
it('should page', fakeAsync(() => {
95115
const env = new TestEnvironment();
96116
env.setupProjectData();
@@ -116,6 +136,7 @@ class TestProjectDoc extends ProjectDoc {
116136
class TestEnvironment {
117137
readonly component: ServalProjectsComponent;
118138
readonly fixture: ComponentFixture<ServalProjectsComponent>;
139+
readonly loader: HarnessLoader;
119140

120141
private readonly realtimeService: TestRealtimeService = TestBed.inject<TestRealtimeService>(TestRealtimeService);
121142

@@ -139,6 +160,7 @@ class TestEnvironment {
139160

140161
this.fixture = TestBed.createComponent(ServalProjectsComponent);
141162
this.component = this.fixture.componentInstance;
163+
this.loader = TestbedHarnessEnvironment.loader(this.fixture);
142164
}
143165

144166
get table(): DebugElement {
@@ -181,6 +203,14 @@ class TestEnvironment {
181203
this.fixture.detectChanges();
182204
}
183205

206+
async toggleServalConfigFilter(): Promise<void> {
207+
const checkbox = await this.loader.getHarness(MatCheckboxHarness);
208+
await checkbox.toggle();
209+
this.fixture.detectChanges();
210+
tick();
211+
this.fixture.detectChanges();
212+
}
213+
184214
setupProjectData(): void {
185215
this.realtimeService.addSnapshots<SFProject>(TestProjectDoc.COLLECTION, [
186216
{
@@ -204,7 +234,8 @@ class TestEnvironment {
204234
name: 'Project 04',
205235
shortName: 'P4'
206236
}
207-
]
237+
],
238+
servalConfig: '{ "custom": "value" }'
208239
},
209240
preTranslate: true,
210241
source: {

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-projects.component.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Component, DestroyRef, OnInit } from '@angular/core';
2+
import { FormsModule } from '@angular/forms';
23
import { MatButton } from '@angular/material/button';
4+
import { MatCheckbox } from '@angular/material/checkbox';
35
import { MatFormField, MatLabel } from '@angular/material/form-field';
46
import { MatIcon } from '@angular/material/icon';
57
import { MatInput } from '@angular/material/input';
@@ -83,13 +85,15 @@ class Row {
8385
templateUrl: './serval-projects.component.html',
8486
styleUrls: ['./serval-projects.component.scss'],
8587
imports: [
88+
FormsModule,
8689
MatButton,
8790
MatTable,
8891
MatColumnDef,
8992
MatHeaderCell,
9093
MatHeaderCellDef,
9194
MatCell,
9295
MatCellDef,
96+
MatCheckbox,
9397
MatHeaderRow,
9498
MatHeaderRowDef,
9599
MatIcon,
@@ -109,6 +113,7 @@ export class ServalProjectsComponent extends DataLoadingComponent implements OnI
109113
length: number = 0;
110114
pageIndex: number = 0;
111115
pageSize: number = 50;
116+
showProjectsWithCustomServalConfig: boolean = false;
112117

113118
private projectDocs?: Readonly<SFProjectProfileDoc[]>;
114119

@@ -160,6 +165,11 @@ export class ServalProjectsComponent extends DataLoadingComponent implements OnI
160165
this.queryParameters$.next(this.getQueryParameters());
161166
}
162167

168+
updateServalConfigFilter(): void {
169+
this.pageIndex = 0;
170+
this.queryParameters$.next(this.getQueryParameters());
171+
}
172+
163173
viewDraftJobs(projectId: string): void {
164174
void this.router.navigate(['/serval-administration'], {
165175
queryParams: {
@@ -182,12 +192,19 @@ export class ServalProjectsComponent extends DataLoadingComponent implements OnI
182192
}
183193

184194
private getQueryParameters(): QueryParameters {
185-
return {
195+
const params: QueryParameters = {
186196
// Do not return resources
187197
[obj<SFProject>().pathStr(q => q.resourceConfig)]: null,
188198
$sort: { [obj<Project>().pathStr(p => p.name)]: 1 },
189199
$skip: this.pageIndex * this.pageSize,
190200
$limit: this.pageSize
191201
};
202+
203+
// Filter for projects with servalConfig set
204+
if (this.showProjectsWithCustomServalConfig) {
205+
params[obj<SFProject>().pathStr(q => q.translateConfig.draftConfig?.servalConfig)] = { $ne: null };
206+
}
207+
208+
return params;
192209
}
193210
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ <h2>{{ t("summary_notifications") }}</h2>
318318
</div>
319319
</mat-card>
320320
}
321+
@if (isCustomConfigSet) {
322+
<app-notice icon="info" type="info">
323+
{{ t("custom_configurations_apply") }}
324+
</app-notice>
325+
}
321326

322327
<div class="button-strip">
323328
<button mat-stroked-button matStepperPrevious>

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,8 @@ describe('DraftGenerationStepsComponent', () => {
13181318
{ projectId: 'source1', scriptureRange: 'LEV;NUM;DEU;JOS' },
13191319
{ projectId: 'source2', scriptureRange: 'DEU;JOS;1SA' }
13201320
],
1321-
lastSelectedTranslationScriptureRanges: [{ projectId: 'draftingSource', scriptureRange: 'GEN;EXO' }]
1321+
lastSelectedTranslationScriptureRanges: [{ projectId: 'draftingSource', scriptureRange: 'GEN;EXO' }],
1322+
servalConfig: '{ "custom": "value" }'
13221323
}
13231324
}
13241325
})
@@ -1357,6 +1358,10 @@ describe('DraftGenerationStepsComponent', () => {
13571358
expect(trainingGroups[1].ranges[0]).toEqual('Deuteronomy');
13581359
expect(trainingGroups[1].ranges[1]).toEqual('1 Samuel');
13591360
});
1361+
1362+
it('sets the custom serval config flag', () => {
1363+
expect(component['isCustomConfigSet']).toEqual(true);
1364+
});
13601365
});
13611366

13621367
describe('pending updates', () => {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export class DraftGenerationStepsComponent implements OnInit {
159159
protected trainingSources: DraftSource[] = [];
160160
protected trainingTargets: DraftSource[] = [];
161161
protected trainingDataFiles: Readonly<TrainingData>[] = [];
162+
protected isCustomConfigSet = false;
162163

163164
private trainingDataQuery?: RealtimeQuery<TrainingDataDoc>;
164165
private trainingDataQuerySubscription?: Subscription;
@@ -289,6 +290,7 @@ export class DraftGenerationStepsComponent implements OnInit {
289290
// See if there is an existing training scripture range
290291
const draftConfig: DraftConfig | undefined =
291292
this.activatedProject.projectDoc?.data?.translateConfig.draftConfig;
293+
this.isCustomConfigSet = draftConfig?.servalConfig != null;
292294
const hasPreviousTrainingRange: boolean =
293295
(draftConfig?.lastSelectedTrainingScriptureRanges ?? []).length > 0;
294296

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
@@ -242,6 +242,7 @@
242242
"configure_advanced_settings_header": "Advanced",
243243
"configure_advanced_settings_title": "Advanced settings",
244244
"confirm_codes_correct_to_continue": "Please confirm the language codes are correct before continuing",
245+
"custom_configurations_apply": "Custom draft configurations have been applied by an administrator.",
245246
"email_me": "Email me at {{ email }} when the draft is finished.",
246247
"fast_training_warning": "Enabling this will save time but greatly reduce accuracy.",
247248
"fast_training": "Enable Fast Training",

0 commit comments

Comments
 (0)