-
Notifications
You must be signed in to change notification settings - Fork 128
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
Adds initial mhv-supply-reordering application #33224
Changes from all commits
e7f37be
e1ef873
2fc3d8d
6f0f61d
1640844
a320df0
3192cb4
7c05b66
8327774
256a65d
9ecef23
e67788c
217d6ec
b94d005
7c96130
bc02c77
e626448
af807be
f62ece4
ae5e379
46080d3
9a0848c
5a6bab3
26bafcd
f4b9e2e
eb480c3
03e8cc3
cb3b951
4a08315
d2afa71
2073076
716cb3f
8896ca5
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,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. |
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)); | ||
}); | ||
}; |
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, | ||
}); |
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'; | ||
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. really like these clean little components |
||
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; |
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 }) => { | ||
Check warning on line 26 in src/applications/mhv-supply-reordering/components/EditAddress.jsx GitHub Actions / Linting (Files Changed)
Check warning on line 26 in src/applications/mhv-supply-reordering/components/EditAddress.jsx GitHub Actions / Linting (Files Changed)
|
||
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; |
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.
I love this thorough README and the docs references!