Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

96561 Update signer flow to automatically copy data + notify user - Form 10-10d #33032

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

michaelclement
Copy link
Contributor

@michaelclement michaelclement commented Nov 14, 2024

Are you removing, renaming or moving a folder in this PR?

  • No, I'm not changing any folders (skip to TeamSites and delete the rest of this section)
  • Yes, I'm removing, renaming or moving a folder

Did you change site-wide styles, platform utilities or other infrastructure?

Summary

This PR updates the signer flow in form 10-10d so that some user data is automatically copied to different parts of the form based on user responses. This lets the user enter their contact details once, and automatically have them show up in the sponsor or applicant section depending on the signer's certifierRole.

It also adds custom field validation to ensure data is consistent across the signer/sponsor/applicant once duplicated.

Related issue(s)

Testing done

  • Updated/Added unit tests
  • Updated Cypress tests
  • Manual testing

Screenshots

Signer is an applicant Signer is the sponsor
Name app_prefill sponsor_prefill
Contact info app_prefill_contact Not pictured, but same behavior
Address app_prefill_address Not pictured, but same behavior
In accordion Out of accordion
Paired info validation review_cert_name_validation reveiw_cert_name_validation_link
Current test coverage %
Coverage unit_test_cov

What areas of the site does it impact?

(Describe what parts of the site are impacted if code touched other areas)

Acceptance criteria

Quality Assurance & Testing

  • I fixed|updated|added unit tests and integration tests for each feature (if applicable).
  • No sensitive information (i.e. PII/credentials/internal URLs/etc.) is captured in logging, hardcoded, or specs
  • Linting warnings have been addressed
  • Documentation has been updated (link to documentation *if necessary)
  • Screenshot of the developed feature is added
  • Accessibility testing has been performed

Error Handling

  • Browser console contains no warnings or errors.
  • Events are being sent to the appropriate logging solution
  • Feature/bug has a monitor built into Datadog or Grafana (if applicable)

Authentication

  • Did you login to a local build and verify all authenticated routes work as expected with a test user

Requested Feedback

(OPTIONAL) What should the reviewers know in addition to the above. Is there anything specific you wish the reviewer to assist with. Do you have any concerns with this PR, why?

@va-vfs-bot va-vfs-bot temporarily deployed to master/96561-signer-flow-1010d/main November 14, 2024 21:51 Inactive
@va-vfs-bot va-vfs-bot temporarily deployed to master/96561-signer-flow-1010d/main November 15, 2024 16:19 Inactive
@va-vfs-bot va-vfs-bot temporarily deployed to master/96561-signer-flow-1010d/main November 15, 2024 19:27 Inactive
Comment on lines +264 to +266
CustomPage: SignerContactInfoPage,
CustomPageReview: null,
...signerContactInfoPage,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switched to a custom page here so that we can do a custom onGoForward function. That custom function is what copies the certifier information to the appropriate other locations in the form.

Comment on lines +193 to +222
page1: {
// initialData: mockData.data,
path: 'signer-type',
title: 'Which of these best describes you?',
uiSchema: {
...titleUI('Your information'),
certifierRole: radioUI({
title: 'Which of these best describes you?',
required: () => true,
labels: {
applicant: 'I’m applying for benefits for myself',
sponsor:
'I’m a Veteran applying for benefits for my spouse or dependents',
other:
'I’m a representative applying for benefits on behalf of someone else',
},
// Changing this data on review messes up the ad hoc prefill
// mapping of certifier -> applicant|sponsor:
hideOnReview: true,
}),
},
schema: {
type: 'object',
required: ['certifierRole'],
properties: {
titleSchema,
certifierRole: radioSchema(['applicant', 'sponsor', 'other']),
},
},
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This page collects the signer's certifierRole, which is the key to determining which data to duplicate + which extra screens to show.

Comment on lines -283 to -298
if (
get(
'certifierRelationship.relationshipToVeteran.applicant',
formData,
)
) {
// If the certifier is also an applicant, pre-populate first app slot with certifier info:
populateFirstApplicant(
formData,
SIGNER_REL_PATH, // Used to verify we only ever apply this fn in one location
formData.certifierName,
formData.certifierEmail,
formData.certifierPhone,
formData.certifierAddress,
);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dropping this updateSchema functionality - the data replication has been moved into the SignerContactInfoPage's custom onGoForward function.

@@ -334,7 +342,6 @@ const formConfig = {
type: 'object',
properties: {
relationshipToVeteran: checkboxGroupSchema([
'applicant',
Copy link
Contributor Author

@michaelclement michaelclement Nov 15, 2024

Choose a reason for hiding this comment

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

No longer need this option because we get this info from the new page1.

Comment on lines +453 to +462
({ formData }) => (
// Prefill message conditionally displays based on `certifierRole`
<>
<p>
We’ll send any important information about this application
to this address.
</p>
{CustomPrefillMessage(formData, 'sponsor')}
</>
),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the signer is also the sponsor, we duplicate some of the signer's information over to the sponsor fields and add this alert to the top of the page.

Comment on lines -503 to +529
...titleUI('Applicant name and date of birth', () => (
...titleUI('Applicant name and date of birth', ({ formData }) => (
// Prefill message conditionally displays based on `certifierRole`
<>
Enter the information for any applicants you want to enroll in
CHAMPVA benefits.
<br />
<br />
{`You can add up to ${MAX_APPLICANTS} applicants in a single application. If you
need to add more than ${MAX_APPLICANTS} applicants, you'll need to submit a
separate application for them.`}
<p>
Enter the information for any applicants you want to enroll in
CHAMPVA benefits.
</p>
{CustomPrefillMessage(formData, 'applicant')}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updating copy + adding the custom prefill message for when the signer is also an applicant. This lets the users know when we've duplicated the signer info for them so they don't have to re-type.

Comment on lines +645 to +657
({ formData, formContext }) => {
const txt =
'We’ll send any important information about your application to this address';
// Prefill message conditionally displays based on `certifierRole`
return formContext.pagePerItemIndex === '0' ? (
<>
<p>{txt}</p>
{CustomPrefillMessage(formData, 'applicant')}
</>
) : (
<p>{txt}</p>
);
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same as all the other uses of CustomPrefillMessage - if the signer is also an applicant, we copy their address over to the first applicant slot and show this blue alert.

Comment on lines +686 to +703
`${applicantWording(formData)} mailing address`,
({ formData, formContext }) => {
const txt = `We'll use this information to contact ${applicantWording(
formData,
false,
false,
true,
)} if we need to follow up about this application.`;
// Prefill message conditionally displays based on `certifierRole`
return formContext.pagePerItemIndex === '0' ? (
<>
<p>{txt}</p>
{CustomPrefillMessage(formData, 'applicant')}
</>
) : (
<p>{txt}</p>
);
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same prefill alert functionality as referenced above.

Comment on lines +16 to +21
// Returns list of object keys where obj[key] === true. Used
// to get the list of relationships a certifier has w/ the applicants:
function trueKeysOnly(obj) {
return Object.keys(obj ?? {}).filter(k => obj[k] === true);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This lets us take an object like:

let obj = { applicant: true, spouse: false, child: true };

and produce a list like:

// ['applicant', 'child']

Comment on lines -110 to +116
transformedData?.certifierRelationship?.relationshipToVeteran
?.applicant === true
transformedData?.certifierRole === 'applicant'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that we have the certifierRole property, we no longer use the certifierRelationship object for this logic.

Comment on lines -119 to +127
relationship: transformRelationship(
transformedData?.certifierRelationship,
),
// will produce string list of checkboxgroup keys (e.g., "spouse; child")
relationship: trueKeysOnly(
transformedData?.certifierRelationship?.relationshipToVeteran,
).join('; '),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously, a certifier could have multiple relationships (e.g., spouse AND parent if there were two applicants listed). However, when we filled the PDF we were only showing the first of those values.

Now, we're producing a list like this:

relationship: trueKeysOnly(
  // { spouse: true, parent: true, child: false }
).join('; ')

Produces:

"spouse; parent"

which is then stamped in the PDF.

Comment on lines -159 to +165
// TODO - remove usage of `certifierRole` in vets-api
dataPostTransform.certifierRole =
dataPostTransform?.certifierRelationship?.relationshipToVeteran
?.applicant === true
? 'applicant'
: 'other';
dataPostTransform.certifierRole = transformedData.certifierRole;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that we have certifierRole, this logic can be simplified - the BE has expected a certifierRole property all along.

Comment on lines +102 to +110
// Compare two objects (with the same keys) and identify differences
export function objDiff(obj1, obj2, ignoreViewOnlyProps = true) {
return Object.keys(obj1).filter(
k =>
(obj1[k] && obj2[k]) !== undefined &&
obj1[k] !== obj2[k] &&
!(ignoreViewOnlyProps && k.includes('view:')),
);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Helper function - this gets used when we run the validation checks to make sure that every field of the signer's address is the same as every field of the sponsor (e.g., if the signer is the sponsor, they need to match field-for-field.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These validators are used to ensure that once data is duplicated, any changes made downstream are replicated in the original fields.

For instance - if the signer is also the sponsor, we duplicate the signer name to the sponsor name field so that when the user reaches the sponsor page, that info is already pre-filled. However, if the user then changes the sponsor's name (either intentionally or not), we will throw a validation error on the signer (visible on the review page) indicating that the names must be identical.

Comment on lines +41 to +64
export function signerContactOnGoForward(props) {
const formData = props.data; // changes made here will reflect in global formData;
if (props?.data?.certifierRole === 'applicant') {
populateFirstApplicant(
formData,
formData.certifierName,
formData.certifierEmail,
formData.certifierPhone,
formData.certifierAddress,
);
} else if (props?.data?.certifierRole === 'sponsor') {
// Populate some sponsor fields with certifier info:
formData.sponsorIsDeceased = false;
formData.veteransFullName = formData.certifierName;
formData.sponsorAddress = formData.certifierAddress;
formData.sponsorPhone = formData.certifierPhone;
}

if (formData?.applicants?.[0]) {
// This is so we have an accurate `certifierRole` prop inside the
// list loop where that context is normally unavailable:
formData.applicants[0]['view:certifierRole'] = formData?.certifierRole;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Custom onGoForward function - when the user clicks continue on this custom page, any data we want to duplicate is sent to the appropriate fields downstream.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a pass-through custom page. It takes the uiSchema and schema for a phone/email address page, and wraps it in a custom page so that we can attach a custom onGoForward function (described below).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants