Skip to content

Commit aeb6d43

Browse files
authored
SF-3776 Add warnings for Serval admins on onboarding request page (#3793)
1 parent 02161c1 commit aeb6d43

2 files changed

Lines changed: 73 additions & 8 deletions

File tree

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-request-detail.component.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ <h1>{{ pageTitle }}</h1>
112112
</mat-card-content>
113113
</mat-card>
114114

115+
<h2>Warnings</h2>
116+
117+
@for (warning of warnings; track $index) {
118+
<app-notice type="warning" icon="warning" mode="fill-dark">{{ warning }}</app-notice>
119+
} @empty {
120+
<em>No warnings</em>
121+
}
122+
115123
<h2>Form submission</h2>
116124

117125
<mat-card>

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-request-detail.component.ts

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@ import { ActivatedRoute, Router } from '@angular/router';
1717
import { Canon } from '@sillsdev/scripture';
1818
import { saveAs } from 'file-saver';
1919
import Papa from 'papaparse';
20+
import { ProjectType } from 'realtime-server/lib/esm/scriptureforge/models/translate-config';
2021
import { catchError, lastValueFrom, of, throwError } from 'rxjs';
2122
import { DataLoadingComponent } from 'xforge-common/data-loading-component';
2223
import { DialogService } from 'xforge-common/dialog.service';
2324
import { NoticeService } from 'xforge-common/notice.service';
2425
import { OwnerComponent } from 'xforge-common/owner/owner.component';
2526
import { RouterLinkDirective } from 'xforge-common/router-link.directive';
27+
import { isPopulatedString } from '../../type-utils';
28+
import { SFProjectProfileDoc } from '../core/models/sf-project-profile-doc';
2629
import { ParatextService } from '../core/paratext.service';
2730
import { DevOnlyComponent } from '../shared/dev-only/dev-only.component';
2831
import { JsonViewerComponent } from '../shared/json-viewer/json-viewer.component';
2932
import { MobileNotSupportedComponent } from '../shared/mobile-not-supported/mobile-not-supported.component';
33+
import { NoticeComponent } from '../shared/notice/notice.component';
3034
import { projectLabel } from '../shared/utils';
35+
import { normalizeLanguageCodeToISO639_3 } from '../translate/draft-generation/draft-utils';
3136
import {
3237
DraftingSignupFormData,
3338
DraftRequestResolutionKey,
@@ -65,12 +70,15 @@ import { ServalAdministrationService } from './serval-administration.service';
6570
DevOnlyComponent,
6671
MatFormFieldModule,
6772
MatInputModule,
68-
MobileNotSupportedComponent
73+
MobileNotSupportedComponent,
74+
NoticeComponent
6975
]
7076
})
7177
export class DraftRequestDetailComponent extends DataLoadingComponent implements OnInit {
7278
request?: OnboardingRequest;
79+
mainProjectDoc?: SFProjectProfileDoc;
7380
projectName?: string;
81+
projectDocs: Map<string, SFProjectProfileDoc> = new Map();
7482
projectNames: Map<string, string> = new Map();
7583
projectIds: Map<string, string> = new Map(); // Maps Paratext ID to SF project ID
7684
projectShortNames: Map<string, string> = new Map(); // Maps Paratext ID to project short name
@@ -103,7 +111,6 @@ export class DraftRequestDetailComponent extends DataLoadingComponent implements
103111
try {
104112
this.request = await this.onboardingRequestService.getRequestById(requestId);
105113
await this.loadProjectNames();
106-
this.loadingFinished();
107114
} finally {
108115
this.loadingFinished();
109116
}
@@ -115,12 +122,13 @@ export class DraftRequestDetailComponent extends DataLoadingComponent implements
115122
}
116123

117124
// Load the main project (submission.projectId is an SF project ID)
118-
const mainProjectDoc = await this.servalAdministrationService.get(this.request.submission.projectId);
119-
if (mainProjectDoc?.data != null) {
120-
this.projectNames.set(this.request.submission.projectId, projectLabel(mainProjectDoc.data));
121-
this.projectIds.set(this.request.submission.projectId, mainProjectDoc.id);
122-
this.projectShortNames.set(this.request.submission.projectId, mainProjectDoc.data.shortName);
123-
this.projectName = projectLabel(mainProjectDoc.data);
125+
this.mainProjectDoc = await this.servalAdministrationService.get(this.request.submission.projectId);
126+
if (this.mainProjectDoc?.data != null) {
127+
this.projectDocs.set(this.request.submission.projectId, this.mainProjectDoc);
128+
this.projectNames.set(this.request.submission.projectId, projectLabel(this.mainProjectDoc.data));
129+
this.projectIds.set(this.request.submission.projectId, this.mainProjectDoc.id);
130+
this.projectShortNames.set(this.request.submission.projectId, this.mainProjectDoc.data.shortName);
131+
this.projectName = projectLabel(this.mainProjectDoc.data);
124132
} else {
125133
this.projectNames.set(this.request.submission.projectId, this.request.submission.projectId);
126134
this.projectName = this.request.submission.projectId;
@@ -139,6 +147,7 @@ export class DraftRequestDetailComponent extends DataLoadingComponent implements
139147
for (const paratextId of paratextIds) {
140148
const projectDoc = await this.servalAdministrationService.getByParatextId(paratextId);
141149
if (projectDoc?.data != null) {
150+
this.projectDocs.set(paratextId, projectDoc);
142151
this.projectNames.set(paratextId, projectLabel(projectDoc.data));
143152
this.projectIds.set(paratextId, projectDoc.id);
144153
this.projectShortNames.set(paratextId, projectDoc.data.shortName);
@@ -406,6 +415,54 @@ export class DraftRequestDetailComponent extends DataLoadingComponent implements
406415
saveAs(blob, fileName);
407416
}
408417

418+
get warnings(): string[] {
419+
const warnings: string[] = [];
420+
421+
if (this.request?.resolution === 'approved' && this.mainProjectDoc?.data?.translateConfig.preTranslate !== true) {
422+
warnings.push('This request is marked as approved but drafting is not enabled on the project.');
423+
}
424+
425+
const partnerOrg = this.request?.submission.formData.partnerOrganization;
426+
if (isPopulatedString(partnerOrg) && partnerOrg !== 'none' && this.request?.resolution !== 'outsourced') {
427+
warnings.push('This request has a partner organization specified but is not marked as outsourced.');
428+
}
429+
430+
const projectISOCode: string | undefined = this.mainProjectDoc?.data?.writingSystem.tag;
431+
const formISOCode: string | undefined = this.request?.submission.formData.translationLanguageIsoCode?.trim();
432+
if (
433+
isPopulatedString(projectISOCode) &&
434+
isPopulatedString(formISOCode) &&
435+
normalizeLanguageCodeToISO639_3(projectISOCode) !== normalizeLanguageCodeToISO639_3(formISOCode)
436+
) {
437+
warnings.push(
438+
`The project language code (${projectISOCode}) is not identical to the code specified in the form (${formISOCode}).`
439+
);
440+
}
441+
442+
// Check if back translation is specified, but isn't marked as a back translation, and isn't enabled for drafting
443+
const backTranslationProjectId = this.request?.submission.formData.backTranslationProject;
444+
const backTranslationTranslateConfig = this.projectDocs.get(backTranslationProjectId ?? '')?.data?.translateConfig;
445+
if (
446+
backTranslationTranslateConfig != null &&
447+
backTranslationTranslateConfig.projectType !== ProjectType.BackTranslation &&
448+
backTranslationTranslateConfig.preTranslate !== true
449+
) {
450+
warnings.push(
451+
'The back translation project specified is not marked as a back translation project in Paratext, and does not have draft generation enabled. You will need to enable it if you want the user to be able to generate back translation drafts.'
452+
);
453+
}
454+
455+
// Find projects that failed their last sync
456+
for (const [id, projectDoc] of this.projectDocs.entries()) {
457+
if (projectDoc.data?.sync.lastSyncSuccessful === false) {
458+
const projectName = this.projectNames.get(id) ?? id;
459+
warnings.push(`The project "${projectName}" failed to sync successfully the last time it was synced.`);
460+
}
461+
}
462+
463+
return warnings;
464+
}
465+
409466
/**
410467
* Data is pulled from the DOM rather than directly constructing the data in order to maintain consistency between
411468
* exported data and displayed data. It may have been the wrong call but it kept things simpler.

0 commit comments

Comments
 (0)