Skip to content

Commit 21e5c92

Browse files
committed
SF-3757 Simplify draft sources configuration page
1 parent 50ffeaf commit 21e5c92

28 files changed

Lines changed: 654 additions & 1106 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: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import {
2727
UsersAuthGuard
2828
} from './shared/project-router.guard';
2929
import { SyncComponent } from './sync/sync.component';
30+
import { ConfigureSourcesComponent } from './translate/draft-generation/configure-sources/configure-sources.component';
3031
import { DraftGenerationComponent } from './translate/draft-generation/draft-generation.component';
3132
import { DraftOnboardingFormComponent } from './translate/draft-generation/draft-signup-form/draft-onboarding-form.component';
32-
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';
3434
import { EditorComponent } from './translate/editor/editor.component';
3535
import { TranslateOverviewComponent } from './translate/translate-overview/translate-overview.component';
@@ -67,8 +67,8 @@ export const APP_ROUTES: Routes = [
6767
canDeactivate: [DraftNavigationAuthGuard]
6868
},
6969
{
70-
path: 'projects/:projectId/draft-generation/sources',
71-
component: DraftSourcesComponent,
70+
path: 'projects/:projectId/draft-generation/configure-sources',
71+
component: ConfigureSourcesComponent,
7272
canActivate: [NmtDraftAuthGuard],
7373
canDeactivate: [DraftNavigationAuthGuard]
7474
},
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<ng-container *transloco="let t; read: 'draft_sources'">
2+
<h1>{{ t("configure_draft_sources") }}</h1>
3+
4+
<mat-card>
5+
<mat-card-header>
6+
<mat-card-title>{{ t("train_language_model") }}</mat-card-title>
7+
</mat-card-header>
8+
<mat-card-content class="training-data-card-content">
9+
<h3 class="heading-left">{{ t("overview_reference") }} {{ parentheses(referenceLanguageDisplayName) }}</h3>
10+
11+
<div class="description-left">{{ t("select_primary_reference") }}</div>
12+
<div class="inputs-left">
13+
@for (source of trainingSources; track $index) {
14+
<app-project-select
15+
[isDisabled]="loading || !appOnline"
16+
[projects]="projects"
17+
[resources]="resources"
18+
[nonSelectableProjects]="nonSelectableProjects"
19+
[value]="source?.paratextId"
20+
[hiddenParatextIds]="getHiddenParatextIds(trainingSources, source?.paratextId)"
21+
(valueChange)="sourceSelected(trainingSources, $index, $event)"
22+
[placeholder]="projectPlaceholder(source)"
23+
></app-project-select>
24+
}
25+
26+
@if (allowAddingATrainingSource) {
27+
<div>{{ t("some_projects_use_back_translation") }}</div>
28+
<button
29+
mat-button
30+
(click)="trainingSources.push(undefined); $event.preventDefault()"
31+
class="add-another-project"
32+
>
33+
<mat-icon>add</mat-icon> {{ t("add_another_reference_project") }}
34+
</button>
35+
}
36+
</div>
37+
38+
<span class="arrow mirror-rtl"><mat-icon>arrow_right_alt</mat-icon></span>
39+
<h3 class="heading-right">{{ t("overview_translated_project") }} {{ parentheses(targetLanguageDisplayName) }}</h3>
40+
<div class="description-right">
41+
@for (portion of i18n.interpolateVariables("draft_sources.project_always_used"); track $index) {
42+
@if (portion.id === "currentProjectShortName") {
43+
<strong>{{ currentProjectShortName }}</strong>
44+
}
45+
<!-- prettier-ignore -->
46+
@else {{{ portion.text }}}
47+
}
48+
<!-- 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>). -->
49+
</div>
50+
<div class="inputs-right">
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+
<div class="training-files">
64+
<mat-divider></mat-divider>
65+
<h3>{{ t("additional_training_data_optional") }}</h3>
66+
<div>{{ t("training_files_description", { sourceLanguageDisplayName, targetLanguageDisplayName }) }}</div>
67+
<app-training-data-multi-select
68+
[availableTrainingData]="availableTrainingFiles"
69+
(trainingDataSelect)="onTrainingDataSelect($event)"
70+
></app-training-data-multi-select>
71+
</div>
72+
</mat-card-content>
73+
</mat-card>
74+
75+
<mat-card>
76+
<mat-card-header>
77+
<mat-card-title> {{ t("generate_a_draft_from_the_language_model") }} </mat-card-title>
78+
</mat-card-header>
79+
<mat-card-content class="translation-data-card-content">
80+
<h3 class="heading-left">{{ t("overview_source") }} {{ parentheses(sourceLanguageDisplayName) }}</h3>
81+
<div class="description-left">{{ t("select_project_to_translate") }}</div>
82+
<div class="inputs-left">
83+
@for (source of draftingSources; track $index) {
84+
<app-project-select
85+
[isDisabled]="loading || !appOnline"
86+
[projects]="projects"
87+
[resources]="resources"
88+
[nonSelectableProjects]="nonSelectableProjects"
89+
[value]="source?.paratextId"
90+
[hiddenParatextIds]="getHiddenParatextIds(draftingSources, source?.paratextId)"
91+
(valueChange)="sourceSelected(draftingSources, $index, $event)"
92+
[placeholder]="projectPlaceholder(source)"
93+
></app-project-select>
94+
}
95+
</div>
96+
</mat-card-content>
97+
</mat-card>
98+
99+
<app-language-codes-confirmation
100+
class="confirm-language-codes"
101+
[sources]="draftSourcesAsArray"
102+
[clearCheckbox]="clearLanguageCodeConfirmationCheckbox"
103+
(messageIfUserTriesToContinue)="languageCodeConfirmationMessageIfUserTriesToContinue = $event"
104+
></app-language-codes-confirmation>
105+
106+
<div class="component-footer">
107+
@if (!appOnline) {
108+
<mat-error id="offline-message">
109+
{{ t("offline_message") }}
110+
</mat-error>
111+
}
112+
<div class="page-actions">
113+
<button mat-button (click)="cancel()"><mat-icon>close</mat-icon>{{ t("cancel") }}</button>
114+
<button id="save_button" mat-flat-button color="primary" (click)="save()" [disabled]="!appOnline">
115+
<mat-icon>check</mat-icon>{{ t("save_and_sync") }}
116+
</button>
117+
</div>
118+
</div>
119+
120+
@if (getControlState("projectSettings") != null) {
121+
<mat-card class="saving">
122+
<mat-card-header>
123+
<mat-card-title>{{ t("saving_draft_sources") }}</mat-card-title>
124+
</mat-card-header>
125+
<mat-card-content>
126+
<div class="saving-indicator">
127+
@if (getControlState("projectSettings") === ElementState.Submitting) {
128+
<mat-spinner [diameter]="24" color="primary"></mat-spinner> {{ t("saving") }}
129+
} @else if (getControlState("projectSettings") === ElementState.Submitted) {
130+
<mat-icon class="success">checkmark</mat-icon> {{ t("all_changes_saved") }}
131+
} @else {
132+
<mat-icon class="failure">error</mat-icon> {{ t("failed_to_save_changes") }}
133+
}
134+
</div>
135+
@for (entry of syncStatus | keyvalue; track entry.key) {
136+
<div>
137+
@if (entry.value.knownToBeOnSF) {
138+
@if (entry.value.isSyncing) {
139+
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_syncing") }}
140+
} @else {
141+
@if (entry.value.lastSyncSuccessful) {
142+
<mat-icon class="success">check</mat-icon> {{ entry.value.shortName }} -
143+
{{ t("state_sync_successful") }}
144+
} @else {
145+
<mat-icon class="failure">error</mat-icon> {{ entry.value.shortName }} -
146+
{{ t("state_sync_failed") }}
147+
}
148+
}
149+
} @else {
150+
<mat-spinner [diameter]="24"></mat-spinner> {{ entry.value.shortName }} - {{ t("state_connecting") }}
151+
}
152+
</div>
153+
}
154+
</mat-card-content>
155+
@if (allProjectsSavedAndSynced || getControlState("projectSettings") === ElementState.Error) {
156+
<mat-card-actions align="end">
157+
<button mat-button (click)="navigateToDrafting()"><mat-icon>close</mat-icon> {{ t("close") }}</button>
158+
</mat-card-actions>
159+
}
160+
</mat-card>
161+
}
162+
</ng-container>
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
:host {
2+
display: flex;
3+
gap: 2em;
4+
flex-direction: column;
5+
max-width: 130em;
6+
}
7+
8+
h1 {
9+
margin: 0;
10+
}
11+
12+
h2,
13+
h3,
14+
strong {
15+
font-weight: 500;
16+
}
17+
18+
.training-data-card-content,
19+
.translation-data-card-content {
20+
display: grid;
21+
grid-row-gap: 12px;
22+
grid-column-gap: 24px;
23+
grid-template-columns: 1fr 24px 1fr;
24+
overflow-y: auto;
25+
26+
.heading-left,
27+
.description-left,
28+
.inputs-left {
29+
grid-column-start: 1;
30+
grid-column-end: 2;
31+
}
32+
.arrow {
33+
align-self: start;
34+
margin-top: 16px;
35+
grid-column-start: 2;
36+
grid-column-end: 3;
37+
}
38+
.heading-right,
39+
.description-right,
40+
.inputs-right {
41+
grid-column-start: 3;
42+
grid-column-end: 4;
43+
}
44+
.heading-right {
45+
grid-row-start: 1;
46+
}
47+
.description-right {
48+
grid-row-start: 2;
49+
}
50+
.inputs-right {
51+
grid-row-start: 3;
52+
}
53+
}
54+
55+
.training-files {
56+
grid-column-start: 1;
57+
grid-column-end: 4;
58+
display: flex;
59+
flex-direction: column;
60+
gap: 16px;
61+
}
62+
63+
.arrow mat-icon {
64+
scale: 2;
65+
}
66+
67+
.arrow {
68+
display: flex;
69+
align-items: center;
70+
}
71+
72+
#offline-message {
73+
display: flex;
74+
justify-self: flex-end;
75+
gap: 0.5em;
76+
margin-bottom: 0.5em;
77+
}
78+
79+
.page-actions {
80+
display: flex;
81+
gap: 0.5em;
82+
justify-content: flex-end;
83+
}
84+
85+
.add-another-project {
86+
margin-bottom: 1em;
87+
}
88+
89+
:host:has(.saving) > :not(.saving):not(h1) {
90+
display: none;
91+
}
92+
93+
.saving {
94+
grid-column-start: 1;
95+
grid-column-end: 3;
96+
width: auto;
97+
margin-inline: auto;
98+
}
99+
100+
.saving .mat-mdc-card-content {
101+
display: flex;
102+
gap: 1em;
103+
flex-direction: column;
104+
padding-block: 1em;
105+
106+
& > * {
107+
display: flex;
108+
align-items: center;
109+
gap: 0.5em;
110+
}
111+
}
112+
113+
.success {
114+
color: green;
115+
}
116+
.failure {
117+
color: var(--sf-error-color);
118+
}

0 commit comments

Comments
 (0)