Skip to content

Commit

Permalink
Adds initial mhv-supply-reordering application (#33224)
Browse files Browse the repository at this point in the history
* Adds initial mhv-supply-reorder form app
* Adds mocks
* Adds alerts
* Moves breadcrumbs to IntroductionPage
* Sets useTopBackLink: true in formConfig (large continue button)
* Show subtitle on IntroductionPage, only
* Sets 'Finish this order later' content for save link
* Removes margin from paragraph in informative va-alert
* Sets Select supplies as first page
* Sets supply count, 'No supplies selected/found' text
* Adds formatDate helper
* Adds checkboxes for selecting supplies
* Adds product description
* Updates readme
* No e2e specs, yet
* Adds unit specs
  • Loading branch information
radavis authored Dec 3, 2024
1 parent 73100ed commit cd0d6bc
Show file tree
Hide file tree
Showing 57 changed files with 2,318 additions and 0 deletions.
100 changes: 100 additions & 0 deletions src/applications/mhv-supply-reordering/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# mhv-supply-reordering

## Background Info

About: This app provides an interface to re-order Hearing Aid and Sleep Apnea accessories
Slack Channel: [#va-cto-supply-reordering](https://dsva.slack.com/archives/C05DFSM57FW/p1689711688225089)

## App

Form app generated with `yarn new:app`. Changes to the following files were reverted, since `VA_FORM_IDS.FORM_VA_2346A` already exists.

- `src/platform/forms/constants.js`
- `src/platform/forms/tests/forms.unit.spec.js`

## Quick start to get running locally

Before you get started check [this page](https://depo-platform-documentation.scrollhelp.site/developer-docs/setting-up-your-local-frontend-environment) first to make sure you are setup to use the correct version of Node and Yarn.

- clone vets-website repo `git clone [email protected]:department-of-veterans-affairs/vets-website.git`
- run `yarn install`
- turn on local mocks `yarn mock-api --responses src/applications/mhv-supply-reordering/mocks/index.js`
- start app `yarn watch --env entry=mhv-supply-reordering`
- Run this in your browser console to simulate being logged in `localStorage.setItem('hasSession', true);`
- visit the app: `http://localhost:3001/my-health/order-supplies`

Note: The application fetches supply data from `/v0/in_progress_forms/mdot`. This endpoint is mocked in the local development environment.

## Running tests

Unit tests can be run using this command: `yarn test:unit --app-folder mhv-supply-reordering`. To get detailed errors, run this command with `--log-level=error`. To get coverage reports run this command `yarn test:unit --app-folder mhv-supply-reordering --coverage --coverage-html`. View the report at `/coverage/index.html`

Cypress tests can be run with the GUI using this command: `yarn cy:open`. From there you can filter by `mhv-supply-reordering` to run end to end tests for this app.

Run Cypress from command line:

- Run all `yarn cy:run --spec "src/applications/mhv-supply-reordering/**/**/*"`
- Specify browser `-b electron`

## VA Forms - Web Component Fields and Patterns

[[docs](https://depo-platform-documentation.scrollhelp.site/developer-docs/va-forms-library-web-component-fields-and-patterns)]

[[examples](https://staging.va.gov/mock-form-patterns/introduction)]

A web-component-field is a design system web component for use in forms. These can be found at `src/platform/forms-system/src/js/web-component-fields`.

A web-component-pattern is a group of web-component-fields that can span one or more pages (e.g. - a multi-page form). These can be found at `src/platform/forms-system/src/js/web-component-patterns`.

## Form Flow

- IntroductionPage
- ChooseSupplies
- ContactInformation (optionally: EditEmail, EditAddress)
- ReviewPage
- ConfirmationPage

## API Responses

[[mocker-api](https://github.com/jaywcjlove/mocker-api/tree/v2.9.0?tab=readme-ov-file#usage)]

[[vets-api OpenAPI documentation](https://department-of-veterans-affairs.github.io/va-digital-services-platform-docs/api-reference/#/in_progress_forms)]

`GET /v0/in_progress_forms/MDOT` returns the following... (note the lack of a `data` property)

```json
{
"formData": {
"fullName": {},
"permanentAddress": {},
"temporaryAddress": {},
"ssnLastFour": "",
"gender": "",
"vetEmail": "",
"dateOfBirth": "",
"eligibility": {},
"supplies": []
},
"metadata": {
"version": 0,
"prefill": true,
"returnUrl": ""
}
}
```

When requesting `GET /v0/in_progress_forms/MDOT`, the MDOT client in vets-api will make a request to the system of record for veteran details and supplies available to the veteran. See `V0::InProgressFormsController.camelized_prefill_for_user` and `FormProfiles::MDOT#prefill`. On the front-end, test against the possible responses for `MDOT::Client.new(user).get_supplies` which are mapped to `mdot.exceptions` values in `vets-api/config/locales/exceptions.en.yml` and then passed along in the response. Also, see `vets-api/spec/support/vcr_cassettes/mdot/get_supplies*.yml`.

## Dynamic Form Fields

[[using update and replace schema funcs](https://depo-platform-documentation.scrollhelp.site/developer-docs/va-forms-library-how-to-use-updateschema-and-repla)]

see `src/applications/disability-benefits/all-claims/pages/toxicExposure/toxicExposureConditions.js` for an example.

## Device Types, Device Names

How do we access other device types? (e.g. - assistive devices, nebulizers). Are these included in the request for supplies?

The `productGroup` property of a supply can be one of the following values: `['accessories', 'batteries', 'apnea']`. `'assistive devices'` will be added to this list in the near future.

The `deviceName` property of a supply indicates the associated device for the supply.
13 changes: 13 additions & 0 deletions src/applications/mhv-supply-reordering/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
GET_MDOT_IN_PROGRESS_FORM_STARTED,
GET_MDOT_IN_PROGRESS_FORM_SUCCEEDED,
GET_MDOT_IN_PROGRESS_FORM_FAILED,
getMdotInProgressForm,
} from './mdotInProgressForm';

export {
GET_MDOT_IN_PROGRESS_FORM_STARTED,
GET_MDOT_IN_PROGRESS_FORM_SUCCEEDED,
GET_MDOT_IN_PROGRESS_FORM_FAILED,
getMdotInProgressForm,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { apiRequest } from 'platform/utilities/api';

export const GET_MDOT_IN_PROGRESS_FORM_STARTED =
'GET_MDOT_IN_PROGRESS_FORM_STARTED';
export const GET_MDOT_IN_PROGRESS_FORM_SUCCEEDED =
'GET_MDOT_IN_PROGRESS_FORM_SUCCEEDED';
export const GET_MDOT_IN_PROGRESS_FORM_FAILED =
'GET_MDOT_IN_PROGRESS_FORM_FAILED';

const getMdotInProgressFormStarted = () => ({
type: GET_MDOT_IN_PROGRESS_FORM_STARTED,
});

const getMdotInProgressFormSucceeded = payload => ({
type: GET_MDOT_IN_PROGRESS_FORM_SUCCEEDED,
payload,
});

const getMdotInProgressFormFailed = payload => ({
type: GET_MDOT_IN_PROGRESS_FORM_FAILED,
payload,
});

const MDOT_IN_PROGRESS_FORM_PATH = '/in_progress_forms/MDOT';

export const getMdotInProgressForm = () => async dispatch => {
await dispatch(getMdotInProgressFormStarted());
return apiRequest(MDOT_IN_PROGRESS_FORM_PATH)
.then(payload => {
return dispatch(getMdotInProgressFormSucceeded(payload));
})
.catch(err => {
return dispatch(getMdotInProgressFormFailed(err));
});
};
15 changes: 15 additions & 0 deletions src/applications/mhv-supply-reordering/app-entry.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import '@department-of-veterans-affairs/platform-polyfills';
import './sass/mhv-supply-reordering.scss';

import { startAppFromIndex } from '@department-of-veterans-affairs/platform-startup/exports';

import routes from './routes';
import reducer from './reducers';
import manifest from './manifest.json';

startAppFromIndex({
entryName: manifest.entryName,
url: manifest.rootUrl,
reducer,
routes,
});
22 changes: 22 additions & 0 deletions src/applications/mhv-supply-reordering/components/Breadcrumbs.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { VaBreadcrumbs } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import manifest from '../manifest.json';

const breadcrumbList = [
{
href: '/',
label: 'VA.gov Home',
},
{
href: '/my-health',
label: 'Health care',
},
{
href: manifest.rootUrl,
label: manifest.appName,
},
];

const Breadcrumbs = () => <VaBreadcrumbs breadcrumbList={breadcrumbList} />;

export default Breadcrumbs;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';
import { DLC_EMAIL } from '../constants';

const DlcEmailLink = () => <a href={`mailto:${DLC_EMAIL}`}>{DLC_EMAIL}</a>;

export default DlcEmailLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts';
import { DLC_TELEPHONE } from '../constants';

const DlcTelephoneLink = () => (
<>
<va-telephone contact={DLC_TELEPHONE} /> (
<va-telephone contact={CONTACTS['711']} tty />)
</>
);

export default DlcTelephoneLink;
154 changes: 154 additions & 0 deletions src/applications/mhv-supply-reordering/components/EditAddress.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React, { useState } from 'react';
import {
VaCheckbox,
VaSelect,
VaTextInput,
VaButton,
} from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import constants from 'vets-json-schema/dist/constants.json';
import UnsavedFieldNote from './UnsavedFieldNote';

const COUNTRY_VALUES = constants.countries.map(country => country.value);
const COUNTRY_NAMES = constants.countries.map(country => country.label);

const MILITARY_STATE_VALUES = constants.militaryStates.map(
state => state.value,
);

// filtered States that include US territories
const filteredStates = constants.states.USA.filter(
state => !MILITARY_STATE_VALUES.includes(state.value),
);

const STATE_VALUES = filteredStates.map(state => state.value);
const STATE_NAMES = filteredStates.map(state => state.label);

const EditAddress = ({ data, goToPath, setFormData }) => {
const [address, setAddress] = useState(data.permanentAddress || {});

const handleSubmit = event => {
event.preventDefault();
setFormData({ ...data, permanentAddress: address });
goToPath('/contact-information');
};

const handleInputChange = event => {
const { name, value } = event.target;
setAddress(prevAddress => ({ ...prevAddress, [name]: value }));
};

return (
<div>
<h2>Order medical supplies</h2>
<h3>Contact information</h3>
<UnsavedFieldNote fieldName="mailing address" />
<form onSubmit={handleSubmit}>
<VaCheckbox
label="I live on a U.S. military base outside of the United States."
onVaChange={e =>
setAddress(prevAddress => ({
...prevAddress,
isMilitary: e.detail.checked,
}))
}
checked={address.isMilitary}
/>

<VaSelect
label="Country (Required)"
name="country"
value={address.country}
onVaSelect={e =>
handleInputChange({
target: { name: 'country', value: e.detail.value },
})
}
required
>
{COUNTRY_VALUES.map((value, index) => (
<option key={value} value={value}>
{COUNTRY_NAMES[index]}
</option>
))}
</VaSelect>

<VaTextInput
label="Street address (Required)"
name="street"
value={address.street}
onVaInput={e =>
handleInputChange({
target: { name: 'street', value: e.target.value },
})
}
required
/>

<VaTextInput
label="Street address line 2"
name="street2"
value={address.street2}
onVaInput={e =>
handleInputChange({
target: { name: 'street2', value: e.target.value },
})
}
/>

<VaTextInput
label="City (Required)"
name="city"
value={address.city}
onVaInput={e =>
handleInputChange({
target: { name: 'city', value: e.target.value },
})
}
required
/>

<VaSelect
label="State (Required)"
name="state"
value={address.state}
onVaSelect={e =>
handleInputChange({
target: { name: 'state', value: e.detail.value },
})
}
required
>
{STATE_VALUES.map((value, index) => (
<option key={value} value={value}>
{STATE_NAMES[index]}
</option>
))}
</VaSelect>

<VaTextInput
label="Postal code (Required)"
name="postalCode"
value={address.postalCode}
onVaInput={e =>
handleInputChange({
target: { name: 'postalCode', value: e.target.value },
})
}
required
/>

<div>
<VaButton text="Update" onClick={handleSubmit} uswds />
<VaButton
text="Cancel"
secondary
onClick={() => goToPath('/contact-information')}
uswds
/>
</div>
</form>
</div>
);
};

export default EditAddress;
Loading

0 comments on commit cd0d6bc

Please sign in to comment.