diff --git a/src/applications/travel-pay/components/Breadcrumbs.jsx b/src/applications/travel-pay/components/Breadcrumbs.jsx index f076a8232f38..2f9635b49912 100644 --- a/src/applications/travel-pay/components/Breadcrumbs.jsx +++ b/src/applications/travel-pay/components/Breadcrumbs.jsx @@ -11,7 +11,7 @@ export default function BreadCrumbs() { const { apptId } = useParams(); - // TODO: this needs work + // TODO: this might need work - it works for now, but we might need a regex like the isDetailsPage const isSubmitWrapper = pathname.includes(`/file-new-claim/${apptId}`); const isFileClaimExplainerPage = pathname.includes('/file-new-claim'); diff --git a/src/applications/travel-pay/components/HelpText.jsx b/src/applications/travel-pay/components/HelpText.jsx index c9dfdfde3c00..bc4fef75a8fa 100644 --- a/src/applications/travel-pay/components/HelpText.jsx +++ b/src/applications/travel-pay/components/HelpText.jsx @@ -1,9 +1,12 @@ import React from 'react'; -import { Link } from 'react-router-dom'; -export default function HelpTextContent() { - const BTSSS_PORTAL_URL = 'https://dvagov-btsss.dynamics365portals.us/'; +import { + BTSSS_PORTAL_URL, + FORM_103542_LINK, + FIND_FACILITY_TP_CONTACT_LINK, +} from '../constants'; +export const HelpTextManage = () => { return (

@@ -20,17 +23,57 @@ export default function HelpTextContent() { Monday through Friday. Have your claim number ready to share when you call.

-

- - What does my claim status mean? - +

+ ); +}; + +export const HelpTextGeneral = () => { + return ( +
+

+ Call the BTSSS call center at . + We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET. +

+

+ Or call your VA health facility’s Beneficiary Travel contact. +

+ +
+ ); +}; + +export const HelpTextModalities = () => { + return ( +
+

+ You can still file a claim within 30 days of this appointment these + other ways:

+
); -} +}; diff --git a/src/applications/travel-pay/components/TravelClaimDetails.jsx b/src/applications/travel-pay/components/TravelClaimDetails.jsx index 855a928e0465..e2a52162bb2e 100644 --- a/src/applications/travel-pay/components/TravelClaimDetails.jsx +++ b/src/applications/travel-pay/components/TravelClaimDetails.jsx @@ -9,7 +9,7 @@ import { Element } from 'platform/utilities/scroll'; import { apiRequest } from '@department-of-veterans-affairs/platform-utilities/api'; import environment from '@department-of-veterans-affairs/platform-utilities/environment'; -import HelpTextContent from './HelpText'; +import { HelpTextManage } from './HelpText'; import BreadCrumbs from './Breadcrumbs'; import ClaimDetailsContent from './ClaimDetailsContent'; @@ -116,7 +116,7 @@ export default function TravelClaimDetails() {
- +
diff --git a/src/applications/travel-pay/components/submit-flow/pages/AddressPage.jsx b/src/applications/travel-pay/components/submit-flow/pages/AddressPage.jsx index 8111686e3940..325678f4b3a6 100644 --- a/src/applications/travel-pay/components/submit-flow/pages/AddressPage.jsx +++ b/src/applications/travel-pay/components/submit-flow/pages/AddressPage.jsx @@ -1,7 +1,8 @@ import React from 'react'; -import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; import PropTypes from 'prop-types'; +import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; + const AddressPage = ({ handlers }) => { return (
diff --git a/src/applications/travel-pay/components/submit-flow/pages/IntroductionPage.jsx b/src/applications/travel-pay/components/submit-flow/pages/IntroductionPage.jsx index 32fb43b9412f..1beebd92d8de 100644 --- a/src/applications/travel-pay/components/submit-flow/pages/IntroductionPage.jsx +++ b/src/applications/travel-pay/components/submit-flow/pages/IntroductionPage.jsx @@ -1,21 +1,100 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import PropTypes from 'prop-types'; + +import { HelpTextManage } from '../../HelpText'; +import { formatDateTime, getDaysLeft } from '../../../util/dates'; + +const IntroductionPage = ({ appointment, onStart }) => { + const [formattedDate] = formatDateTime(appointment.vaos.apiData.start); + const daysLeft = getDaysLeft(appointment.vaos.apiData.start); -const IntroductionPage = ({ onNext }) => { return (

File a travel reimbursement claim

- onNext(e)} - href="javascript0:void" - text="File a mileage only claim" - /> +

+ You have{' '} + {`${daysLeft} ${daysLeft === 1 ? 'day' : 'days'}`} left + to file for your appointment on{' '} + + {formattedDate} at {appointment.vaos.apiData.location.attributes.name} + + . +

+ +

+ Follow the steps below to apply for beneficiary travel claim. +

+ + +

+ If you’re eligible for health care travel reimbursement and you have + your direct deposit set up, you can file a reimbursement claim now. +

+ +
+ +

+ If you’re only claiming mileage, you can file online right now. + We’ll just ask you a few questions—you won’t need receipts. +

+ onStart(e)} + href="javascript0:void" + text="File a mileage only claim" + /> + +

+ If you’re claiming other expenses, like lodging, meals, or tolls, + you will need receipts for these expenses. You can file online + through the Beneficiary Travel Self Service System (BTSSS), by mail, + or in person. +

+ +
+
+ +

+ Set up direct deposit +

+

+ You have to set up direct deposit to receive travel reimbursement. If + you’ve already done this, no additional steps are needed. +

+ +
+
+ +
+ + +
+ +
+
); }; IntroductionPage.propTypes = { - onNext: PropTypes.func, + appointment: PropTypes.object, + onStart: PropTypes.func, }; export default IntroductionPage; diff --git a/src/applications/travel-pay/components/submit-flow/pages/MileagePage.jsx b/src/applications/travel-pay/components/submit-flow/pages/MileagePage.jsx index 470312acd8ab..bf35c01b0c99 100644 --- a/src/applications/travel-pay/components/submit-flow/pages/MileagePage.jsx +++ b/src/applications/travel-pay/components/submit-flow/pages/MileagePage.jsx @@ -1,7 +1,8 @@ import React from 'react'; -import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; import PropTypes from 'prop-types'; +import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; + const MileagePage = ({ handlers }) => { return (
diff --git a/src/applications/travel-pay/components/submit-flow/pages/CantFilePage.jsx b/src/applications/travel-pay/components/submit-flow/pages/UnsupportedClaimTypePage.jsx similarity index 63% rename from src/applications/travel-pay/components/submit-flow/pages/CantFilePage.jsx rename to src/applications/travel-pay/components/submit-flow/pages/UnsupportedClaimTypePage.jsx index f73c2a49cbd0..5ff42a7a90b0 100644 --- a/src/applications/travel-pay/components/submit-flow/pages/CantFilePage.jsx +++ b/src/applications/travel-pay/components/submit-flow/pages/UnsupportedClaimTypePage.jsx @@ -1,10 +1,14 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import PropTypes from 'prop-types'; -const CantFilePage = ({ pageIndex, setCantFile, setPageIndex }) => { +const UnsupportedClaimTypePage = ({ + pageIndex, + setIsUnsupportedClaimType, + setPageIndex, +}) => { const onBack = e => { e.preventDefault(); - setCantFile(false); + setIsUnsupportedClaimType(false); setPageIndex(pageIndex); }; @@ -22,10 +26,10 @@ const CantFilePage = ({ pageIndex, setCantFile, setPageIndex }) => { ); }; -CantFilePage.propTypes = { +UnsupportedClaimTypePage.propTypes = { pageIndex: PropTypes.number, - setCantFile: PropTypes.func, + setIsUnsupportedClaimType: PropTypes.func, setPageIndex: PropTypes.func, }; -export default CantFilePage; +export default UnsupportedClaimTypePage; diff --git a/src/applications/travel-pay/components/submit-flow/pages/VehiclePage.jsx b/src/applications/travel-pay/components/submit-flow/pages/VehiclePage.jsx index d097aab9ab4f..4d343d901ccf 100644 --- a/src/applications/travel-pay/components/submit-flow/pages/VehiclePage.jsx +++ b/src/applications/travel-pay/components/submit-flow/pages/VehiclePage.jsx @@ -1,7 +1,8 @@ import React from 'react'; -import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; import PropTypes from 'prop-types'; +import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; + const VehiclePage = ({ handlers }) => { return (
diff --git a/src/applications/travel-pay/constants.js b/src/applications/travel-pay/constants.js index 9ea53d560494..fd2eadec8c25 100644 --- a/src/applications/travel-pay/constants.js +++ b/src/applications/travel-pay/constants.js @@ -1,3 +1,8 @@ +export const BTSSS_PORTAL_URL = 'https://dvagov-btsss.dynamics365portals.us/'; +export const FORM_103542_LINK = '/find-forms/about-form-10-3542/'; +export const FIND_FACILITY_TP_CONTACT_LINK = + '/HEALTHBENEFITS/vtp/beneficiary_travel_pocs.asp'; + export const STATUS_GROUPINGS = [ { name: 'Saved or Incomplete', diff --git a/src/applications/travel-pay/containers/SubmitFlowWrapper.jsx b/src/applications/travel-pay/containers/SubmitFlowWrapper.jsx index 590bc085085c..6acd19411afb 100644 --- a/src/applications/travel-pay/containers/SubmitFlowWrapper.jsx +++ b/src/applications/travel-pay/containers/SubmitFlowWrapper.jsx @@ -11,21 +11,23 @@ import ReviewPage from '../components/submit-flow/pages/ReviewPage'; import ConfirmationPage from '../components/submit-flow/pages/ConfirmationPage'; import BreadCrumbs from '../components/Breadcrumbs'; -import CantFilePage from '../components/submit-flow/pages/CantFilePage'; +import UnsupportedClaimTypePage from '../components/submit-flow/pages/UnsupportedClaimTypePage'; import SubmissionErrorPage from '../components/submit-flow/pages/SubmissionErrorPage'; +import { appointment1 } from '../services/mocks/appointments'; const SubmitFlowWrapper = () => { - const [cantFile, setCantFile] = useState(false); - + // TODO: Placeholders until backend integration is complete + // API call based on the URL Params, but for now is hard coded + const appointment = appointment1; // This will actually be handled by the redux action, but for now it lives here const [isSubmissionError, setIsSubmissionError] = useState(false); const [pageIndex, setPageIndex] = useState(0); + const [isUnsupportedClaimType, setIsUnsupportedClaimType] = useState(false); const handlers = { onNext: e => { e.preventDefault(); - setPageIndex(pageIndex + 1); }, onBack: e => { @@ -49,7 +51,8 @@ const SubmitFlowWrapper = () => { page: 'intro', component: ( { + appointment={appointment} + onStart={e => { e.preventDefault(); setPageIndex(pageIndex + 1); }} @@ -111,15 +114,17 @@ const SubmitFlowWrapper = () => {
- {cantFile && ( - )} {isSubmissionError && } - {!cantFile && !isSubmissionError && pageList[pageIndex].component} + {!isUnsupportedClaimType && + !isSubmissionError && + pageList[pageIndex].component}
diff --git a/src/applications/travel-pay/containers/TravelPayStatusApp.jsx b/src/applications/travel-pay/containers/TravelPayStatusApp.jsx index 3d477d802567..8967813fc8d3 100644 --- a/src/applications/travel-pay/containers/TravelPayStatusApp.jsx +++ b/src/applications/travel-pay/containers/TravelPayStatusApp.jsx @@ -21,7 +21,7 @@ import { toggleLoginModal } from 'platform/site-wide/user-nav/actions'; import BreadCrumbs from '../components/Breadcrumbs'; import TravelClaimCard from '../components/TravelClaimCard'; import TravelPayClaimFilters from '../components/TravelPayClaimFilters'; -import HelpTextContent from '../components/HelpText'; +import { HelpTextManage } from '../components/HelpText'; import { getTravelClaims } from '../redux/actions'; import { getDateFilters } from '../util/dates'; @@ -283,8 +283,18 @@ export default function App({ children }) {

You can use this tool to check the status of your VA travel claims.

- - + + <> + + + {isLoading && ( diff --git a/src/applications/travel-pay/services/mocks/appointments.js b/src/applications/travel-pay/services/mocks/appointments.js new file mode 100644 index 000000000000..15948a1b0bfe --- /dev/null +++ b/src/applications/travel-pay/services/mocks/appointments.js @@ -0,0 +1,417 @@ +export const appointment1 = { + resourceType: 'Appointment', + id: 'aa6bb54b5f8ba22a82720a30abdfa3efe0805cc0dc1b6b248815e942ad61847e', + status: 'booked', + cancelationReason: null, + avsPath: null, + start: '2024-12-30T08:00:00-06:00', + patientComments: null, + timezone: 'America/Denver', + description: 'VAOS_UNKNOWN', + minutesDuration: 15, + location: { + vistaId: '983', + clinicId: '1038', + stationId: '983', + clinicName: 'COVID VACCINE CLIN1', + clinicPhysicalLocation: 'MHC', + clinicPhone: null, + clinicPhoneExtension: null, + }, + videoData: { + isVideo: false, + }, + communityCareProvider: null, + preferredProviderName: null, + practitioners: [ + { + identifier: [ + { + system: 'https://veteran.apps.va.gov/terminology/fhir/sid/secid', + value: null, + }, + ], + name: { + family: 'BERNARDO', + given: ['KENNETH J'], + }, + }, + ], + vaos: { + isPendingAppointment: false, + isUpcomingAppointment: true, + isVideo: false, + isPastAppointment: false, + isCompAndPenAppointment: false, + isCancellable: false, + appointmentType: 'vaAppointment', + isCommunityCare: false, + isExpressCare: false, + isPhoneAppointment: false, + isCOVIDVaccine: true, + apiData: { + id: 'aa6bb54b5f8ba22a82720a30abdfa3efe0805cc0dc1b6b248815e942ad61847e', + identifier: [ + { + system: 'Appointment/', + value: '413938333133353533', + }, + { + system: 'http://www.va.gov/Terminology/VistADefinedTerms/409_84', + value: '983:13553', + }, + ], + kind: 'clinic', + status: 'booked', + serviceType: 'covid', + serviceTypes: [ + { + coding: [ + { + system: + 'http://veteran.apps.va.gov/terminologies/fhir/CodeSystem/vats-service-type', + code: 'covid', + }, + ], + }, + ], + serviceCategory: [ + { + coding: [ + { + system: 'http://www.va.gov/Terminology/VistADefinedTerms/409_1', + code: 'REGULAR', + display: 'REGULAR', + }, + ], + text: 'REGULAR', + }, + ], + patientIcn: '1012846043V576341', + locationId: '983', + clinic: '1038', + practitioners: [ + { + identifier: [ + { + system: 'https://veteran.apps.va.gov/terminology/fhir/sid/secid', + value: null, + }, + ], + name: { + family: 'BERNARDO', + given: ['KENNETH J'], + }, + }, + ], + start: '2024-12-30T14:00:00Z', + end: '2024-08-01T14:15:00Z', + minutesDuration: 15, + slot: { + id: '3230323430383031313430303A323032343038303131343135', + start: '2024-12-01T14:00:00Z', + end: '2024-12-01T14:15:00Z', + }, + created: '2024-12-09T00:00:00Z', + cancellable: false, + extension: { + ccLocation: { + address: {}, + }, + vistaStatus: ['FUTURE'], + preCheckinAllowed: true, + eCheckinAllowed: true, + clinic: { + physicalLocation: 'MHC', + }, + }, + localStartTime: '2024-12-01T08:00:00.000-06:00', + serviceName: 'COVID VACCINE CLIN1', + friendlyName: 'COVID VACCINE CLIN1', + physicalLocation: 'MHC', + location: { + id: '983', + type: 'appointments', + attributes: { + id: '983', + vistaSite: '983', + vastParent: '983', + type: 'va_facilities', + name: 'Cheyenne VA Medical Center', + classification: 'VA Medical Center (VAMC)', + timezone: { + timeZoneId: 'America/Denver', + }, + lat: 39.744507, + long: -104.830956, + website: 'https://www.denver.va.gov/locations/directions.asp', + phone: { + main: '307-778-7550', + fax: '307-778-7381', + pharmacy: '866-420-6337', + afterHours: '307-778-7550', + patientAdvocate: '307-778-7550 x7517', + mentalHealthClinic: '307-778-7349', + enrollmentCoordinator: '307-778-7550 x7579', + }, + physicalAddress: { + type: 'physical', + line: ['2360 East Pershing Boulevard'], + city: 'Cheyenne', + state: 'WY', + postalCode: '82001-5356', + }, + mobile: false, + healthService: [ + 'Audiology', + 'Cardiology', + 'DentalServices', + 'EmergencyCare', + 'Gastroenterology', + 'Gynecology', + 'MentalHealthCare', + 'Nutrition', + 'Ophthalmology', + 'Optometry', + 'Orthopedics', + 'Podiatry', + 'PrimaryCare', + 'SpecialtyCare', + 'UrgentCare', + 'Urology', + 'WomensHealth', + ], + operatingStatus: { + code: 'NORMAL', + }, + }, + }, + }, + timeZone: 'America/Denver', + facilityData: { + resourceType: 'Location', + id: '983', + vistaId: '983', + name: 'Cheyenne VA Medical Center', + identifier: [ + { + system: 'http://med.va.gov/fhir/urn', + value: 'urn:va:division:983:983', + }, + { + system: 'urn:oid:2.16.840.1.113883.6.233', + value: '983', + }, + ], + telecom: [ + { + system: 'phone', + value: '307-778-7550', + }, + ], + position: { + longitude: -104.830956, + latitude: 39.744507, + }, + address: { + line: ['2360 East Pershing Boulevard'], + city: 'Cheyenne', + state: 'WY', + postalCode: '82001-5356', + }, + }, + }, + version: 2, +}; + +export const appointment = { + resourceType: 'Appointment', + id: 'aa6bb54b5f8ba22a82720a30abdfa3efe0805cc0dc1b6b248815e942ad61847e', + status: 'booked', + cancelationReason: null, + start: '2024-11-20T10:30:00-05:00', + patientComments: null, + reasonForAppointment: 'Medication concern', + timezone: 'Asia/Manila', + description: 'VAOS_UNKNOWN', + minutesDuration: 60, + practitioners: [ + { + identifier: [ + { + system: 'https://veteran.apps.va.gov/terminology/fhir/sid/secid', + value: null, + }, + ], + name: { + family: 'BERNARDO', + given: ['KENNETH J'], + }, + }, + ], + location: { + vistaId: '983', + clinicId: '945', + stationId: '983GC', + clinicName: 'C&P BEV AUDIO FTC', + clinicPhysicalLocation: 'FORT COLLINS AUDIO', + clinicPhone: null, + clinicPhoneExtension: null, + }, + videoData: { + isVideo: false, + }, + communityCareProvider: null, + preferredProviderName: null, + vaos: { + isPendingAppointment: false, + isUpcomingAppointment: false, + isVideo: false, + isPastAppointment: true, + isCompAndPenAppointment: true, + isCancellable: false, + appointmentType: 'vaAppointment', + isCommunityCare: false, + isExpressCare: false, + isPhoneAppointment: false, + isCOVIDVaccine: false, + apiData: { + id: '167322', + identifier: [ + { + system: 'Appointment/', + value: '4139383339353233', + }, + ], + kind: 'clinic', + status: 'booked', + serviceType: 'audiology', + serviceTypes: [ + { + coding: [ + { + system: + 'http://veteran.apps.va.gov/terminologies/fhir/CodeSystem/vats-service-type', + code: 'audiology', + }, + ], + }, + ], + serviceCategory: [ + { + coding: [ + { + system: 'http://www.va.gov/Terminology/VistADefinedTerms/409_1', + code: 'COMPENSATION & PENSION', + display: 'COMPENSATION & PENSION', + }, + ], + text: 'COMPENSATION & PENSION', + }, + ], + patientIcn: '1013120826V646496', + locationId: '983GC', + localStartTime: '2024-11-20T10:30:00.000+08:00', + clinic: '945', + start: '2024-11-20T09:30:00Z', + end: '2024-11-20T10:00:00Z', + created: '2024-03-17T00:00:00Z', + cancellable: false, + extension: { + ccLocation: { + address: {}, + }, + vistaStatus: ['NO ACTION TAKEN'], + }, + serviceName: 'FTC C&P AUDIO BEV', + physicalLocation: 'FORT COLLINS AUDIO', + friendlyName: 'C&P BEV AUDIO FTC', + practitioners: [ + { + identifier: [ + { + system: 'https://veteran.apps.va.gov/terminology/fhir/sid/secid', + value: null, + }, + ], + name: { + family: 'BERNARDO', + given: ['KENNETH J'], + }, + }, + ], + location: { + id: '983GC', + type: 'appointments', + attributes: { + id: '983GC', + vistaSite: '983', + vastParent: '983', + type: 'va_facilities', + name: 'Fort Collins VA Clinic', + classification: 'Multi-Specialty CBOC', + timezone: { + timeZoneId: 'Asia/Manila', + }, + lat: 40.553875, + long: -105.08795, + website: + 'https://www.cheyenne.va.gov/locations/Fort_Collins_VA_CBOC.asp', + phone: { + main: '970-224-1550', + }, + physicalAddress: { + line: ['2509 Research Boulevard'], + city: 'Fort Collins', + state: 'CO', + postalCode: '80526-8108', + }, + healthService: [ + 'Audiology', + 'EmergencyCare', + 'MentalHealthCare', + 'PrimaryCare', + 'SpecialtyCare', + ], + }, + }, + claim: { + message: 'No claim for this appointment', + }, + }, + timeZone: 'Asia/Manila', + facilityData: { + resourceType: 'Location', + id: '983GC', + vistaId: '983', + name: 'Fort Collins VA Clinic', + identifier: [ + { + system: 'http://med.va.gov/fhir/urn', + value: 'urn:va:division:983:983GC', + }, + { + system: 'urn:oid:2.16.840.1.113883.6.233', + value: '983GC', + }, + ], + telecom: [ + { + system: 'phone', + value: '970-224-1550', + }, + ], + position: { + longitude: -105.08795, + latitude: 40.553875, + }, + address: { + line: ['2509 Research Boulevard'], + city: 'Fort Collins', + state: 'CO', + postalCode: '80526-8108', + }, + }, + }, + version: 2, +}; diff --git a/src/applications/travel-pay/services/mocks/index.js b/src/applications/travel-pay/services/mocks/index.js index 70a4ec6029d8..eb23a3c3f872 100644 --- a/src/applications/travel-pay/services/mocks/index.js +++ b/src/applications/travel-pay/services/mocks/index.js @@ -1,11 +1,11 @@ const delay = require('mocker-api/lib/delay'); const TOGGLE_NAMES = require('../../../../platform/utilities/feature-toggles/featureFlagNames.json'); -const commonResponses = require('../../../../platform/testing/local-dev-mock-api/common'); const travelClaims = require('./travel-claims-31.json'); +const user = require('./user.json'); const responses = { - ...commonResponses, + 'GET /v0/user': user, 'GET /v0/feature_toggles': { data: { type: 'feature_toggles', diff --git a/src/applications/travel-pay/services/mocks/user.json b/src/applications/travel-pay/services/mocks/user.json new file mode 100644 index 000000000000..e957ddb2ade2 --- /dev/null +++ b/src/applications/travel-pay/services/mocks/user.json @@ -0,0 +1,224 @@ +{ + "data": { + "id": "", + "type": "users_scaffolds", + "attributes": { + "services": [ + "facilities", + "hca", + "edu-benefits", + "form-save-in-progress", + "form-prefill", + "form526", + "user-profile", + "appeals-status", + "id-card", + "identity-proofed", + "vet360", + "lighthouse" + ], + "account": { + "accountUuid": "7d9e2bfb-13ae-45c8-8764-ea3c87cd8af3" + }, + "profile": { + "email": "vets.gov.user+75@gmail.com", + "firstName": "MITCHELL", + "middleName": "G", + "lastName": "JENKINS", + "birthDate": "1949-03-04", + "gender": "M", + "zip": "97063", + "lastSignedIn": "2022-03-24T18:15:06.566Z", + "loa": { + "current": 3, + "highest": 3 + }, + "multifactor": true, + "verified": true, + "signIn": { + "serviceName": "idme", + "accountType": "N/A" + }, + "authnContext": "http://idmanagement.gov/ns/assurance/loa/3", + "claims": { + "ch33BankAccounts": true, + "communicationPreferences": true, + "connectedApps": true, + "militaryHistory": true, + "paymentHistory": true, + "personalInformation": true, + "ratingInfo": true, + "appeals": true, + "medicalCopays": true + }, + "edipi": 3332224445 + }, + "vaProfile": { + "status": "OK", + "birthDate": "19490304", + "familyName": "Jenkins", + "gender": "M", + "givenNames": ["Mitchell", "G"], + "isCernerPatient": false, + "facilities": [ + { + "facilityId": "989", + "isCerner": false + }, + { + "facilityId": "987", + "isCerner": false + }, + { + "facilityId": "983", + "isCerner": false + } + ], + "vaPatient": true, + "mhvAccountState": "NONE" + }, + "veteranStatus": { + "status": "OK", + "isVeteran": true, + "servedInMilitary": true + }, + "inProgressForms": [], + "prefillsAvailable": ["21-686C", "mock-form-ae-design-patterns"], + "vet360ContactInformation": { + "email": { + "createdAt": "2018-04-20T17:24:13.000Z", + "emailAddress": "myemail72585885@unattended.com", + "effectiveEndDate": null, + "effectiveStartDate": "2019-03-07T22:32:40.000Z", + "id": 20648, + "sourceDate": "2019-03-07T22:32:40.000Z", + "sourceSystemUser": null, + "transactionId": "44a0858b-3dd1-4de2-903d-38b147981a9c", + "updatedAt": "2019-03-08T05:09:58.000Z", + "vet360Id": "1273766" + }, + "residentialAddress": { + "addressLine1": "345 Home Address St.", + "addressLine2": null, + "addressLine3": null, + "addressPou": "RESIDENCE/CHOICE", + "addressType": "DOMESTIC", + "city": "San Francisco", + "countryName": "United States", + "countryCodeIso2": "US", + "countryCodeIso3": "USA", + "countryCodeFips": null, + "countyCode": null, + "countyName": null, + "createdAt": "2022-03-21T21:26:35.000Z", + "effectiveEndDate": null, + "effectiveStartDate": "2022-03-23T19:11:51.000Z", + "geocodeDate": "2022-03-23T19:11:51.000Z", + "geocodePrecision": null, + "id": 312003, + "internationalPostalCode": null, + "latitude": 37.781, + "longitude": -122.4605, + "province": null, + "sourceDate": "2022-03-23T19:11:51.000Z", + "sourceSystemUser": null, + "stateCode": "CA", + "transactionId": "c5adb989-3b87-47b6-afe3-dc18800cedc3", + "updatedAt": "2022-03-23T19:11:52.000Z", + "validationKey": null, + "vet360Id": "1273766", + "zipCode": "94118", + "zipCodeSuffix": null, + "badAddress": null + }, + "mailingAddress": { + "addressLine1": "123 Mailing Address St.", + "addressLine2": "Apt 1", + "addressLine3": null, + "addressPou": "CORRESPONDENCE", + "addressType": "DOMESTIC", + "city": "Fulton", + "countryName": "United States", + "countryCodeIso2": "US", + "countryCodeIso3": "USA", + "countryCodeFips": null, + "countyCode": null, + "countyName": null, + "createdAt": "2022-03-21T21:06:15.000Z", + "effectiveEndDate": null, + "effectiveStartDate": "2022-03-23T19:14:59.000Z", + "geocodeDate": "2022-03-23T19:15:00.000Z", + "geocodePrecision": null, + "id": 311999, + "internationalPostalCode": null, + "latitude": 45.2248, + "longitude": -121.3595, + "province": null, + "sourceDate": "2022-03-23T19:14:59.000Z", + "sourceSystemUser": null, + "stateCode": "NY", + "transactionId": "3ea3ecf8-3ddf-46d9-8a4b-b5554385b3fb", + "updatedAt": "2022-03-23T19:15:01.000Z", + "validationKey": null, + "vet360Id": "1273766", + "zipCode": "97063", + "zipCodeSuffix": null, + "badAddress": null + }, + "mobilePhone": { + "areaCode": "619", + "countryCode": "1", + "createdAt": "2022-01-12T16:22:03.000Z", + "extension": null, + "effectiveEndDate": null, + "effectiveStartDate": "2022-02-17T20:15:44.000Z", + "id": 269804, + "isInternational": false, + "isTextable": null, + "isTextPermitted": null, + "isTty": null, + "isVoicemailable": null, + "phoneNumber": "5551234", + "phoneType": "MOBILE", + "sourceDate": "2022-02-17T20:15:44.000Z", + "sourceSystemUser": null, + "transactionId": "fdb13953-f670-4bd3-a3bb-8881eb9165dd", + "updatedAt": "2022-02-17T20:15:45.000Z", + "vet360Id": "1273766" + }, + "homePhone": { + "areaCode": "989", + "countryCode": "1", + "createdAt": "2018-04-20T17:22:56.000Z", + "extension": null, + "effectiveEndDate": null, + "effectiveStartDate": "2022-03-11T16:31:55.000Z", + "id": 2272982, + "isInternational": false, + "isTextable": null, + "isTextPermitted": null, + "isTty": null, + "isVoicemailable": null, + "phoneNumber": "8981233", + "phoneType": "HOME", + "sourceDate": "2022-03-11T16:31:55.000Z", + "sourceSystemUser": null, + "transactionId": "2814cdf6-7f2c-431b-95f3-d37f3837215d", + "updatedAt": "2022-03-11T16:31:56.000Z", + "vet360Id": "1273766" + }, + "workPhone": null, + "temporaryPhone": null, + "faxNumber": null, + "textPermission": null + }, + "session": { + "ssoe": true, + "transactionid": "YEI6t8W3ANsvCT04oB+iXh/UP03PXSFg3Y36L2QaxLE=" + } + } + }, + "meta": { + "errors": null + } +} diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/AddressPage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/AddressPage.unit.spec.jsx new file mode 100644 index 000000000000..4248b256763b --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/AddressPage.unit.spec.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import AddressPage from '../../../../components/submit-flow/pages/AddressPage'; + +it('should render with back and continue buttons', () => { + const props = { + handlers: { + onBack: () => {}, + onNext: () => {}, + }, + }; + const screen = render(); + + expect(screen.getByText('Address page')).to.exist; + expect($('va-button-pair')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/ConfirmationPage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/ConfirmationPage.unit.spec.jsx new file mode 100644 index 000000000000..9842e0216c6d --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/ConfirmationPage.unit.spec.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; + +import ConfirmationPage from '../../../../components/submit-flow/pages/ConfirmationPage'; + +it('should render', () => { + const screen = render(); + + expect(screen.getByText('We’re processing your travel reimbursement claim')) + .to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/IntroductionPage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/IntroductionPage.unit.spec.jsx new file mode 100644 index 000000000000..c34d52411bde --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/IntroductionPage.unit.spec.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import IntroductionPage from '../../../../components/submit-flow/pages/IntroductionPage'; + +const appointment = require('../../../fixtures/appointment.json'); + +it('should render with link to file a claim', () => { + const props = { + appointment, + onNext: () => {}, + }; + const screen = render(); + + expect(screen.getByText('File a travel reimbursement claim')).to.exist; + expect( + screen.getByText( + /Monday, December 30, 2024 at Cheyenne VA Medical Center/i, + ), + ).to.exist; + expect($('va-link-action[text="File a mileage only claim"]')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/MileagePage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/MileagePage.unit.spec.jsx new file mode 100644 index 000000000000..9edccecc810c --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/MileagePage.unit.spec.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import MileagePage from '../../../../components/submit-flow/pages/MileagePage'; + +it('should render with back and continue buttons', () => { + const props = { + handlers: { + onBack: () => {}, + onNext: () => {}, + }, + }; + const screen = render(); + + expect(screen.getByText('Mileage page')).to.exist; + expect($('va-button-pair')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/ReviewPage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/ReviewPage.unit.spec.jsx new file mode 100644 index 000000000000..c46b4ffdcb60 --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/ReviewPage.unit.spec.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import ReviewPage from '../../../../components/submit-flow/pages/ReviewPage'; + +it('should render with back and submit buttons', () => { + const props = { + handlers: { + onBack: () => {}, + onSubmit: () => {}, + }, + }; + const screen = render(); + + expect(screen.getByText('Review your travel claim')).to.exist; + expect($('va-button[text="Back"')).to.exist; + expect($('va-button[text="Submit"')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx new file mode 100644 index 000000000000..113115f944e9 --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; + +import SubmissionErrorPage from '../../../../components/submit-flow/pages/SubmissionErrorPage'; + +it('should render with back button', () => { + const screen = render(); + + expect(screen.getByText('We couldn’t file your claim')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/UnsupportedClaimTypePage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/UnsupportedClaimTypePage.unit.spec.jsx new file mode 100644 index 000000000000..af5afdbb0471 --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/UnsupportedClaimTypePage.unit.spec.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import UnsupportedClaimTypePage from '../../../../components/submit-flow/pages/UnsupportedClaimTypePage'; + +it('should render with back button', () => { + const props = { + pageIndex: 2, + setIsUnsupportedClaimType: () => {}, + setPageIndex: () => {}, + }; + const screen = render(); + + expect( + screen.getByText('We can’t file this type of travel reimbursement claim'), + ).to.exist; + expect($('va-button[text="Back"]')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/VehiclePage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/VehiclePage.unit.spec.jsx new file mode 100644 index 000000000000..45c4fbabd4b5 --- /dev/null +++ b/src/applications/travel-pay/tests/components/submit-flow/pages/VehiclePage.unit.spec.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { expect } from 'chai'; +import { render } from '@testing-library/react'; +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import VehiclePage from '../../../../components/submit-flow/pages/VehiclePage'; + +it('should render with back and continue buttons', () => { + const props = { + handlers: { + onBack: () => {}, + onNext: () => {}, + }, + }; + const screen = render(); + + expect(screen.getByText('Vehicle page')).to.exist; + expect($('va-button-pair')).to.exist; +}); diff --git a/src/applications/travel-pay/tests/e2e/submit-claim.cypress.spec.js b/src/applications/travel-pay/tests/e2e/submit-claim.cypress.spec.js new file mode 100644 index 000000000000..be74185704eb --- /dev/null +++ b/src/applications/travel-pay/tests/e2e/submit-claim.cypress.spec.js @@ -0,0 +1,53 @@ +/* eslint-disable @department-of-veterans-affairs/axe-check-required */ +import { rootUrl } from '../../manifest.json'; +import user from '../fixtures/user.json'; +import ApiInitializer from './utilities/ApiInitializer'; + +describe('Submit Mileage Only Claims', () => { + beforeEach(() => { + cy.intercept('/data/cms/vamc-ehr.json', {}); + ApiInitializer.initializeFeatureToggle.withAllFeatures(); + cy.login(user); + cy.visit(`${rootUrl}/file-new-claim/12345`); + cy.injectAxeThenAxeCheck(); + }); + + it('defaults to the Introduction page', () => { + cy.get('h1').should('include.text', 'File a travel reimbursement claim'); + }); + + it('should navigate through the flow', () => { + cy.get('va-link-action[text="File a mileage only claim"]') + .first() + .click(); + + // Mileage question should be first + cy.get('h1').should('include.text', 'Mileage page'); + + // Click the "Continue" button + cy.selectVaButtonPairPrimary(); + + // Then navigate to the Vehicle question + cy.get('h1').should('include.text', 'Vehicle page'); + + // Click the "Continue" button + cy.selectVaButtonPairPrimary(); + + // Then navigate to the Address question + cy.get('h1').should('include.text', 'Address page'); + + // Click the "Continue" button + cy.selectVaButtonPairPrimary(); + + // Then navigate to the Review page + cy.get('h1').should('include.text', 'Review your travel claim'); + + // Click the "Submit" button + cy.get('va-button[text="Submit"]') + .first() + .click(); + + // Submission Error page is currently the hard-coded default behavior + cy.get('h1').should('include.text', 'We couldn’t file your claim'); + }); +}); diff --git a/src/applications/travel-pay/tests/e2e/travel-pay.cypress.spec.js b/src/applications/travel-pay/tests/e2e/travel-pay.cypress.spec.js index bee9f711e032..400b19b69c05 100644 --- a/src/applications/travel-pay/tests/e2e/travel-pay.cypress.spec.js +++ b/src/applications/travel-pay/tests/e2e/travel-pay.cypress.spec.js @@ -81,7 +81,7 @@ describe(`${appName} -- Status Page`, () => { .first() .click(); - cy.get('a[data-testid="status-explainer-link"]') + cy.get('va-link[data-testid="status-explainer-link"]') .first() .click(); diff --git a/src/applications/travel-pay/tests/e2e/utilities/ApiInitializer.js b/src/applications/travel-pay/tests/e2e/utilities/ApiInitializer.js index eb5b5abaa910..e9c920c783bf 100644 --- a/src/applications/travel-pay/tests/e2e/utilities/ApiInitializer.js +++ b/src/applications/travel-pay/tests/e2e/utilities/ApiInitializer.js @@ -9,6 +9,7 @@ class ApiInitializer { features: [ { name: 'travel_pay_power_switch', value: true }, { name: 'travel_pay_view_claim_details', value: true }, + { name: 'travel_pay_submit_mileage_expense', value: true }, ], }, }).as('featureToggles'); diff --git a/src/applications/travel-pay/tests/fixtures/appointment.json b/src/applications/travel-pay/tests/fixtures/appointment.json new file mode 100644 index 000000000000..3c61dd33a308 --- /dev/null +++ b/src/applications/travel-pay/tests/fixtures/appointment.json @@ -0,0 +1,98 @@ +{ + "vaos": { + "apiData": { + "practitioners": [ + { + "identifier": [ + { + "system": "https://veteran.apps.va.gov/terminology/fhir/sid/secid", + "value": null + } + ], + "name": { + "family": "BERNARDO", + "given": ["KENNETH J"] + } + } + ], + "start": "2024-12-30T14:00:00Z", + "localStartTime": "2024-12-01T08:00:00.000-06:00", + "serviceName": "COVID VACCINE CLIN1", + "friendlyName": "COVID VACCINE CLIN1", + "physicalLocation": "MHC", + "location": { + "id": "983", + "type": "appointments", + "attributes": { + "id": "983", + "vistaSite": "983", + "vastParent": "983", + "type": "va_facilities", + "name": "Cheyenne VA Medical Center", + "classification": "VA Medical Center (VAMC)", + "timezone": { + "timeZoneId": "America/Denver" + }, + "lat": 39.744507, + "long": -104.830956, + "website": "https://www.denver.va.gov/locations/directions.asp", + "phone": { + "main": "307-778-7550", + "fax": "307-778-7381", + "pharmacy": "866-420-6337", + "afterHours": "307-778-7550", + "patientAdvocate": "307-778-7550 x7517", + "mentalHealthClinic": "307-778-7349", + "enrollmentCoordinator": "307-778-7550 x7579" + }, + "physicalAddress": { + "type": "physical", + "line": ["2360 East Pershing Boulevard"], + "city": "Cheyenne", + "state": "WY", + "postalCode": "82001-5356" + }, + "mobile": false, + "healthService": [], + "operatingStatus": { + "code": "NORMAL" + } + } + }, + "timeZone": "America/Denver", + "facilityData": { + "resourceType": "Location", + "id": "983", + "vistaId": "983", + "name": "Cheyenne VA Medical Center", + "identifier": [ + { + "system": "http://med.va.gov/fhir/urn", + "value": "urn:va:division:983:983" + }, + { + "system": "urn:oid:2.16.840.1.113883.6.233", + "value": "983" + } + ], + "telecom": [ + { + "system": "phone", + "value": "307-778-7550" + } + ], + "position": { + "longitude": -104.830956, + "latitude": 39.744507 + }, + "address": { + "line": ["2360 East Pershing Boulevard"], + "city": "Cheyenne", + "state": "WY", + "postalCode": "82001-5356" + } + } + }, + "version": 2 + } +} diff --git a/src/applications/travel-pay/tests/fixtures/user.json b/src/applications/travel-pay/tests/fixtures/user.json index e0fedaff4cf7..e73a34a8c501 100644 --- a/src/applications/travel-pay/tests/fixtures/user.json +++ b/src/applications/travel-pay/tests/fixtures/user.json @@ -40,9 +40,7 @@ "birthDate": "19320205", "familyName": "Allen", "gender": "M", - "givenNames": [ - "Hector" - ], + "givenNames": ["Hector"], "isCernerPatient": false, "facilities": [ { @@ -94,7 +92,40 @@ ], "vet360ContactInformation": { "email": null, - "residentialAddress": null, + "residentialAddress": { + "addressLine1": "345 Home Address St.", + "addressLine2": null, + "addressLine3": null, + "addressPou": "RESIDENCE/CHOICE", + "addressType": "DOMESTIC", + "city": "San Francisco", + "countryName": "United States", + "countryCodeIso2": "US", + "countryCodeIso3": "USA", + "countryCodeFips": null, + "countyCode": null, + "countyName": null, + "createdAt": "2022-03-21T21:26:35.000Z", + "effectiveEndDate": null, + "effectiveStartDate": "2022-03-23T19:11:51.000Z", + "geocodeDate": "2022-03-23T19:11:51.000Z", + "geocodePrecision": null, + "id": 312003, + "internationalPostalCode": null, + "latitude": 37.781, + "longitude": -122.4605, + "province": null, + "sourceDate": "2022-03-23T19:11:51.000Z", + "sourceSystemUser": null, + "stateCode": "CA", + "transactionId": "c5adb989-3b87-47b6-afe3-dc18800cedc3", + "updatedAt": "2022-03-23T19:11:52.000Z", + "validationKey": null, + "vet360Id": "1273766", + "zipCode": "94118", + "zipCodeSuffix": null, + "badAddress": null + }, "mailingAddress": null, "mobilePhone": null, "homePhone": { diff --git a/src/applications/travel-pay/tests/util/dates.spec.js b/src/applications/travel-pay/tests/util/dates.spec.js index de714c51139c..2c6a9ba3c150 100644 --- a/src/applications/travel-pay/tests/util/dates.spec.js +++ b/src/applications/travel-pay/tests/util/dates.spec.js @@ -1,6 +1,6 @@ import MockDate from 'mockdate'; import { expect } from 'chai'; -import { getDateFilters, formatDateTime } from '../../util/dates'; +import { getDateFilters, formatDateTime, getDaysLeft } from '../../util/dates'; function formatDateRange(dateRange) { const [startDay, startTime] = formatDateTime(dateRange.start); @@ -205,3 +205,27 @@ describe('getDateFilters', () => { expect(dateRanges.length).to.eq(6); }); }); + +describe('getDaysLeft', () => { + afterEach(() => { + MockDate.reset(); + }); + + it('returns 10 for a date 20 days ago', () => { + MockDate.set('2024-06-25T15:00:00Z'); + const actual = getDaysLeft('2024-06-05T14:00:00Z'); + expect(actual).to.eq(10); + }); + + it('returns 30 for an appointment on the day filed', () => { + MockDate.set('2024-06-25T15:00:00Z'); + const actual = getDaysLeft('2024-06-25T14:00:00Z'); + expect(actual).to.eq(30); + }); + + it('returns 0 for a date more than 30 days ago', () => { + MockDate.set('2024-06-25T14:00:00Z'); + const actual = getDaysLeft('2024-05-05T14:00:00Z'); + expect(actual).to.eq(0); + }); +}); diff --git a/src/applications/travel-pay/util/dates.js b/src/applications/travel-pay/util/dates.js index e2c325aca36b..c6496fd9462a 100644 --- a/src/applications/travel-pay/util/dates.js +++ b/src/applications/travel-pay/util/dates.js @@ -1,4 +1,5 @@ import { + differenceInCalendarDays, endOfQuarter, endOfYear, format, @@ -74,3 +75,10 @@ export function getDateFilters() { return dateRanges; } + +export function getDaysLeft(datetimeString) { + const apptDate = new Date(datetimeString); + const daysSinceAppt = differenceInCalendarDays(new Date(), apptDate); + + return daysSinceAppt > 30 ? 0 : 30 - daysSinceAppt; +}