+ {data?.attributes?.veteranStatus === 'confirmed' ? (
+ <>
+
-
-
-
+
+ {errors?.length > 0 ? (
+
+
+ {errors[0]}
+
+
+ ) : null}
+
+
+
+
+
+
-
-
-
- You can use our mobile app to get proof of Veteran status.
- To get started, download the{' '}
- VA: Health and Benefits mobile app.
- >
- }
- />
-
- >
- ) : null}
-
- {!vetStatusEligibility.confirmed &&
- vetStatusEligibility.message.length > 0 ? (
- <>
-
-
- {componentizedMessage.map((message, i) => {
- if (i === 0) {
- return (
-
- {message}
-
- );
+
+
+ You can use our mobile app to get proof of Veteran status.
+ To get started, download the{' '}
+ VA: Health and Benefits mobile app.
+ >
}
- return {message}
;
- })}
+ />
+
+ >
+ ) : null}
+
+ {data?.attributes?.veteranStatus !== 'confirmed' &&
+ data?.attributes?.message.length > 0 ? (
+ <>
+
+
+ {contactInfoElements.map((message, i) => {
+ if (i === 0) {
+ return (
+
+ {message}
+
+ );
+ }
+ return {message}
;
+ })}
+
+
+ >
+ ) : null}
+
+ {errors?.length > 0 ? (
+
+
+ {errors[0]}
- >
- ) : null}
-
+ ) : null}
+
+ ) : (
+
+
+ Proof of Veteran status
+
+
+ You can use your Veteran status card to get discounts offered to
+ Veterans at many restaurants, hotels, stores, and other businesses.
+
+
+ Note:
+ This card doesn’t entitle you to any VA benefits.
+
+
+ {vetStatusEligibility.confirmed ? (
+ <>
+
+
+
+
+ {errors?.length > 0 ? (
+
+
+ {errors[0]}
+
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+ You can use our mobile app to get proof of Veteran status.
+ To get started, download the{' '}
+ VA: Health and Benefits mobile app.
+ >
+ }
+ />
+
+ >
+ ) : null}
+
+ {!vetStatusEligibility.confirmed &&
+ vetStatusEligibility.message.length > 0 ? (
+ <>
+
+
+ {componentizedMessage.map((message, i) => {
+ if (i === 0) {
+ return (
+
+ {message}
+
+ );
+ }
+ return {message}
;
+ })}
+
+
+ >
+ ) : null}
+
+ )}
>
);
};
diff --git a/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx b/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx
index 81df2cad97f7..d2533e9c4ba9 100644
--- a/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx
+++ b/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx
@@ -6,6 +6,8 @@ import { generatePdf } from '~/platform/pdf';
import { focusElement } from '~/platform/utilities/ui';
import { captureError } from '~/platform/user/profile/vap-svc/util/analytics';
import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts';
+import { useFeatureToggle } from '~/platform/utilities/feature-toggles';
+import { apiRequest } from '~/platform/utilities/api';
import { formatFullName } from '../../../common/helpers';
import { getServiceBranchDisplayName } from '../../helpers';
import ProofOfVeteranStatusCard from './ProofOfVeteranStatusCard/ProofOfVeteranStatusCard';
@@ -24,6 +26,8 @@ const ProofOfVeteranStatusNew = ({
mockUserAgent,
}) => {
const [errors, setErrors] = useState([]);
+ const [data, setData] = useState(null);
+ const [shouldFocusError, setShouldFocusError] = useState(false);
const { first, middle, last, suffix } = userFullName;
const userAgent =
@@ -59,6 +63,8 @@ const ProofOfVeteranStatusNew = ({
serviceHistory.length && formattedFullName
);
+ const hasConfirmationData = !!(data && data.attributes);
+
const pdfData = {
title: `Veteran status card for ${formattedFullName}`,
details: {
@@ -86,13 +92,45 @@ const ProofOfVeteranStatusNew = ({
},
};
+ const { TOGGLE_NAMES, useToggleValue } = useFeatureToggle();
+ const useLighthouseApi = useToggleValue(
+ TOGGLE_NAMES.veteranStatusCardUseLighthouseFrontend,
+ );
+
+ useEffect(() => {
+ let isMounted = true;
+
+ const fetchVerificationStatus = async () => {
+ try {
+ const path = '/profile/vet_verification_status';
+ const response = await apiRequest(path);
+ if (isMounted) {
+ setData(response.data);
+ }
+ } catch (error) {
+ if (isMounted) {
+ setErrors([
+ "We're sorry. There's a problem with our system. We can't show your Veteran status card right now. Try again later.",
+ ]);
+ captureError(error, { eventName: 'vet-status-fetch-verification' });
+ }
+ }
+ };
+ fetchVerificationStatus();
+
+ return () => {
+ isMounted = false;
+ };
+ }, []);
+
useEffect(
() => {
- if (errors?.length > 0) {
+ if (shouldFocusError && errors?.length > 0) {
focusElement('.vet-status-pdf-download-error');
+ setShouldFocusError(false);
}
},
- [errors],
+ [shouldFocusError, errors],
);
const createPdf = async () => {
@@ -140,6 +178,33 @@ const ProofOfVeteranStatusNew = ({
);
});
+ const contactInfoElements = data?.attributes?.message?.map(item => {
+ const contactNumber = `${CONTACTS.DS_LOGON.slice(
+ 0,
+ 3,
+ )}-${CONTACTS.DS_LOGON.slice(3, 6)}-${CONTACTS.DS_LOGON.slice(6)}`;
+ const startIndex = item.indexOf(contactNumber);
+
+ if (startIndex === -1) {
+ return item;
+ }
+
+ const before = item.slice(0, startIndex);
+ const telephone = item.slice(
+ startIndex,
+ startIndex + contactNumber.length + 11,
+ );
+ const after = item.slice(startIndex + telephone.length);
+
+ return (
+ <>
+ {before}
+
(
+
){after}
+ >
+ );
+ });
+
return (
<>
@@ -152,74 +217,165 @@ const ProofOfVeteranStatusNew = ({
{userHasRequiredCardData ? (
<>
- {vetStatusEligibility.confirmed ? (
+ {!useLighthouseApi ? (
<>
- {errors?.length > 0 ? (
-
-
- {errors[0]}
-
-
+ {vetStatusEligibility.confirmed ? (
+ <>
+ {errors?.length > 0 ? (
+
+
+ {errors[0]}
+
+
+ ) : null}
+
+
+
+
+
+
+ You can use our mobile app to get proof of Veteran
+ status. To get started, download the{' '}
+ VA: Health and Benefits mobile
+ app.
+ >
+ }
+ />
+
+ >
+ ) : null}
+ {!vetStatusEligibility.confirmed &&
+ vetStatusEligibility.message.length > 0 ? (
+ <>
+
+
+ {componentizedMessage.map((message, i) => {
+ if (i === 0) {
+ return (
+
+ {message}
+
+ );
+ }
+ return {message}
;
+ })}
+
+
+ >
) : null}
-
-
-
-
-
-
- You can use our mobile app to get proof of Veteran
- status. To get started, download the{' '}
- VA: Health and Benefits mobile app.
- >
- }
- />
-
>
) : null}
- {!vetStatusEligibility.confirmed &&
- vetStatusEligibility.message.length > 0 ? (
+ {useLighthouseApi && hasConfirmationData ? (
<>
-
-
- {componentizedMessage.map((message, i) => {
- if (i === 0) {
- return (
-
- {message}
-
- );
- }
- return {message}
;
- })}
-
-
+ {data?.attributes?.veteranStatus === 'confirmed' ? (
+ <>
+ {errors?.length > 0 ? (
+
+
+ {errors[0]}
+
+
+ ) : null}
+
+
+
+
+
+
+ You can use our mobile app to get proof of Veteran
+ status. To get started, download the{' '}
+ VA: Health and Benefits mobile
+ app.
+ >
+ }
+ />
+
+ >
+ ) : null}
+
+ {data?.attributes?.veteranStatus !== 'confirmed' &&
+ data?.attributes?.message.length > 0 ? (
+ <>
+
+
+ {contactInfoElements.map((message, i) => {
+ if (i === 0) {
+ return (
+
+ {message}
+
+ );
+ }
+ return {message}
;
+ })}
+
+
+ >
+ ) : null}
>
) : null}
+
+ {useLighthouseApi && !hasConfirmationData ? (
+
+
+ We’re sorry. There’s a problem with our system. We can’t show
+ your Veteran status card right now. Try again later.
+
+
+ ) : null}
>
) : (
{
});
});
+ describe('should fetch verification status on render', () => {
+ let apiRequestStub;
+ const initialState = createBasicInitialState(
+ [eligibleServiceHistoryItem],
+ confirmedEligibility,
+ true,
+ );
+
+ beforeEach(() => {
+ apiRequestStub = sinon.stub(api, 'apiRequest');
+ });
+
+ afterEach(() => {
+ apiRequestStub.restore();
+ });
+
+ it('displays the card successfully', async () => {
+ const mockData = {
+ data: {
+ id: '',
+ type: 'veteran_status_confirmations',
+ attributes: { veteranStatus: 'confirmed' },
+ },
+ };
+
+ apiRequestStub.resolves(mockData);
+
+ const view = renderWithProfileReducers(, {
+ initialState,
+ });
+
+ sinon.assert.calledWith(
+ apiRequestStub,
+ '/profile/vet_verification_status',
+ );
+ await waitFor(() => {
+ expect(
+ view.queryByText(
+ /Get proof of Veteran status on your mobile device/i,
+ ),
+ ).to.exist;
+ expect(
+ view.queryByText(
+ /We’re sorry. There’s a problem with your discharge status records. We can’t provide a Veteran status card for you right now./,
+ ),
+ ).to.not.exist;
+ });
+ });
+
+ it('displays the returned not confirmed message', async () => {
+ const mockData = {
+ data: {
+ id: '',
+ type: 'veteran_status_confirmations',
+ attributes: {
+ veteranStatus: 'not confirmed',
+ notConfirmedReason: 'PERSON_NOT_FOUND',
+ message: problematicEligibility.message,
+ },
+ },
+ };
+
+ apiRequestStub.resolves(mockData);
+ const view = renderWithProfileReducers(, {
+ initialState,
+ });
+
+ await waitFor(() => {
+ expect(
+ view.queryByText(
+ /Get proof of Veteran Status on your mobile device/i,
+ ),
+ ).to.not.exist;
+ expect(
+ view.queryByText(
+ /We’re sorry. There’s a problem with your discharge status records. We can’t provide a Veteran status card for you right now./,
+ ),
+ ).to.exist;
+ });
+ });
+
+ it('handles empty API response', async () => {
+ const mockData = {
+ data: {},
+ };
+ apiRequestStub.resolves(mockData);
+ const view = renderWithProfileReducers(, {
+ initialState,
+ });
+
+ await waitFor(() => {
+ expect(
+ view.queryByText(
+ 'We’re sorry. There’s a problem with our system. We can’t show your Veteran status card right now. Try again later.',
+ ),
+ ).to.exist;
+ });
+ });
+
+ it('handles API error', async () => {
+ apiRequestStub.rejects(new Error('API Error'));
+ const view = renderWithProfileReducers(, {
+ initialState,
+ });
+
+ await waitFor(() => {
+ expect(
+ view.getByText(
+ 'We’re sorry. There’s a problem with our system. We can’t show your Veteran status card right now. Try again later.',
+ ),
+ ).to.exist;
+ });
+ });
+ });
+
describe('when eligible', () => {
const initialState = createBasicInitialState(
[
diff --git a/src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js b/src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js
new file mode 100644
index 000000000000..ba62fadf4807
--- /dev/null
+++ b/src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js
@@ -0,0 +1,29 @@
+const confirmed = {
+ data: {
+ id: '',
+ type: 'veteran_status_confirmations',
+ attributes: {
+ veteranStatus: 'confirmed',
+ },
+ },
+};
+
+const notConfirmed = {
+ data: {
+ id: null,
+ type: 'veteran_status_confirmations',
+ attributes: {
+ veteranStatus: 'not confirmed',
+ notConfirmedReason: 'PERSON_NOT_FOUND',
+ message: [
+ 'We’re sorry. There’s a problem with your discharge status records. We can’t provide a Veteran status card for you right now.',
+ 'To fix the problem with your records, call the Defense Manpower Data Center at 800-538-9552 (TTY: 711). They’re open Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.',
+ ],
+ },
+ },
+};
+
+module.exports = {
+ confirmed,
+ notConfirmed,
+};
diff --git a/src/applications/personalization/profile/mocks/server.js b/src/applications/personalization/profile/mocks/server.js
index 9177f471ca1d..fcb081e41bc3 100644
--- a/src/applications/personalization/profile/mocks/server.js
+++ b/src/applications/personalization/profile/mocks/server.js
@@ -22,6 +22,7 @@ const mockDisabilityCompensations = require('./endpoints/disability-compensation
const directDeposits = require('./endpoints/direct-deposits');
const bankAccounts = require('./endpoints/bank-accounts');
const serviceHistory = require('./endpoints/service-history');
+const vetVerificationStatus = require('./endpoints/vet-verification-status');
const fullName = require('./endpoints/full-name');
const {
baseUserTransitionAvailabilities,
@@ -107,6 +108,7 @@ const responses = {
profileShowPrivacyPolicy: true,
veteranOnboardingContactInfoFlow: true,
veteranStatusCardUseLighthouse: true,
+ veteranStatusCardUseLighthouseFrontend: true,
}),
),
secondsOfDelay,
@@ -236,6 +238,10 @@ const responses = {
// .status(200)
// .json(serviceHistory.generateServiceHistoryError('403'));
},
+ 'GET /v0/profile/vet_verification_status': (_req, res) => {
+ return res.status(200).json(vetVerificationStatus.confirmed);
+ // return res.status(200).json(vetVerificationStatus.notConfirmed);
+ },
'GET /v0/disability_compensation_form/rating_info': (_req, res) => {
// return res.status(200).json(ratingInfo.success.serviceConnected0);
return res.status(200).json(ratingInfo.success.serviceConnected40);
diff --git a/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx b/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx
index 3ac7fdcd0b6d..fcf3bb8ebf91 100644
--- a/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx
+++ b/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import { expect } from 'chai';
-
+import * as api from '~/platform/utilities/api';
+import sinon from 'sinon';
import { renderWithProfileReducers } from '../../unit-test-helpers';
-
import MilitaryInformation from '../../../components/military-information/MilitaryInformation';
function createBasicInitialState(toggles = {}) {
@@ -61,6 +61,16 @@ function createBasicInitialState(toggles = {}) {
describe('MilitaryInformation', () => {
let initialState;
let view;
+ let apiRequestStub;
+
+ beforeEach(() => {
+ apiRequestStub = sinon.stub(api, 'apiRequest');
+ });
+
+ afterEach(() => {
+ apiRequestStub.restore();
+ });
+
describe('when military history exists', () => {
it('should render data for each entry of military history', () => {
initialState = createBasicInitialState();
@@ -91,6 +101,15 @@ describe('MilitaryInformation', () => {
initialState = createBasicInitialState();
initialState.vaProfile.militaryInformation.serviceHistory.serviceHistory[0].branchOfService = null;
initialState.vaProfile.militaryInformation.serviceHistory.serviceHistory[1].branchOfService = undefined;
+ const mockData = {
+ data: {
+ id: '',
+ type: 'veteran_status_confirmations',
+ attributes: { veteranStatus: 'confirmed' },
+ },
+ };
+
+ apiRequestStub.resolves(mockData);
view = renderWithProfileReducers(, {
initialState,
});
diff --git a/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx b/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx
index 2cd5b272046a..3b6a2c289116 100644
--- a/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx
+++ b/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx
@@ -1,5 +1,7 @@
import React from 'react';
import { expect } from 'chai';
+import * as api from '~/platform/utilities/api';
+import sinon from 'sinon';
import { renderWithProfileReducers } from '../../unit-test-helpers';
import MilitaryInformation from '../../../components/military-information/MilitaryInformation';
@@ -58,6 +60,16 @@ function createBasicInitialState(toggles = {}) {
}
describe('MilitaryInformation - Period of Service Type Text', () => {
+ let apiRequestStub;
+
+ beforeEach(() => {
+ apiRequestStub = sinon.stub(api, 'apiRequest');
+ });
+
+ afterEach(() => {
+ apiRequestStub.restore();
+ });
+
describe('when military history exists', () => {
it('should render periodOfServiceTypeText when present and when periodOfServiceTypeCode is A or V', () => {
const initialState = createBasicInitialState();
diff --git a/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js b/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js
index 5f007816d5c0..08cb01bf1704 100644
--- a/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js
+++ b/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js
@@ -8,6 +8,10 @@ import {
dishonorableDischarge,
unknownDischarge,
} from '../../../mocks/endpoints/service-history';
+import {
+ confirmed,
+ notConfirmed,
+} from '../../../mocks/endpoints/vet-verification-status';
import MilitaryInformation from '../military-information/MilitaryInformation';
describe('Proof of Veteran status', () => {
@@ -16,6 +20,7 @@ describe('Proof of Veteran status', () => {
cy.intercept('GET', '/v0/user', loa3User72);
cy.intercept('GET', '/v0/profile/full_name', fullName.success);
cy.intercept('GET', '/v0/profile/service_history', airForce);
+ cy.intercept('GET', '/v0/profile/vet_verification_status', confirmed);
});
it('Should display the Proof of Veteran Status component', () => {
@@ -31,6 +36,7 @@ const login = ({ dischargeCode }) => {
cy.intercept('GET', '/v0/user', loa3User72);
cy.intercept('GET', '/v0/profile/full_name', fullName.success);
cy.intercept('GET', '/v0/profile/service_history', dischargeCode);
+ cy.intercept('GET', '/v0/profile/vet_verification_status', notConfirmed);
};
describe('Veteran is not eligible', () => {