diff --git a/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx b/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx index 76413e6c7b58..6c1052adab4a 100644 --- a/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx +++ b/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { useHistory, useLocation } from 'react-router-dom'; import { startOfMonth, format, addMinutes, isWithinInterval } from 'date-fns'; import { zonedTimeToUtc } from 'date-fns-tz'; @@ -11,10 +11,14 @@ import { referral } from './temp-data/referral'; import { getSelectedDate } from '../new-appointment/redux/selectors'; import { selectUpcomingAppointments } from '../appointment-list/redux/selectors'; import { routeToNextReferralPage, routeToPreviousReferralPage } from './flow'; -import { setFormCurrentPage } from './redux/actions'; -import { selectCurrentPage } from './redux/selectors'; +import { setFormCurrentPage, fetchProviderDetails } from './redux/actions'; +import { selectCurrentPage, getProviderInfo } from './redux/selectors'; +import { FETCH_STATUS } from '../utils/constants'; +import { scrollAndFocus } from '../utils/scrollAndFocus'; export const ChooseDateAndTime = () => { + const dispatch = useDispatch(); + const history = useHistory(); const selectedDate = useSelector(state => getSelectedDate(state)); const upcomingAppointments = useSelector(state => @@ -22,7 +26,6 @@ export const ChooseDateAndTime = () => { ); const location = useLocation(); const currentPage = useSelector(selectCurrentPage); - const dispatch = useDispatch(); const startMonth = format(startOfMonth(referral.preferredDate), 'yyyy-MM'); const [error, setError] = useState(''); const pageTitle = 'Schedule an appointment with your provider'; @@ -55,6 +58,23 @@ export const ChooseDateAndTime = () => { [dispatch], ); + const { provider, providerFetchStatus } = useSelector( + state => getProviderInfo(state), + shallowEqual, + ); + + useEffect( + () => { + if (providerFetchStatus === FETCH_STATUS.notStarted) { + dispatch(fetchProviderDetails(referral.provider)); + } else if (providerFetchStatus === FETCH_STATUS.succeeded) { + scrollAndFocus('h1'); + } else if (providerFetchStatus === FETCH_STATUS.failed) { + scrollAndFocus('h2'); + } + }, + [dispatch, providerFetchStatus], + ); useEffect( () => { dispatch(setFormCurrentPage('scheduleAppointment')); @@ -142,6 +162,28 @@ export const ChooseDateAndTime = () => { }, [hasConflict, selectedDate, upcomingAppointments], ); + if ( + providerFetchStatus === FETCH_STATUS.loading || + providerFetchStatus === FETCH_STATUS.notStarted + ) { + return ( +
+ +
+ ); + } + + if (providerFetchStatus === FETCH_STATUS.failed) { + return ( + +

"We’re sorry. We’ve run into a problem"

+

+ We’re having trouble getting your upcoming appointments. Please try + again later. +

+
+ ); + } return ( <> @@ -152,29 +194,29 @@ export const ChooseDateAndTime = () => { appointment online with this provider:

- {referral.providerName} + {provider.providerName}

{referral.typeOfCare}

- {referral.orgName} + {provider.orgName}

- {referral.orgAddress.street1}
- {referral.orgAddress.street2 && ( + {provider.orgAddress.street1}
+ {provider.orgAddress.street2 && ( <> - {referral.orgAddress.street2} + {provider.orgAddress.street2}
)} - {referral.orgAddress.street3 && ( + {provider.orgAddress.street3 && ( <> - {referral.orgAddress.street3} + {provider.orgAddress.street3}
)} - {referral.orgAddress.city}, {referral.orgAddress.state},{' '} - {referral.orgAddress.zip} + {provider.orgAddress.city}, {provider.orgAddress.state},{' '} + {provider.orgAddress.zip}

{ @@ -198,9 +240,9 @@ export const ChooseDateAndTime = () => {
-

Phone: {referral.orgPhone}

+

Phone: {provider.orgPhone}

- {referral.driveTime} ({referral.driveDistance}) + {provider.driveTime} ({provider.driveDistance})

Choose a date and time

@@ -211,7 +253,7 @@ export const ChooseDateAndTime = () => {

{ + try { + dispatch({ + type: FETCH_PROVIDER_DETAILS, + }); + const providerDetails = await getProviderById(id); + + dispatch({ + type: FETCH_PROVIDER_DETAILS_SUCCEEDED, + data: providerDetails, + }); + return providerDetails; + } catch (error) { + dispatch({ + type: FETCH_PROVIDER_DETAILS_FAILED, + }); + return captureError(error); + } + }; +} diff --git a/src/applications/vaos/referral-appointments/redux/reducers.js b/src/applications/vaos/referral-appointments/redux/reducers.js index d0541f59aac6..dc15f7eb9cc3 100644 --- a/src/applications/vaos/referral-appointments/redux/reducers.js +++ b/src/applications/vaos/referral-appointments/redux/reducers.js @@ -4,13 +4,18 @@ import { SET_SORT_PROVIDER_BY, SET_SELECTED_PROVIDER, SET_FORM_CURRENT_PAGE, + FETCH_PROVIDER_DETAILS, + FETCH_PROVIDER_DETAILS_FAILED, + FETCH_PROVIDER_DETAILS_SUCCEEDED, } from './actions'; +import { FETCH_STATUS } from '../../utils/constants'; const initialState = { facility: null, sortProviderBy: '', selectedProvider: '', currentPage: null, + providerFetchStatus: FETCH_STATUS.notStarted, }; function ccAppointmentReducer(state = initialState, action) { @@ -41,6 +46,22 @@ function ccAppointmentReducer(state = initialState, action) { ...state, currentPage: action.payload, }; + case FETCH_PROVIDER_DETAILS: + return { + ...state, + providerFetchStatus: FETCH_STATUS.loading, + }; + case FETCH_PROVIDER_DETAILS_SUCCEEDED: + return { + ...state, + providerFetchStatus: FETCH_STATUS.succeeded, + selectedProvider: action.data, + }; + case FETCH_PROVIDER_DETAILS_FAILED: + return { + ...state, + providerFetchStatus: FETCH_STATUS.failed, + }; default: return state; } diff --git a/src/applications/vaos/referral-appointments/redux/selectors.js b/src/applications/vaos/referral-appointments/redux/selectors.js index 4d14355a7fc7..2acca535c69e 100644 --- a/src/applications/vaos/referral-appointments/redux/selectors.js +++ b/src/applications/vaos/referral-appointments/redux/selectors.js @@ -2,3 +2,10 @@ export const selectCCAppointment = state => state.ccAppointment; export const selectProvider = state => state.referral.selectedProvider; export const selectProviderSortBy = state => state.referral.sortProviderBy; export const selectCurrentPage = state => state.referral.currentPage; + +export function getProviderInfo(state) { + return { + provider: state.referral.selectedProvider, + providerFetchStatus: state.referral.providerFetchStatus, + }; +} diff --git a/src/applications/vaos/referral-appointments/temp-data/referral.js b/src/applications/vaos/referral-appointments/temp-data/referral.js index dfa0fbf9c4cf..049bf0383efb 100644 --- a/src/applications/vaos/referral-appointments/temp-data/referral.js +++ b/src/applications/vaos/referral-appointments/temp-data/referral.js @@ -19,6 +19,7 @@ const getAvailableSlots = (number = 2) => { const referral = { id: 123456, providerName: 'Dr. Face', + provider: '111', typeOfCare: 'Dermatology', appointmentCount: 2, orgName: 'New Skin Technologies', diff --git a/src/applications/vaos/referral-appointments/tests/utils/provider.unit.spec.js b/src/applications/vaos/referral-appointments/tests/utils/provider.unit.spec.js new file mode 100644 index 000000000000..a3df3f73934c --- /dev/null +++ b/src/applications/vaos/referral-appointments/tests/utils/provider.unit.spec.js @@ -0,0 +1,26 @@ +import { addDays, startOfDay, addHours } from 'date-fns'; +import { expect } from 'chai'; + +const providerUtil = require('../../utils/provider'); + +describe('VAOS provider generator', () => { + const tomorrow = addDays(startOfDay(new Date()), 1); + describe('createProviderDetails', () => { + const providerObjectTwoSlots = providerUtil.createProviderDetails(2); + const providerObjectNoSlots = providerUtil.createProviderDetails(0); + it('Creates a provider with specified number of slots', () => { + expect(providerObjectTwoSlots.slots.length).to.equal(2); + }); + it('Creates slots for tomorrow an hour apart starting at 12', () => { + expect(providerObjectTwoSlots.slots[0].start).to.equal( + addHours(tomorrow, 12).toISOString(), + ); + expect(providerObjectTwoSlots.slots[1].start).to.equal( + addHours(tomorrow, 13).toISOString(), + ); + }); + it('Creates empty slots array with 0', () => { + expect(providerObjectNoSlots.slots.length).to.equal(0); + }); + }); +}); diff --git a/src/applications/vaos/referral-appointments/utils/provider.js b/src/applications/vaos/referral-appointments/utils/provider.js new file mode 100644 index 000000000000..94afcb411f16 --- /dev/null +++ b/src/applications/vaos/referral-appointments/utils/provider.js @@ -0,0 +1,43 @@ +/* eslint-disable no-plusplus */ +const dateFns = require('date-fns'); + +/** + * Creates a provider object with a configurable number of slots an hour apart. + * + * @param {Number} numberOfSlots How many slots to create + * @returns {Object} Provider object + */ +const createProviderDetails = numberOfSlots => { + const slots = []; + const tomorrow = dateFns.addDays(dateFns.startOfDay(new Date()), 1); + let hourFromNow = 12; + for (let i = 0; i < numberOfSlots; i++) { + const startTime = dateFns.addHours(tomorrow, hourFromNow); + slots.push({ + end: dateFns.addMinutes(startTime, 30).toISOString(), + id: Math.floor(Math.random() * 90000) + 10000, + start: startTime.toISOString(), + }); + hourFromNow++; + } + return { + providerName: 'Dr. Face', + typeOfCare: 'Dermatology', + orgName: 'New Skin Technologies', + orgAddress: { + street1: '111 Lori Ln.', + street2: '', + street3: '', + city: 'New York', + state: 'New York', + zip: '10016', + }, + orgPhone: '555-867-5309', + driveTime: '7 minute drive', + driveDistance: '8 miles', + slots, + location: 'New skin technologies bldg 2', + }; +}; + +module.exports = { createProviderDetails }; diff --git a/src/applications/vaos/services/mocks/index.js b/src/applications/vaos/services/mocks/index.js index e7beecf6ff4c..d15904a1ee55 100644 --- a/src/applications/vaos/services/mocks/index.js +++ b/src/applications/vaos/services/mocks/index.js @@ -58,6 +58,7 @@ const providerOrgs = require('./epsApi/providerOrganizations.json'); const providerServices = require('./epsApi/providerServices.json'); const providerSlots = require('./epsApi/providerServicesSlots.json'); const referralUtils = require('../../referral-appointments/utils/referrals'); +const providerUtils = require('../../referral-appointments/utils/provider'); // Returns the meta object without any backend service errors const meta = require('./v2/meta.json'); @@ -667,6 +668,13 @@ const responses = { data: getSlot.find(slot => slot?.id === req.params.slotId), }); }, + 'GET /vaos/v2/epsApi/providerDetails/:providerId': (req, res) => { + // Provider 3 throws error + if (req.params.providerId === '3') { + return res.status(500).json({ error: true }); + } + return res.json({ data: providerUtils.createProviderDetails(5) }); + }, 'GET /v0/user': { data: { attributes: { diff --git a/src/applications/vaos/services/referral/index.js b/src/applications/vaos/services/referral/index.js index 98861ffb1577..8e5365efa73c 100644 --- a/src/applications/vaos/services/referral/index.js +++ b/src/applications/vaos/services/referral/index.js @@ -28,3 +28,13 @@ export async function getPatientReferralById(referralId) { return null; } } + +export async function getProviderById(providerId) { + const response = await apiRequestWithUrl( + `/vaos/v2/epsApi/providerDetails/${providerId}`, + { + method: 'GET', + }, + ); + return response.data; +}