-
Notifications
You must be signed in to change notification settings - Fork 125
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
base: main
Are you sure you want to change the base?
Conversation
…-website into 96561-signer-flow-1010d
CustomPage: SignerContactInfoPage, | ||
CustomPageReview: null, | ||
...signerContactInfoPage, |
There was a problem hiding this comment.
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.
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']), | ||
}, | ||
}, | ||
}, |
There was a problem hiding this comment.
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.
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, | ||
); | ||
} |
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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
.
({ 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')} | ||
</> | ||
), |
There was a problem hiding this comment.
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.
...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')} |
There was a problem hiding this comment.
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.
({ 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> | ||
); | ||
}, |
There was a problem hiding this comment.
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.
`${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> | ||
); | ||
}, |
There was a problem hiding this comment.
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.
// 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); | ||
} | ||
|
There was a problem hiding this comment.
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']
transformedData?.certifierRelationship?.relationshipToVeteran | ||
?.applicant === true | ||
transformedData?.certifierRole === 'applicant' |
There was a problem hiding this comment.
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.
relationship: transformRelationship( | ||
transformedData?.certifierRelationship, | ||
), | ||
// will produce string list of checkboxgroup keys (e.g., "spouse; child") | ||
relationship: trueKeysOnly( | ||
transformedData?.certifierRelationship?.relationshipToVeteran, | ||
).join('; '), |
There was a problem hiding this comment.
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.
// TODO - remove usage of `certifierRole` in vets-api | ||
dataPostTransform.certifierRole = | ||
dataPostTransform?.certifierRelationship?.relationshipToVeteran | ||
?.applicant === true | ||
? 'applicant' | ||
: 'other'; | ||
dataPostTransform.certifierRole = transformedData.certifierRole; |
There was a problem hiding this comment.
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.
// 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:')), | ||
); | ||
} |
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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.
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; | ||
} | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
Are you removing, renaming or moving a folder in this PR?
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
orapplicant
section depending on the signer'scertifierRole
.It also adds custom field validation to ensure data is consistent across the signer/sponsor/applicant once duplicated.
Related issue(s)
Testing done
Screenshots
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
Error Handling
Authentication
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?