Skip to content

Commit 1c4ac76

Browse files
committed
SF-3772 Prevent duplicate onboarding requests
1 parent 797889b commit 1c4ac76

4 files changed

Lines changed: 38 additions & 12 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ import { JsonViewerComponent } from '../shared/json-viewer/json-viewer.component
2929
import { MobileNotSupportedComponent } from '../shared/mobile-not-supported/mobile-not-supported.component';
3030
import { projectLabel } from '../shared/utils';
3131
import {
32-
DraftingSignupFormData,
3332
DraftRequestResolutionKey,
3433
DraftRequestResolutionMetadata,
3534
OnboardingRequest,
35+
OnboardingRequestFormData,
3636
OnboardingRequestService
3737
} from '../translate/draft-generation/onboarding-request.service';
3838
import { ServalAdministrationService } from './serval-administration.service';
@@ -225,7 +225,7 @@ export class DraftRequestDetailComponent extends DataLoadingComponent implements
225225

226226
getStatus = this.onboardingRequestService.getStatus;
227227

228-
get formData(): DraftingSignupFormData {
228+
get formData(): OnboardingRequestFormData {
229229
return this.request!.submission.formData;
230230
}
231231

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

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { User } from 'realtime-server/lib/esm/common/models/user';
1717
import { DevOnlyComponent } from 'src/app/shared/dev-only/dev-only.component';
1818
import { ActivatedProjectService } from 'xforge-common/activated-project.service';
1919
import { DataLoadingComponent } from 'xforge-common/data-loading-component';
20+
import { DialogService } from 'xforge-common/dialog.service';
2021
import { I18nService } from 'xforge-common/i18n.service';
2122
import { NoticeService } from 'xforge-common/notice.service';
2223
import { UserService } from 'xforge-common/user.service';
@@ -28,7 +29,7 @@ import { ProjectSelectComponent } from '../../../project-select/project-select.c
2829
import { BookMultiSelectComponent } from '../../../shared/book-multi-select/book-multi-select.component';
2930
import { JsonViewerComponent } from '../../../shared/json-viewer/json-viewer.component';
3031
import { compareProjectsForSorting, projectLabel } from '../../../shared/utils';
31-
import { DraftingSignupFormData, OnboardingRequestService } from '../onboarding-request.service';
32+
import { OnboardingRequestFormData, OnboardingRequestService } from '../onboarding-request.service';
3233

3334
export const DRAFT_SIGNUP_RESPONSE_DAYS = { min: 1, max: 3 } as const;
3435

@@ -135,8 +136,9 @@ export class DraftOnboardingFormComponent extends DataLoadingComponent implement
135136
private readonly activatedProject: ActivatedProjectService,
136137
private readonly userService: UserService,
137138
private readonly paratextService: ParatextService,
138-
private readonly draftingSignupService: OnboardingRequestService,
139+
private readonly onboardingRequestService: OnboardingRequestService,
139140
protected readonly noticeService: NoticeService,
141+
protected readonly dialogService: DialogService,
140142
private readonly destroyRef: DestroyRef,
141143
private readonly cd: ChangeDetectorRef,
142144
private readonly i18n: I18nService
@@ -147,6 +149,8 @@ export class DraftOnboardingFormComponent extends DataLoadingComponent implement
147149
ngOnInit(): void {
148150
this.loadingStarted();
149151

152+
void this.checkAndWarnIfAlreadySubmitted();
153+
150154
// Get the current user and pre-fill the form
151155
this.userService
152156
.getCurrentUser()
@@ -204,9 +208,9 @@ export class DraftOnboardingFormComponent extends DataLoadingComponent implement
204208
}
205209

206210
async onSubmit(): Promise<void> {
207-
if (this.uiState === 'submitting') {
208-
return;
209-
}
211+
if (this.uiState === 'submitting') return;
212+
213+
if (await this.checkAndWarnIfAlreadySubmitted()) return;
210214

211215
if (this.signupForm.valid === true) {
212216
if (this.activatedProject.projectId == null) {
@@ -217,10 +221,10 @@ export class DraftOnboardingFormComponent extends DataLoadingComponent implement
217221
this.uiState = 'submitting';
218222
this.cd.markForCheck();
219223

220-
const formData: DraftingSignupFormData = this.signupForm.getRawValue() as DraftingSignupFormData;
224+
const formData: OnboardingRequestFormData = this.signupForm.getRawValue() as OnboardingRequestFormData;
221225

222226
try {
223-
const requestId = await this.draftingSignupService.submitOnboardingRequest(
227+
const requestId = await this.onboardingRequestService.submitOnboardingRequest(
224228
this.activatedProject.projectId,
225229
formData
226230
);
@@ -348,6 +352,27 @@ export class DraftOnboardingFormComponent extends DataLoadingComponent implement
348352
}
349353
}
350354

355+
/**
356+
* If a request has already been submitted:
357+
* - Informs user with a message dialog
358+
* - Redirects to drafting page when user closes dialog
359+
* - Resolves to true
360+
*
361+
* Otherwise, resolves to false
362+
*/
363+
private async checkAndWarnIfAlreadySubmitted(): Promise<boolean> {
364+
const alreadySubmitted = await this.hasRequestAlreadyBeenSubmitted();
365+
if (alreadySubmitted) {
366+
await this.dialogService.message('draft_sources.request_already_submitted', undefined, true);
367+
this.cancel();
368+
}
369+
return alreadySubmitted;
370+
}
371+
372+
private async hasRequestAlreadyBeenSubmitted(): Promise<boolean> {
373+
return (await this.onboardingRequestService.getOpenOnboardingRequest(this.activatedProject.projectId!)) != null;
374+
}
375+
351376
private async loadProjectsAndResources(): Promise<void> {
352377
try {
353378
const [projects, resources] = await Promise.all([

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/onboarding-request.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface DraftRequestComment {
1010
dateCreated: string;
1111
}
1212

13-
export interface DraftingSignupFormData {
13+
export interface OnboardingRequestFormData {
1414
name: string;
1515
email: string;
1616
organization: string;
@@ -44,7 +44,7 @@ export interface OnboardingRequest {
4444
projectId: string;
4545
userId: string;
4646
timestamp: string;
47-
formData: DraftingSignupFormData;
47+
formData: OnboardingRequestFormData;
4848
};
4949
assigneeId: string;
5050
status: DraftRequestStatusOption;
@@ -105,7 +105,7 @@ export class OnboardingRequestService {
105105
}
106106

107107
/** Submits a new signup request. */
108-
async submitOnboardingRequest(projectId: string, formData: DraftingSignupFormData): Promise<string> {
108+
async submitOnboardingRequest(projectId: string, formData: OnboardingRequestFormData): Promise<string> {
109109
return (await this.onlineInvoke<string>('submitOnboardingRequest', { projectId, formData }))!;
110110
}
111111

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
@@ -403,6 +403,7 @@
403403
"select_project_to_translate": "Select the project to translate.",
404404
"some_projects_use_back_translation": "Some projects get better results by adding a back translation as another reference.",
405405
"source_side_language_codes_differ": "All source and reference projects must be in the same language. Please select different source or reference projects.",
406+
"request_already_submitted": "A request to activate drafting on this project has already been submitted. Please wait for a response from the team before submitting another request.",
406407
"state_connecting": "Connecting",
407408
"state_sync_failed": "There was an error syncing",
408409
"state_sync_successful": "Sync successful",

0 commit comments

Comments
 (0)