-
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?
Changes from 6 commits
9b8b968
c74b5db
dd02abf
847f6f3
42d6fba
addfc05
c0b28c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
|
||
/** | ||
* Displays a custom alert based on the current `certifierRole` value. | ||
* Used to alert the user when we automatically fill in some portions | ||
* of the form based on their previous answers, e.g., adding an applicant | ||
* with the certifier's information if the certifier specified that they | ||
* were applying for themselves. | ||
* @param {object} formData Standard formdata object | ||
* @param {string} role Value we expect certifierRole to have if we are to display this message | ||
* @param {string} msg Optional custom text to display in our alert | ||
* @returns | ||
*/ | ||
export default function CustomPrefillMessage(formData, role, msg) { | ||
const text = | ||
msg || | ||
'We’ve prefilled some details based on information you provided earlier in this form. If you need to make changes, you can edit on this screen.'; | ||
const certifierRole = | ||
formData.certifierRole ?? formData['view:certifierRole']; | ||
if (certifierRole && certifierRole === role) | ||
return ( | ||
<va-alert status="info"> | ||
<p className="vads-u-margin-y--0"> | ||
<strong>Note:</strong> {text} | ||
</p> | ||
</va-alert> | ||
); | ||
return <></>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,13 +20,15 @@ import { | |
yesNoSchema, | ||
yesNoUI, | ||
radioSchema, | ||
radioUI, | ||
titleSchema, | ||
titleUI, | ||
} from 'platform/forms-system/src/js/web-component-patterns'; | ||
import VaTextInputField from 'platform/forms-system/src/js/web-component-fields/VaTextInputField'; | ||
import get from '@department-of-veterans-affairs/platform-forms-system/get'; | ||
import { blankSchema } from 'platform/forms-system/src/js/utilities/data/profile'; | ||
import SubmissionError from '../../shared/components/SubmissionError'; | ||
import CustomPrefillMessage from '../components/CustomPrefillAlert'; | ||
// import { fileUploadUi as fileUploadUI } from '../components/File/upload'; | ||
|
||
import { ssnOrVaFileNumberCustomUI } from '../components/CustomSsnPattern'; | ||
|
@@ -42,9 +44,12 @@ import { | |
onReviewPage, | ||
applicantListSchema, | ||
sponsorWording, | ||
populateFirstApplicant, | ||
page15aDepends, | ||
} from '../helpers/utilities'; | ||
import { | ||
certifierNameValidation, | ||
certifierAddressValidation, | ||
} from '../helpers/validations'; | ||
import { MAX_APPLICANTS, ADDITIONAL_FILES_HINT } from './constants'; | ||
import { applicantWording, getAgeInYears } from '../../shared/utilities'; | ||
import { sponsorNameDobConfig } from '../pages/Sponsor/sponsorInfoConfig'; | ||
|
@@ -107,16 +112,17 @@ import { | |
depends18f3, | ||
} from '../pages/ApplicantSponsorMarriageDetailsPage'; | ||
import { ApplicantAddressCopyPage } from '../../shared/components/applicantLists/ApplicantAddressPage'; | ||
import { | ||
signerContactInfoPage, | ||
SignerContactInfoPage, | ||
} from '../pages/SignerContactInfoPage'; | ||
|
||
import { hasReq } from '../../shared/components/fileUploads/MissingFileOverview'; | ||
import { fileWithMetadataSchema } from '../../shared/components/fileUploads/attachments'; | ||
|
||
// import mockData from '../tests/e2e/fixtures/data/test-data.json'; | ||
import FileFieldWrapped from '../components/FileUploadWrapper'; | ||
|
||
// Used by populateFirstApplicant fn: | ||
const SIGNER_REL_PATH = 'signer-relationship'; | ||
|
||
// Control whether we show the file overview page by calling `hasReq` to | ||
// determine if any required files have not been uploaded | ||
function showFileOverviewPage(formData) { | ||
|
@@ -134,7 +140,7 @@ const formConfig = { | |
rootUrl: manifest.rootUrl, | ||
urlPrefix: '/', | ||
transformForSubmit, | ||
showReviewErrors: !environment.isProduction(), | ||
showReviewErrors: true, // May want to hide in prod later, but for now keeping in due to complexity of this form | ||
submitUrl: `${environment.API_URL}/ivc_champva/v1/forms`, | ||
footerContent: GetFormHelp, | ||
// submit: () => | ||
|
@@ -184,13 +190,44 @@ const formConfig = { | |
certifierInformation: { | ||
title: 'Signer information', | ||
pages: { | ||
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']), | ||
}, | ||
}, | ||
}, | ||
page2: { | ||
// initialData: mockData.data, | ||
path: 'signer-info', | ||
title: 'Certification', | ||
uiSchema: { | ||
...titleUI('Your name'), | ||
certifierName: fullNameUI(), | ||
'ui:validations': [certifierNameValidation], | ||
}, | ||
schema: { | ||
type: 'object', | ||
|
@@ -210,6 +247,7 @@ const formConfig = { | |
'We’ll send any important information about this application to your address', | ||
), | ||
certifierAddress: addressUI(), | ||
'ui:validations': [certifierAddressValidation], | ||
}, | ||
schema: { | ||
type: 'object', | ||
|
@@ -223,27 +261,14 @@ const formConfig = { | |
page4: { | ||
path: 'signer-contact-info', | ||
title: 'Certification', | ||
uiSchema: { | ||
...titleUI( | ||
'Your contact information', | ||
'We use this information to contact you and verify other details.', | ||
), | ||
certifierPhone: phoneUI(), | ||
certifierEmail: emailUI(), | ||
}, | ||
schema: { | ||
type: 'object', | ||
required: ['certifierPhone', 'certifierEmail'], | ||
properties: { | ||
titleSchema, | ||
certifierPhone: phoneSchema, | ||
certifierEmail: emailSchema, | ||
}, | ||
}, | ||
CustomPage: SignerContactInfoPage, | ||
CustomPageReview: null, | ||
...signerContactInfoPage, | ||
Comment on lines
+264
to
+266
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
}, | ||
page5: { | ||
path: SIGNER_REL_PATH, | ||
path: 'signer-relationship', | ||
title: 'Certification', | ||
depends: formData => get('certifierRole', formData) === 'other', | ||
uiSchema: { | ||
...titleUI( | ||
'Your relationship to the applicant', | ||
|
@@ -255,7 +280,6 @@ const formConfig = { | |
hint: 'Select all that apply', | ||
required: () => true, | ||
labels: { | ||
applicant: 'I’m applying for benefits for myself', | ||
spouse: 'I’m an applicant’s spouse', | ||
child: 'I’m an applicant’s child', | ||
parent: 'I’m an applicant’s parent', | ||
|
@@ -280,22 +304,6 @@ const formConfig = { | |
'ui:options': { | ||
updateSchema: (formData, formSchema) => { | ||
const fs = formSchema; | ||
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, | ||
); | ||
} | ||
Comment on lines
-283
to
-298
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dropping this |
||
// If 'other', open the text field to specify: | ||
if ( | ||
get( | ||
|
@@ -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 commentThe 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 |
||
'spouse', | ||
'child', | ||
'parent', | ||
|
@@ -384,6 +391,7 @@ const formConfig = { | |
page8: { | ||
path: 'sponsor-status', | ||
title: 'Sponsor status', | ||
depends: formData => get('certifierRole', formData) !== 'sponsor', | ||
uiSchema: { | ||
sponsorInfoTitle: titleUI( | ||
'Sponsor status', | ||
|
@@ -409,7 +417,9 @@ const formConfig = { | |
page9: { | ||
path: 'sponsor-status-date', | ||
title: 'Sponsor status details', | ||
depends: formData => get('sponsorIsDeceased', formData), | ||
depends: formData => | ||
get('certifierRole', formData) !== 'sponsor' && | ||
get('sponsorIsDeceased', formData), | ||
uiSchema: { | ||
sponsorInfoTitle: titleUI('Sponsor status details'), | ||
sponsorDOD: dateOfDeathUI('When did the sponsor die?'), | ||
|
@@ -440,7 +450,16 @@ const formConfig = { | |
uiSchema: { | ||
...titleUI( | ||
({ formData }) => `${sponsorWording(formData)} mailing address`, | ||
'We’ll send any important information about this application to this address.', | ||
({ 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')} | ||
</> | ||
), | ||
Comment on lines
+453
to
+462
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
), | ||
sponsorAddress: { | ||
...addressUI({ | ||
|
@@ -500,15 +519,14 @@ const formConfig = { | |
title: 'Applicant information', | ||
path: 'applicant-info', | ||
uiSchema: { | ||
...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')} | ||
Comment on lines
-503
to
+529
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
</> | ||
)), | ||
applicants: { | ||
|
@@ -624,11 +642,20 @@ const formConfig = { | |
...titleUI( | ||
({ formData }) => | ||
`${applicantWording(formData)} mailing address`, | ||
({ 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> | ||
); | ||
}, | ||
Comment on lines
+645
to
+657
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as all the other uses of |
||
), | ||
'view:description': { | ||
'ui:description': | ||
'We’ll send any important information about your application to this address.', | ||
}, | ||
applicantAddress: { | ||
...addressUI({ | ||
labels: { | ||
|
@@ -642,7 +669,6 @@ const formConfig = { | |
}, | ||
schema: applicantListSchema([], { | ||
titleSchema, | ||
'view:description': blankSchema, | ||
applicantAddress: addressSchema(), | ||
}), | ||
}, | ||
|
@@ -657,8 +683,24 @@ const formConfig = { | |
items: { | ||
...titleUI( | ||
({ formData }) => | ||
`${applicantWording(formData)} contact information`, | ||
'This information helps us contact you faster if we need to follow up with you about your application', | ||
`${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> | ||
); | ||
}, | ||
Comment on lines
+686
to
+703
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same prefill alert functionality as referenced above. |
||
), | ||
applicantPhone: phoneUI(), | ||
applicantEmailAddress: emailUI(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,12 @@ function fmtDate(date) { | |
: date; | ||
} | ||
|
||
// 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); | ||
} | ||
|
||
Comment on lines
+16
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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'] |
||
// Simplify a relationship object from (potentially) multiple nested objects to a single string | ||
function transformRelationship(obj) { | ||
if (typeof obj === 'string') return obj; | ||
|
@@ -107,18 +113,18 @@ export default function transformForSubmit(formConfig, form) { | |
// If certifier is also applicant, we don't need to fill out the | ||
// bottom "Certification" section of the PDF: | ||
certification: | ||
transformedData?.certifierRelationship?.relationshipToVeteran | ||
?.applicant === true | ||
transformedData?.certifierRole === 'applicant' | ||
Comment on lines
-110
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we have the |
||
? { date: currentDate } | ||
: { | ||
date: currentDate, | ||
lastName: transformedData?.certifierName?.last || '', | ||
middleInitial: transformedData?.certifierName?.middle || '', | ||
firstName: transformedData?.certifierName?.first || '', | ||
phoneNumber: transformedData?.certifierPhone || '', | ||
relationship: transformRelationship( | ||
transformedData?.certifierRelationship, | ||
), | ||
// will produce string list of checkboxgroup keys (e.g., "spouse; child") | ||
relationship: trueKeysOnly( | ||
transformedData?.certifierRelationship?.relationshipToVeteran, | ||
).join('; '), | ||
Comment on lines
-119
to
+127
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
streetAddress: | ||
transformedData?.certifierAddress?.streetCombined || '', | ||
city: transformedData?.certifierAddress?.city || '', | ||
|
@@ -156,12 +162,7 @@ export default function transformForSubmit(formConfig, form) { | |
dataPostTransform.supportingDocs = dataPostTransform.supportingDocs | ||
.flat() | ||
.concat(supDocs); | ||
// TODO - remove usage of `certifierRole` in vets-api | ||
dataPostTransform.certifierRole = | ||
dataPostTransform?.certifierRelationship?.relationshipToVeteran | ||
?.applicant === true | ||
? 'applicant' | ||
: 'other'; | ||
dataPostTransform.certifierRole = transformedData.certifierRole; | ||
Comment on lines
-159
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we have |
||
dataPostTransform.statementOfTruthSignature = | ||
transformedData.statementOfTruthSignature; | ||
// `primaryContactInfo` is who BE callback API emails if there's a notification event | ||
|
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.