Skip to content

Conversation

@Nateowami
Copy link
Collaborator

@Nateowami Nateowami commented Dec 11, 2025

Two tests are still failing, and I haven't had time to fix them yet.

Here's a quick walkthrough.

Enable the feature flag

Fill out the form

View the list of submissions

View/manage an individual submission


This change is Reviewable

@Nateowami Nateowami requested a review from Copilot December 11, 2025 21:35
@Nateowami Nateowami added the will require testing PR should not be merged until testers confirm testing is complete label Dec 11, 2025
@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 644e5f9 to 6aceae5 Compare December 11, 2025 21:39
@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 6aceae5 to b64c163 Compare December 11, 2025 21:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR integrates an in-app onboarding form for draft generation signup, replacing the previous external link approach. The implementation spans backend (C#) and frontend (Angular) with comprehensive CRUD operations, admin management interface, and E2E testing.

Key Changes

  • Added backend RPC controller and database models for onboarding requests with admin management capabilities
  • Created frontend form component with validation, conditional fields, and project/resource selection
  • Implemented Serval admin interface for viewing, managing, and processing onboarding requests
  • Refactored ProjectSelectComponent validation to support custom validators and error message mapping

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
src/SIL.XForge/Controllers/UrlConstants.cs Added URL constant for onboarding requests endpoint
src/SIL.XForge.Scripture/Services/SFJsonRpcApplicationBuilderExtensions.cs Registered onboarding RPC controller
src/SIL.XForge.Scripture/Models/OnboardingRequest.cs Backend data models for onboarding requests, submissions, and comments
src/SIL.XForge.Scripture/DataAccess/SFDataAccessServiceCollectionExtensions.cs MongoDB repository configuration for onboarding requests
src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs RPC endpoints for submission, retrieval, and admin management
src/SIL.XForge.Scripture/ClientApp/tsconfig.json Disabled noUnusedLocals compiler option
src/SIL.XForge.Scripture/ClientApp/src/xforge-common/url-constants.ts Added frontend URL constant
src/SIL.XForge.Scripture/ClientApp/src/xforge-common/notice.service.ts Changed loading state implementation from setTimeout to queueMicrotask
src/SIL.XForge.Scripture/ClientApp/src/xforge-common/feature-flags/feature-flag.service.ts Added feature flag for in-app draft signup form
src/SIL.XForge.Scripture/ClientApp/src/material-styles.scss Imported theme files for new components
src/SIL.XForge.Scripture/ClientApp/src/assets/i18n/non_checking_en.json Added localization strings for signup form and related UI
src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/drafting-signup.service.ts Service for onboarding request CRUD operations
src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.* Main signup form component with reactive forms and validation
src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-generation.component.* Updated to show signup button and existing request status
src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-apply-dialog/* Refactored validation logic from custom validators to inline functions
src/SIL.XForge.Scripture/ClientApp/src/app/shared/sfvalidators.ts Removed CustomValidatorState enum and customValidator method
src/SIL.XForge.Scripture/ClientApp/src/app/shared/dev-only/* New component for dev-only content visibility
src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.ts Changed to use MatButtonModule and MatIconModule
src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-administration.service.ts Added getByParatextId method
src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/serval-administration.component.* Added draft requests tab
src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-request-detail.component.* Detail view for individual onboarding requests
src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-request-constants.ts Constants for status and resolution labels
src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-onboarding-requests/* Admin list view for managing onboarding requests
src/SIL.XForge.Scripture/ClientApp/src/app/project-select/project-select.component.* Refactored to support external validators and custom error messages
src/SIL.XForge.Scripture/ClientApp/src/app/app.routes.ts Added routes for signup form and request detail
src/SIL.XForge.Scripture/ClientApp/e2e/workflows/submit-draft-signup.ts E2E test for form submission
.github/copilot-instructions.md Added guideline about using CSS variables/themes instead of hard-coded colors

});
}

private nextLoadingValue = this.isAppLoading;
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nextLoadingValue field is not initialized with a type annotation. According to the coding guidelines, specify types when declaring variables. This should be private nextLoadingValue: boolean = this.isAppLoading;

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 66 to 129
async approveRequest(options: { requestId: string; sfProjectId: string }): Promise<OnboardingRequest> {
const requestUpdateResult = await this.onlineInvoke<OnboardingRequest | undefined>('setResolution', {
requestId: options.requestId,
resolution: 'approved'
});
await this.projectService.onlineSetPreTranslate(options.sfProjectId, true);
return requestUpdateResult!;
}

/** Submits a new signup request. */
async submitOnboardingRequest(projectId: string, formData: DraftingSignupFormData): Promise<string> {
return (await this.onlineInvoke<string>('submitOnboardingRequest', { projectId, formData }))!;
}

/** Gets the existing signup request for the specified project, if any. */
async getOpenOnboardingRequest(projectId: string): Promise<OnboardingRequest | null> {
return (await this.onlineInvoke<OnboardingRequest | null>('getOpenOnboardingRequest', { projectId }))!;
}

/** Gets all onboarding requests (Serval admin only). */
async getAllRequests(): Promise<OnboardingRequest[]> {
return (await this.onlineInvoke<OnboardingRequest[]>('getAllRequests'))!;
}

/** Sets the assignee for an onboarding request (Serval admin only). */
async setAssignee(requestId: string, assigneeId: string): Promise<OnboardingRequest> {
return (await this.onlineInvoke<OnboardingRequest | undefined>('setAssignee', { requestId, assigneeId }))!;
}

/** Sets the resolution of an onboarding request (Serval admin only). */
async setResolution(requestId: string, resolution: string | null): Promise<OnboardingRequest> {
return (await this.onlineInvoke<OnboardingRequest | undefined>('setResolution', { requestId, resolution }))!;
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onlineInvoke method is called with non-null assertion operators (!) on lines 67, 72, 77, 82, 92, and 97. According to the coding guidelines, prefer explicit null/undefined checks rather than relying on truthy/falsy or assertions. These should return the type Promise<T | undefined> without the non-null assertion, or should explicitly check for undefined before returning.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +97 to +116
catch (Exception exception)
{
_exceptionHandler.RecordEndpointInfoForException(
new Dictionary<string, string>
{
{ "method", "SubmitOnboardingRequest" },
{ "projectId", projectId },
{ "userId", UserId },
}
);
// report the exception without failing the whole request
_exceptionHandler.ReportException(exception);
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.

Copilot Autofix

AI 11 days ago

In general, to fix this issue you replace broad catch (Exception) with more specific exception types that are expected and acceptable to handle locally, and allow other exceptions to bubble up. For “best-effort” operations like email sending, you normally catch well-known transient / communication-related exceptions, log them, and move on, while not swallowing unrelated programming bugs.

Here, the try/catch only surrounds the email-notification block. The intent (per the comment) is that email failures should not fail the whole onboarding request, but that failures should still be reported. A minimally invasive fix is therefore:

  • Narrow the catch from Exception to a more appropriate exception type for asynchronous operations and external services, such as System.Net.Mail.SmtpException (for classic SMTP) and System.Net.Http.HttpRequestException (for HTTP-based email services), and possibly TaskCanceledException (for timeout/cancellation).
  • Keep the existing logging/reporting behavior for those expected exceptions.
  • Let any other exceptions bubble up as before the try/catch, preserving detection of unexpected bugs.

Because we don’t see the internal implementation of IEmailService, we can’t know the exact concrete exception types. However, both HttpRequestException and TaskCanceledException are common for HTTP-based APIs; SmtpException is common for SMTP. All are from well-known BCL namespaces. We can safely add using System.Net.Http; and using System.Net.Mail; at the top of the file to reference these exception types.

Concretely:

  • In src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs, update the catch on line 104 to a multi-catch chain:
    • catch (HttpRequestException exception)
    • catch (TaskCanceledException exception)
    • catch (SmtpException exception)
  • Keep the body of each catch identical to the existing catch block, since the handling is the same for all these “email failed” cases.
  • Add the necessary using directives at the top for System.Net.Http and System.Net.Mail.

This yields narrower, intentional handling of likely email-delivery failures, while no longer swallowing every possible exception type.

Suggested changeset 1
src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs b/src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs
--- a/src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs
+++ b/src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs
@@ -2,6 +2,8 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using System.Net.Http;
+using System.Net.Mail;
 using EdjCase.JsonRpc.Router.Abstractions;
 using MongoDB.Bson;
 using SIL.XForge.Controllers;
@@ -101,7 +103,7 @@
                     await emailService.SendEmailAsync(email, subject, body);
                 }
             }
-            catch (Exception exception)
+            catch (HttpRequestException exception)
             {
                 _exceptionHandler.RecordEndpointInfoForException(
                     new Dictionary<string, string>
@@ -114,6 +116,32 @@
                 // report the exception without failing the whole request
                 _exceptionHandler.ReportException(exception);
             }
+            catch (TaskCanceledException exception)
+            {
+                _exceptionHandler.RecordEndpointInfoForException(
+                    new Dictionary<string, string>
+                    {
+                        { "method", "SubmitOnboardingRequest" },
+                        { "projectId", projectId },
+                        { "userId", UserId },
+                    }
+                );
+                // report the exception without failing the whole request
+                _exceptionHandler.ReportException(exception);
+            }
+            catch (SmtpException exception)
+            {
+                _exceptionHandler.RecordEndpointInfoForException(
+                    new Dictionary<string, string>
+                    {
+                        { "method", "SubmitOnboardingRequest" },
+                        { "projectId", projectId },
+                        { "userId", UserId },
+                    }
+                );
+                // report the exception without failing the whole request
+                _exceptionHandler.ReportException(exception);
+            }
 
             // Find all the paratext project ids in the sign up request
             // Start by collecting them into a set
EOF
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Mail;
using EdjCase.JsonRpc.Router.Abstractions;
using MongoDB.Bson;
using SIL.XForge.Controllers;
@@ -101,7 +103,7 @@
await emailService.SendEmailAsync(email, subject, body);
}
}
catch (Exception exception)
catch (HttpRequestException exception)
{
_exceptionHandler.RecordEndpointInfoForException(
new Dictionary<string, string>
@@ -114,6 +116,32 @@
// report the exception without failing the whole request
_exceptionHandler.ReportException(exception);
}
catch (TaskCanceledException exception)
{
_exceptionHandler.RecordEndpointInfoForException(
new Dictionary<string, string>
{
{ "method", "SubmitOnboardingRequest" },
{ "projectId", projectId },
{ "userId", UserId },
}
);
// report the exception without failing the whole request
_exceptionHandler.ReportException(exception);
}
catch (SmtpException exception)
{
_exceptionHandler.RecordEndpointInfoForException(
new Dictionary<string, string>
{
{ "method", "SubmitOnboardingRequest" },
{ "projectId", projectId },
{ "userId", UserId },
}
);
// report the exception without failing the whole request
_exceptionHandler.ReportException(exception);
}

// Find all the paratext project ids in the sign up request
// Start by collecting them into a set
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional.

@codecov
Copy link

codecov bot commented Dec 11, 2025

Codecov Report

❌ Patch coverage is 11.73848% with 594 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.95%. Comparing base (631a7da) to head (7a7ef50).
⚠️ Report is 1 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ture/Controllers/OnboardingRequestRpcController.cs 0.00% 269 Missing ⚠️
...aft-signup-form/draft-onboarding-form.component.ts 1.37% 143 Missing ⚠️
...boarding-requests/onboarding-requests.component.ts 2.12% 92 Missing ⚠️
...c/SIL.XForge.Scripture/Models/OnboardingRequest.cs 0.00% 41 Missing ⚠️
...nslate/draft-generation/drafting-signup.service.ts 38.88% 11 Missing ⚠️
...src/app/project-select/project-select.component.ts 72.22% 9 Missing and 1 partial ⚠️
...draft-apply-dialog/draft-apply-dialog.component.ts 69.69% 8 Missing and 2 partials ⚠️
...aAccess/SFDataAccessServiceCollectionExtensions.cs 50.00% 5 Missing ⚠️
...ate/draft-generation/draft-generation.component.ts 60.00% 4 Missing ⚠️
.../Services/SFJsonRpcApplicationBuilderExtensions.cs 0.00% 4 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3612      +/-   ##
==========================================
- Coverage   83.22%   81.95%   -1.28%     
==========================================
  Files         612      618       +6     
  Lines       37892    38494     +602     
  Branches     6222     6300      +78     
==========================================
+ Hits        31537    31549      +12     
- Misses       5382     5984     +602     
+ Partials      973      961      -12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from b64c163 to e612e74 Compare December 11, 2025 22:19
@RaymondLuong3 RaymondLuong3 self-assigned this Jan 2, 2026
Copy link
Collaborator

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this branch out and it is working well. I deleted a request and that deleted the record in mongo. I think this is probably OK. It allows the project to submit a new request, which is what I expected would happen.

@RaymondLuong3 partially reviewed 47 files and all commit messages, and made 14 comments.
Reviewable status: all files reviewed, 17 unresolved discussions (waiting on @Nateowami).


src/SIL.XForge.Scripture/ClientApp/e2e/workflows/submit-draft-signup.ts line 131 at r3 (raw file):

    // Back translation project (required when stage is written)
    await selectProjectByFieldName(page, user, 'Select your back translation', 'DHH94');

Not really a back translation but I'll let that slide.

Code quote:

    await selectProjectByFieldName(page, user, 'Select your back translation', 'DHH94');

src/SIL.XForge.Scripture/ClientApp/e2e/test_characterization.json line 24 at r3 (raw file):

  "submit_draft_signup": {
    "success": 0,
    "failure": 0

This looks suspicious that both are assigned the value 0.

Code quote:

  "submit_draft_signup": {
    "success": 0,
    "failure": 0

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.ts line 52 at r3 (raw file):

  // selected) even after the user has made the selection (though in the ProjectSelectComponent itself the selection
  // exists and is known to be valid).
  changeDetection: ChangeDetectionStrategy.OnPush,

Thanks for the explanation.

Code quote:

  changeDetection: ChangeDetectionStrategy.OnPush,

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.ts line 222 at r3 (raw file):

  }

  private async loadProjectsAndResources(): Promise<void> {

These private functions should be moved to the end of the file.

Code quote:

  private async loadProjectsAndResources(): Promise<void> {

src/SIL.XForge.Scripture/Models/OnboardingRequest.cs line 107 at r3 (raw file):

    public string TranslationLanguageIsoCode { get; set; } = string.Empty;
    public int[]? CompletedBooks { get; set; }
    public int[]? NextBooksToDraft { get; set; }

This can probably be initialized to an empty array and not be nullable.

Code quote:

    public int[]? CompletedBooks { get; set; }
    public int[]? NextBooksToDraft { get; set; }

src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs line 36 at r3 (raw file):

    private readonly ISFProjectService _projectService = projectService;

    public async Task<IRpcMethodResult> SubmitOnboardingRequest(string projectId, OnboardingRequestFormData formData)

Add a summary statement here.

Code quote:

   public async Task<IRpcMethodResult> SubmitOnboardingRequest(string projectId, OnboardingRequestFormData formData)

src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs line 135 at r3 (raw file):

                {
                    // Create the resource/source project and add the user to it
                    string sourceProjectId = await _projectService.CreateResourceProjectAsync(

What is the reason we need to create the project on SF at this time? Is it so that the serval admin has the ability to download the PT project?

Code quote:

                    string sourceProjectId = await _projectService.CreateResourceProjectAsync(

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 46 at r3 (raw file):

                <mat-option value="Bolshoi Group">Bolshoi Group</mat-option>
                <mat-option value="Seed Company">Seed Company</mat-option>
                <mat-option value="none">{{ t("partner_none") }}</mat-option>

We should probably read these companies from the component and iterate on the names rather than coding it directly into the template.

Code quote:

                <mat-option value="Bolshoi Group">Bolshoi Group</mat-option>
                <mat-option value="Seed Company">Seed Company</mat-option>
                <mat-option value="none">{{ t("partner_none") }}</mat-option>

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 65 at r3 (raw file):

          <mat-card-content>
            <div>
              <div class="form-field-label">{{ t("translation_language_name_label") }}</div>

This can probably be removed since it is in the textbox label.

Code quote:

             <div class="form-field-label">{{ t("translation_language_name_label") }}</div>

src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 77 at r3 (raw file):

            <div>
              <!-- <div class="form-field-label">{{ t("translation_language_iso_label") }}</div> -->

This line can be removed since the label is already in the textbox.

Code quote:

              <!-- <div class="form-field-label">{{ t("translation_language_iso_label") }}</div> -->

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-request-detail.component.html line 76 at r3 (raw file):

            <mat-icon>check_circle</mat-icon> Approve & Enable
          </button>
          <button mat-button color="warn" (click)="deleteRequest()"><mat-icon>delete</mat-icon> Delete Request</button>

This doesn't look like it has an effect.

Code quote:

color="warn" 

src/SIL.XForge.Scripture/ClientApp/src/xforge-common/feature-flags/feature-flag.service.ts line 366 at r3 (raw file):

  );

  readonly inAppDraftSignupForm: ObservableFeatureFlag = new FeatureFlagFromStorage(

When adding a new feature flag you should ad it to parse-verse.ts also.

Code quote:

  readonly inAppDraftSignupForm: ObservableFeatureFlag = new FeatureFlagFromStorage(

src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/onboarding-requests/onboarding-requests.component.ts line 134 at r3 (raw file):

   * Called after loading all requests or after updating individual requests.
   */
  private initializeRequestData(): void {

I normally expect private functions to come at the end of the file. Can you move these private functions to the end?

Code quote:

  private initializeRequestData(): void {

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 2050891 to 785b846 Compare January 5, 2026 18:53
Copy link
Collaborator Author

@Nateowami Nateowami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, being able to delete the requests is intended. The normal flow is that it is submitted and processed and marked complete without being deleted. Deletion provides additional flexibility (and makes more scenarios possible to test).

@Nateowami made 6 comments.
Reviewable status: 37 of 47 files reviewed, 17 unresolved discussions (waiting on @RaymondLuong3).


src/SIL.XForge.Scripture/ClientApp/e2e/test_characterization.json line 24 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

This looks suspicious that both are assigned the value 0.

I have now run the test characterization.


src/SIL.XForge.Scripture/ClientApp/e2e/workflows/submit-draft-signup.ts line 131 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

Not really a back translation but I'll let that slide.

Yeah, it really doesn't make any difference. A lot of projects have back translations that are not marked as back translations in Paratext.


src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-request-detail.component.html line 76 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

This doesn't look like it has an effect.

I've opened a PR that addresses this: #3621


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.ts line 222 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

These private functions should be moved to the end of the file.

Done.


src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs line 135 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

What is the reason we need to create the project on SF at this time? Is it so that the serval admin has the ability to download the PT project?

Yes. This is actually one of the most important improvements this form provides.

@Nateowami Nateowami marked this pull request as ready for review January 5, 2026 19:36
Copy link
Collaborator

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RaymondLuong3 reviewed 10 files and all commit messages, made 1 comment, and resolved 5 discussions.
Reviewable status: all files reviewed, 12 unresolved discussions (waiting on @Nateowami).


src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs line 135 at r3 (raw file):

Previously, Nateowami wrote…

Yes. This is actually one of the most important improvements this form provides.

Ok I see. It might be a surprise when a user discovers that their source project is already connected to Scripture Forge without them explicitly doing so, but it is conceivable that choosing a source at this step would result in that source being connected.

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 785b846 to 761d907 Compare January 7, 2026 16:43
Copy link
Collaborator Author

@Nateowami Nateowami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nateowami made 7 comments and resolved 4 discussions.
Reviewable status: 44 of 47 files reviewed, 8 unresolved discussions (waiting on @RaymondLuong3).


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 46 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

We should probably read these companies from the component and iterate on the names rather than coding it directly into the template.

If we needed to reference these elsewhere, I'd agree. But for static values that are just saved as a string in the model, I don't see any value to that.


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 65 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

This can probably be removed since it is in the textbox label.

Good catch.


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 77 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

This line can be removed since the label is already in the textbox.

Good catch.


src/SIL.XForge.Scripture/Controllers/OnboardingRequestRpcController.cs line 36 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

Add a summary statement here.

Done.


src/SIL.XForge.Scripture/Models/OnboardingRequest.cs line 107 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

This can probably be initialized to an empty array and not be nullable.

Done.


src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/onboarding-requests/onboarding-requests.component.ts line 134 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

I normally expect private functions to come at the end of the file. Can you move these private functions to the end?

For some other files I've moved private methods to the bottom. However, I find it to be an odd rule that I think harms readability.

I was about to move these private methods to the bottom as I've done with some others, but immediately noticed that it would make it hard to see how the component initializes. Right now you can read the constructor and see that it only inits its parent, the notice service. Then you can read ngOnInit and see that it sets the current user ID and kicks off an async method called loadRequests. loadRequests is private because we'd never expect anything outside the component to call it. But it's also the very next method in the file because it's the next logical thing the component does. If you had to scroll hundreds of lines it would be much harder to understand how the component works. You can then read loadRequests and see that it calls initializeRequestData, which is also the very next method. That method then calls loadProjectNames.

Comment on lines +97 to +116
catch (Exception exception)
{
_exceptionHandler.RecordEndpointInfoForException(
new Dictionary<string, string>
{
{ "method", "SubmitOnboardingRequest" },
{ "projectId", projectId },
{ "userId", UserId },
}
);
// report the exception without failing the whole request
_exceptionHandler.ReportException(exception);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional.

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 761d907 to c164e3f Compare January 7, 2026 16:48
Copy link
Collaborator Author

@Nateowami Nateowami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nateowami made 1 comment.
Reviewable status: 44 of 48 files reviewed, 8 unresolved discussions (waiting on @RaymondLuong3).


src/SIL.XForge.Scripture/ClientApp/src/xforge-common/feature-flags/feature-flag.service.ts line 366 at r3 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

When adding a new feature flag you should ad it to parse-verse.ts also.

Done

@Nateowami Nateowami added the e2e Run e2e tests for this pull request label Jan 7, 2026
Copy link
Collaborator

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think other than the failing tests it would be good to pass this along to testers to see what you will find. It is possible to exclude tests and come back to them later as long as the code is functional.

@RaymondLuong3 reviewed 4 files and all commit messages, made 3 comments, and resolved 6 discussions.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @Nateowami).


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/draft-signup-form/draft-onboarding-form.component.html line 46 at r3 (raw file):

Previously, Nateowami wrote…

If we needed to reference these elsewhere, I'd agree. But for static values that are just saved as a string in the model, I don't see any value to that.

That is reasonable to me.


src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/onboarding-requests/onboarding-requests.component.ts line 134 at r3 (raw file):

Previously, Nateowami wrote…

For some other files I've moved private methods to the bottom. However, I find it to be an odd rule that I think harms readability.

I was about to move these private methods to the bottom as I've done with some others, but immediately noticed that it would make it hard to see how the component initializes. Right now you can read the constructor and see that it only inits its parent, the notice service. Then you can read ngOnInit and see that it sets the current user ID and kicks off an async method called loadRequests. loadRequests is private because we'd never expect anything outside the component to call it. But it's also the very next method in the file because it's the next logical thing the component does. If you had to scroll hundreds of lines it would be much harder to understand how the component works. You can then read loadRequests and see that it calls initializeRequestData, which is also the very next method. That method then calls loadProjectNames.

Ok, I see what you mean. The natural flow of the processes is more important than grouping public and private methods. I have appreciated having the conceptual grouping of public methods together to help me understand how a component is exposed to other components, but I do agree that in some situations it makes sense to layout the code in a linear way.

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from c164e3f to 4c0980a Compare January 22, 2026 02:28
@Nateowami Nateowami added ready to test and removed will require testing PR should not be merged until testers confirm testing is complete labels Jan 22, 2026
@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 4c0980a to e2d3fd1 Compare January 22, 2026 14:49
@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from b87fb04 to f58bfd7 Compare January 30, 2026 14:42
@Nateowami
Copy link
Collaborator Author

@RaymondLuong3 What is the review status of this PR?

Copy link
Collaborator

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good on my end. I haven't looked at what the testers have found. I see there are broken tests, but they are likely to be replaced with the new apply draft workflow so that is fine with me.

@RaymondLuong3 partially reviewed 16 files and all commit messages, and made 2 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on @Nateowami).


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/drafting-signup.service.ts line 73 at r8 (raw file):

export const DRAFT_REQUEST_RESOLUTION_OPTIONS = [
  { key: null, label: 'Unresolved', icon: 'help_outline', color: 'gray' },

Any reason as to why the key here needs to be null? It would be more descriptive if it were assigned to a string. But maybe as null it shows there is not yet a resolution.

Code quote:

  { key: null, label: 'Unresolved', icon: 'help_outline', color: 'gray' },

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 8581d5b to 0a4f8da Compare January 30, 2026 20:16
Copy link
Collaborator Author

@Nateowami Nateowami left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nateowami made 1 comment.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on @RaymondLuong3).


src/SIL.XForge.Scripture/ClientApp/src/app/translate/draft-generation/drafting-signup.service.ts line 73 at r8 (raw file):

Previously, RaymondLuong3 (Raymond Luong) wrote…

Any reason as to why the key here needs to be null? It would be more descriptive if it were assigned to a string. But maybe as null it shows there is not yet a resolution.

Perhaps I should replace it with something else, but I've been using null = no resolution = unresolved.

I've changed to to use a string.

Copy link
Collaborator

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@RaymondLuong3 reviewed 4 files and all commit messages, made 1 comment, and resolved 1 discussion.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @Nateowami).

@Nateowami Nateowami force-pushed the feature/integrated-onboarding-form branch from 26979c9 to 7a7ef50 Compare January 30, 2026 20:29
Copy link
Collaborator

@RaymondLuong3 RaymondLuong3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked through the references to resolution and it looks like all have been updated. Glad that the tests caught this.

@RaymondLuong3 reviewed 1 file and all commit messages, and made 1 comment.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @Nateowami).

@Nateowami Nateowami merged commit 3664140 into master Jan 30, 2026
21 of 23 checks passed
@Nateowami Nateowami deleted the feature/integrated-onboarding-form branch January 30, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e Run e2e tests for this pull request ready to test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants