Skip to content

Commit 5d563b0

Browse files
committed
SF-3757 Add simplified draft config page as experimental feature
1 parent 797889b commit 5d563b0

25 files changed

Lines changed: 2044 additions & 34 deletions

scripts/db_tools/parse-version.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,17 @@ class ParseVersion {
3333
'Stillness (non-distracting progress indicators)',
3434
'Use In-Process Machine for Suggestions',
3535
'Use Serval for Suggestions',
36-
'Use Echo for Pre-Translation Drafting',
36+
'Allow Echo for Pre-Translation Drafting',
3737
'Allow Fast Pre-Translation Training',
3838
'Upload Paratext Zip Files for Pre-Translation Drafting',
3939
'Allow mixing in an additional training source',
4040
'Updated Learning Rate For Serval',
41-
'Dark Mode',
41+
'Dark mode',
4242
'Enable Lynx insights',
4343
'Preview new draft history interface',
4444
'USFM Format',
45-
'Show in-app draft signup form instead of external link'
45+
'Show in-app draft signup form instead of external link',
46+
'Show new configure sources page'
4647
];
4748

4849
constructor() {

src/SIL.XForge.Scripture/ClientApp/src/app/app.component.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@
149149
<button mat-menu-item appRouterLink="/projects" id="project-home-link">
150150
<mat-icon>home</mat-icon> {{ "my_projects.my_projects" | transloco }}
151151
</button>
152+
@if (experimentalFeatures.showExperimentalFeaturesInMenu) {
153+
<button mat-menu-item (click)="openExperimentalFeaturesDialog()">
154+
<mat-icon>science</mat-icon> {{ t("experimental_features") }}
155+
</button>
156+
}
152157
@if (featureFlags.showDeveloperTools.enabled) {
153158
<button mat-menu-item [matMenuTriggerFor]="appearanceMenu">
154159
<mat-icon>palette</mat-icon>

src/SIL.XForge.Scripture/ClientApp/src/app/app.component.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { DataLoadingComponent } from 'xforge-common/data-loading-component';
2828
import { DiagnosticOverlayService } from 'xforge-common/diagnostic-overlay.service';
2929
import { DialogService } from 'xforge-common/dialog.service';
3030
import { ErrorReportingService } from 'xforge-common/error-reporting.service';
31+
import { ExperimentalFeaturesDialogComponent } from 'xforge-common/experimental-features/experimental-features-dialog.component';
32+
import { ExperimentalFeaturesService } from 'xforge-common/experimental-features/experimental-features.service';
3133
import { ExternalUrlService } from 'xforge-common/external-url.service';
3234
import { FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service';
3335
import { FeatureFlagsDialogComponent } from 'xforge-common/feature-flags/feature-flags-dialog.component';
@@ -138,6 +140,7 @@ export class AppComponent extends DataLoadingComponent implements OnInit, OnDest
138140
private readonly themeService: ThemeService,
139141
private readonly fontService: FontService,
140142
onlineStatusService: OnlineStatusService,
143+
readonly experimentalFeatures: ExperimentalFeaturesService,
141144
private destroyRef: DestroyRef
142145
) {
143146
super(noticeService);
@@ -478,6 +481,10 @@ export class AppComponent extends DataLoadingComponent implements OnInit, OnDest
478481
this.dialogService.openMatDialog(FeatureFlagsDialogComponent);
479482
}
480483

484+
openExperimentalFeaturesDialog(): void {
485+
this.dialogService.openMatDialog(ExperimentalFeaturesDialogComponent);
486+
}
487+
481488
openDiagnosticOverlay(): void {
482489
this.diagnosticOverlayService.open();
483490
}

src/SIL.XForge.Scripture/ClientApp/src/app/app.routes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { DraftGenerationComponent } from './translate/draft-generation/draft-gen
3131
import { DraftOnboardingFormComponent } from './translate/draft-generation/draft-signup-form/draft-onboarding-form.component';
3232
import { DraftSourcesComponent } from './translate/draft-generation/draft-sources/draft-sources.component';
3333
import { DraftUsfmFormatComponent } from './translate/draft-generation/draft-usfm-format/draft-usfm-format.component';
34+
import { NewConfigureSourcesComponent } from './translate/draft-generation/new-configure-sources/new-configure-sources.component';
3435
import { EditorComponent } from './translate/editor/editor.component';
3536
import { TranslateOverviewComponent } from './translate/translate-overview/translate-overview.component';
3637
import { UsersComponent } from './users/users.component';
@@ -72,6 +73,12 @@ export const APP_ROUTES: Routes = [
7273
canActivate: [NmtDraftAuthGuard],
7374
canDeactivate: [DraftNavigationAuthGuard]
7475
},
76+
{
77+
path: 'projects/:projectId/draft-generation/new-configure-sources',
78+
component: NewConfigureSourcesComponent,
79+
canActivate: [NmtDraftAuthGuard],
80+
canDeactivate: [DraftNavigationAuthGuard]
81+
},
7582
{
7683
path: 'projects/:projectId/draft-generation/signup',
7784
component: DraftOnboardingFormComponent,

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,18 @@ <h1>
211211
{{ t("generate_draft_button") }}
212212
</button>
213213
@if (hasConfigureSourcePermission) {
214-
<button mat-button data-test-id="configure-button" [routerLink]="['sources']">
214+
<button mat-button data-test-id="configure-button" [routerLink]="configureSourcesPath">
215215
<mat-icon>settings</mat-icon>
216216
{{ t("configure_sources") }}
217217
</button>
218218
}
219219
} @else if (hasConfigureSourcePermission) {
220-
<button mat-flat-button data-test-id="configure-button" color="primary" [routerLink]="['sources']">
220+
<button
221+
mat-flat-button
222+
data-test-id="configure-button"
223+
color="primary"
224+
[routerLink]="configureSourcesPath"
225+
>
221226
<mat-icon>settings</mat-icon>
222227
{{ t("configure_sources") }}
223228
</button>
@@ -294,7 +299,7 @@ <h1>
294299
<mat-icon>add</mat-icon> {{ t("generate_new_draft") }}
295300
</button>
296301
@if (hasConfigureSourcePermission) {
297-
<button mat-button data-test-id="configure-button" [routerLink]="['sources']">
302+
<button mat-button data-test-id="configure-button" [routerLink]="configureSourcesPath">
298303
<mat-icon>settings</mat-icon> {{ t("configure_sources") }}
299304
</button>
300305
}
@@ -306,7 +311,7 @@ <h1>
306311
This first check regards what leads up to the first configure button. -->
307312
@if (!isGenerationSupported || isDraftInProgress(draftJob) || hasAnyCompletedBuild) {
308313
@if (draftEnabled && isOnline) {
309-
<button mat-button data-test-id="configure-button" color="primary" [routerLink]="['sources']">
314+
<button mat-button data-test-id="configure-button" color="primary" [routerLink]="configureSourcesPath">
310315
<mat-icon>settings</mat-icon> {{ t("configure_sources") }}
311316
</button>
312317
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ describe('DraftGenerationComponent', () => {
161161
mockFeatureFlagService = jasmine.createSpyObj<FeatureFlagService>({
162162
newDraftHistory: createTestFeatureFlag(false),
163163
usfmFormat: createTestFeatureFlag(false),
164-
inAppDraftSignupForm: createTestFeatureFlag(true)
164+
inAppDraftSignupForm: createTestFeatureFlag(true),
165+
newConfigureSourcesPage: createTestFeatureFlag(false)
165166
});
166167
}
167168

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ export class DraftGenerationComponent extends DataLoadingComponent implements On
342342
);
343343
}
344344

345+
get configureSourcesPath(): string[] {
346+
const projectId = this.activatedProject.projectId;
347+
if (projectId == null) return [];
348+
const minorPath = this.featureFlags.newConfigureSourcesPage.enabled ? 'new-configure-sources' : 'sources';
349+
return ['/projects', projectId, 'draft-generation', minorPath];
350+
}
351+
345352
async generateDraftClicked(): Promise<void> {
346353
if (this.formattingOptionsRequired) {
347354
const dialogRef = this.dialogService.openGenericDialog({

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ <h1>{{ t("configure_draft_sources") }}</h1>
9797
[placeholder]="projectPlaceholder(source)"
9898
></app-project-select>
9999
}
100-
<p class="no-bottom-margin">
100+
<p>
101101
{{ t("training_files_description", { sourceLanguageDisplayName, targetLanguageDisplayName }) }}
102102
</p>
103103
<div class="training-files">

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-sources/draft-sources.component.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,3 @@ strong {
239239
.training-files {
240240
margin-bottom: 30px;
241241
}
242-
243-
.no-bottom-margin {
244-
margin-bottom: 0;
245-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<ng-container *transloco="let t; read: 'draft_sources'">
2+
<h1>{{ t("configure_draft_sources") }}</h1>
3+
4+
<div class="overview">
5+
<mat-card>
6+
<mat-card-header>
7+
<mat-card-title>{{ "new_configure_sources.train_language_model" | transloco }}</mat-card-title>
8+
</mat-card-header>
9+
<mat-card-content class="training-data">
10+
<div class="sources">
11+
<h3>{{ t("overview_reference") }} {{ parentheses(referenceLanguageDisplayName) }}</h3>
12+
13+
<div>{{ t("select_primary_reference") }}</div>
14+
@for (source of trainingSources; track $index) {
15+
<app-project-select
16+
[isDisabled]="loading || !appOnline"
17+
[projects]="projects"
18+
[resources]="resources"
19+
[nonSelectableProjects]="nonSelectableProjects"
20+
[value]="source?.paratextId"
21+
[hiddenParatextIds]="getHiddenParatextIds(trainingSources, source?.paratextId)"
22+
(valueChange)="sourceSelected(trainingSources, $index, $event)"
23+
[placeholder]="projectPlaceholder(source)"
24+
></app-project-select>
25+
}
26+
27+
@if (allowAddingATrainingSource) {
28+
<div>{{ t("some_projects_use_back_translation") }}</div>
29+
<button
30+
mat-button
31+
(click)="trainingSources.push(undefined); $event.preventDefault()"
32+
class="add-another-project"
33+
>
34+
<mat-icon>add</mat-icon> {{ t("add_another_reference_project") }}
35+
</button>
36+
}
37+
</div>
38+
<span class="arrow mirror-rtl"><mat-icon>arrow_right_alt</mat-icon></span>
39+
<div class="targets">
40+
<h3>{{ t("overview_translated_project") }} {{ parentheses(targetLanguageDisplayName) }}</h3>
41+
<div>
42+
@for (portion of i18n.interpolateVariables("draft_sources.project_always_used"); track $index) {
43+
@if (portion.id === "currentProjectShortName") {
44+
<strong>{{ currentProjectShortName }}</strong>
45+
}
46+
<!-- prettier-ignore -->
47+
@else {{{ portion.text }}}
48+
}
49+
<!-- Select here any other projects to be used on the target side. All of these should be in the language of the target (<strong>{{ targetLanguageDisplayName }}</strong>). -->
50+
</div>
51+
@for (source of trainingTargets; track $index) {
52+
<app-project-select
53+
[isDisabled]="true"
54+
[projects]="projects"
55+
[resources]="resources"
56+
[nonSelectableProjects]="nonSelectableProjects"
57+
[value]="source?.paratextId"
58+
[hiddenParatextIds]="getHiddenParatextIds(trainingTargets, source?.paratextId)"
59+
[placeholder]="projectPlaceholder(source)"
60+
></app-project-select>
61+
}
62+
<div>
63+
{{ t("training_files_description", { sourceLanguageDisplayName, targetLanguageDisplayName }) }}
64+
</div>
65+
<div class="training-files">
66+
<app-training-data-multi-select
67+
[availableTrainingData]="availableTrainingFiles"
68+
(trainingDataSelect)="onTrainingDataSelect($event)"
69+
></app-training-data-multi-select>
70+
</div>
71+
</div>
72+
</mat-card-content>
73+
</mat-card>
74+
75+
<mat-card>
76+
<mat-card-header>
77+
<mat-card-title>
78+
{{ "new_configure_sources.generate_a_draft_from_the_language_model" | transloco }}
79+
</mat-card-title>
80+
</mat-card-header>
81+
<mat-card-content>
82+
<div class="translation-data">
83+
<h3>{{ t("overview_source") }} {{ parentheses(sourceLanguageDisplayName) }}</h3>
84+
<div>{{ t("select_project_to_translate") }}</div>
85+
@for (source of draftingSources; track $index) {
86+
<app-project-select
87+
[isDisabled]="loading || !appOnline"
88+
[projects]="projects"
89+
[resources]="resources"
90+
[nonSelectableProjects]="nonSelectableProjects"
91+
[value]="source?.paratextId"
92+
[hiddenParatextIds]="getHiddenParatextIds(draftingSources, source?.paratextId)"
93+
(valueChange)="sourceSelected(draftingSources, $index, $event)"
94+
[placeholder]="projectPlaceholder(source)"
95+
></app-project-select>
96+
}
97+
</div>
98+
</mat-card-content>
99+
</mat-card>
100+
</div>
101+
102+
<app-language-codes-confirmation
103+
class="confirm-language-codes"
104+
[sources]="draftSourcesAsArray"
105+
[clearCheckbox]="clearLanguageCodeConfirmationCheckbox"
106+
(messageIfUserTriesToContinue)="languageCodeConfirmationMessageIfUserTriesToContinue = $event"
107+
></app-language-codes-confirmation>
108+
109+
<div class="component-footer">
110+
@if (!appOnline) {
111+
<mat-error id="offline-message">
112+
{{ t("offline_message") }}
113+
</mat-error>
114+
}
115+
<div class="page-actions">
116+
<button mat-button (click)="cancel()"><mat-icon>close</mat-icon>{{ t("cancel") }}</button>
117+
<button id="save_button" mat-flat-button color="primary" (click)="save()" [disabled]="!appOnline">
118+
<mat-icon>check</mat-icon>{{ t("save_and_sync") }}
119+
</button>
120+
</div>
121+
</div>
122+
123+
@if (getControlState("projectSettings") != null) {
124+
<mat-card class="saving">
125+
<mat-card-header>
126+
<mat-card-title>{{ t("saving_draft_sources") }}</mat-card-title>
127+
</mat-card-header>
128+
<mat-card-content>
129+
<div class="saving-indicator">
130+
@if (getControlState("projectSettings") === ElementState.Submitting) {
131+
<mat-spinner [diameter]="24" color="primary"></mat-spinner> {{ t("saving") }}
132+
} @else if (getControlState("projectSettings") === ElementState.Submitted) {
133+
<mat-icon class="success">checkmark</mat-icon> {{ t("all_changes_saved") }}
134+
} @else {
135+
<mat-icon class="failure">error</mat-icon> {{ t("failed_to_save_changes") }}
136+
}
137+
</div>
138+
@for (entry of syncStatus | keyvalue; track entry.key) {
139+
<div>
140+
@if (entry.value.knownToBeOnSF) {
141+
@if (entry.value.isSyncing) {
142+
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_syncing") }}
143+
} @else {
144+
@if (entry.value.lastSyncSuccessful) {
145+
<mat-icon class="success">check</mat-icon> {{ entry.value.shortName }} -
146+
{{ t("state_sync_successful") }}
147+
} @else {
148+
<mat-icon class="failure">error</mat-icon> {{ entry.value.shortName }} -
149+
{{ t("state_sync_failed") }}
150+
}
151+
}
152+
} @else {
153+
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_connecting") }}
154+
}
155+
</div>
156+
}
157+
</mat-card-content>
158+
@if (allProjectsSavedAndSynced || getControlState("projectSettings") === ElementState.Error) {
159+
<mat-card-actions align="end">
160+
<button mat-button (click)="navigateToDrafting()"><mat-icon>close</mat-icon> {{ t("close") }}</button>
161+
</mat-card-actions>
162+
}
163+
</mat-card>
164+
}
165+
</ng-container>

0 commit comments

Comments
 (0)