From ac113d2d2982e944f64c3990a06797cb8f77c4fc Mon Sep 17 00:00:00 2001 From: Brian Seek Date: Tue, 10 Dec 2024 12:17:11 -0800 Subject: [PATCH] updated choose time page to use mocks selected from redux (#33321) * updated choose time page to use mocks selected from redux * re-arranged code for readability. * tweaks and split up date-picker page into two components * added tests for DateAndTimeContent component * wrapper tests. --- .../ChooseDateAndTime.jsx | 313 +++--------------- .../ChooseDateAndTime.unit.spec.js | 197 +++++++++-- .../components/DateAndTimeContent.jsx | 264 +++++++++++++++ .../DateAndTimeContent.unit.spec.js | 152 +++++++++ .../components/ReferralLayout.jsx | 2 +- .../vaos/referral-appointments/index.jsx | 14 +- .../temp-data/referral.js | 53 --- .../referral-appointments/utils/referrals.js | 1 + src/applications/vaos/utils/featureFlags.js | 2 +- 9 files changed, 646 insertions(+), 352 deletions(-) create mode 100644 src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx create mode 100644 src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js delete mode 100644 src/applications/vaos/referral-appointments/temp-data/referral.js diff --git a/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx b/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx index c930c312bdab..d2b23dd9b43e 100644 --- a/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx +++ b/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx @@ -1,145 +1,60 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; 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'; -import CalendarWidget from '../components/calendar/CalendarWidget'; +import { useLocation } from 'react-router-dom'; import ReferralLayout from './components/ReferralLayout'; -import { onCalendarChange } from '../new-appointment/redux/actions'; -import FormButtons from '../components/FormButtons'; -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 { getUpcomingAppointmentListInfo } from '../appointment-list/redux/selectors'; import { setFormCurrentPage, fetchProviderDetails } from './redux/actions'; -import { selectCurrentPage, getProviderInfo } from './redux/selectors'; +import { fetchFutureAppointments } from '../appointment-list/redux/actions'; +import { getProviderInfo } from './redux/selectors'; import { FETCH_STATUS } from '../utils/constants'; import { scrollAndFocus } from '../utils/scrollAndFocus'; -import { - getTimezoneDescByFacilityId, - getTimezoneByFacilityId, -} from '../utils/timezone'; +import DateAndTimeContent from './components/DateAndTimeContent'; -export const ChooseDateAndTime = () => { +export const ChooseDateAndTime = props => { + const { currentReferral } = props; const dispatch = useDispatch(); - - const history = useHistory(); - const selectedDate = useSelector(state => getSelectedDate(state)); - const upcomingAppointments = useSelector(state => - selectUpcomingAppointments(state), - ); const location = useLocation(); - const currentPage = useSelector(selectCurrentPage); - const startMonth = format(startOfMonth(referral.preferredDate), 'yyyy-MM'); - const [error, setError] = useState(''); - const latestAvailableSlot = new Date( - Math.max.apply( - null, - referral.slots.map(slot => { - return new Date(slot.start); - }), - ), - ); - const fullAddress = addressObject => { - let addressString = addressObject.street1; - if (addressObject.street2) { - addressString = `${addressString}, ${addressObject.street2}`; - } - if (addressObject.street3) { - addressString = `${addressString}, ${addressObject.street3}`; - } - addressString = `${addressString}, ${addressObject.city}, ${ - addressObject.state - }, ${addressObject.zip}`; - return addressString; - }; - const selectedDateKey = `selected-date-referral-${referral.id}`; - const onChange = useCallback( - value => { - setError(''); - dispatch(onCalendarChange(value)); - sessionStorage.setItem(selectedDateKey, value); - }, - [dispatch, selectedDateKey], - ); const { provider, providerFetchStatus } = useSelector( state => getProviderInfo(state), shallowEqual, ); - - useEffect( - () => { - const savedSelectedDate = sessionStorage.getItem(selectedDateKey); - - if (!savedSelectedDate) { - return; - } - - dispatch(onCalendarChange([savedSelectedDate])); - }, - [dispatch, selectedDateKey], - ); - - const hasConflict = useCallback( - () => { - let conflict = false; - const selectedMonth = format(new Date(selectedDate), 'yyyy-MM'); - if (!(selectedMonth in upcomingAppointments)) { - return conflict; - } - const selectedDay = format(new Date(selectedDate), 'dd'); - const monthOfApts = upcomingAppointments[selectedMonth]; - const daysWithApts = monthOfApts.map(apt => { - return format(new Date(apt.start), 'dd'); - }); - if (!daysWithApts.includes(selectedDay)) { - return conflict; - } - const unavailableTimes = monthOfApts.map(upcomingAppointment => { - return { - start: zonedTimeToUtc( - new Date(upcomingAppointment.start), - upcomingAppointment.timezone, - ), - end: zonedTimeToUtc( - addMinutes( - new Date(upcomingAppointment.start), - upcomingAppointment.minutesDuration, - ), - upcomingAppointment.timezone, - ), - }; - }); - unavailableTimes.forEach(range => { - if ( - isWithinInterval( - zonedTimeToUtc(new Date(selectedDate), referral.timezone), - { - start: range.start, - end: range.end, - }, - ) - ) { - conflict = true; - } - }); - return conflict; - }, - [selectedDate, upcomingAppointments], + const { futureStatus, appointmentsByMonth } = useSelector( + state => getUpcomingAppointmentListInfo(state), + shallowEqual, ); + const [loading, setLoading] = useState(true); + const [failed, setFailed] = useState(false); useEffect( () => { - if (providerFetchStatus === FETCH_STATUS.notStarted) { - dispatch(fetchProviderDetails(referral.provider)); - } else if (providerFetchStatus === FETCH_STATUS.succeeded) { + if ( + providerFetchStatus === FETCH_STATUS.notStarted || + futureStatus === FETCH_STATUS.notStarted + ) { + if (providerFetchStatus === FETCH_STATUS.notStarted) { + dispatch(fetchProviderDetails(currentReferral.providerId)); + } + if (futureStatus === FETCH_STATUS.notStarted) { + dispatch(fetchFutureAppointments({ includeRequests: false })); + } + } else if ( + providerFetchStatus === FETCH_STATUS.succeeded && + futureStatus === FETCH_STATUS.succeeded + ) { + setLoading(false); scrollAndFocus('h1'); - } else if (providerFetchStatus === FETCH_STATUS.failed) { + } else if ( + providerFetchStatus === FETCH_STATUS.failed || + futureStatus === FETCH_STATUS.failed + ) { + setLoading(false); + setFailed(true); scrollAndFocus('h2'); } }, - [dispatch, providerFetchStatus], + [currentReferral.providerId, dispatch, providerFetchStatus, futureStatus], ); useEffect( () => { @@ -148,47 +63,18 @@ export const ChooseDateAndTime = () => { [location, dispatch], ); - const onBack = () => { - routeToPreviousReferralPage(history, currentPage, referral.id); - }; - - const onSubmit = () => { - if (error) { - return; - } - - if (!selectedDate) { - setError( - 'Please choose your preferred date and time for your appointment', - ); - return; - } - - if (upcomingAppointments && hasConflict()) { - setError( - 'You already have an appointment at this time. Please select another day or time.', - ); - return; - } - - routeToNextReferralPage(history, currentPage); - }; - - if ( - providerFetchStatus === FETCH_STATUS.loading || - providerFetchStatus === FETCH_STATUS.notStarted - ) { + if (loading) { return ( -
+
); } - if (providerFetchStatus === FETCH_STATUS.failed) { + if (failed) { return ( - -

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

+ +

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

We’re having trouble getting your upcoming appointments. Please try again later. @@ -198,116 +84,17 @@ export const ChooseDateAndTime = () => { } return ( - <> -

-

Schedule an appointment with your provider

-

- You or your referring VA facility selected to schedule an - appointment online with this provider: -

-

- {provider.providerName} -

-

{referral.typeOfCare}

-

- {provider.orgName} -

-
-

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

- -
-

- Phone:{' '} - -

-

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

-

Choose a date and time

-

- Select an available date and time from the calendar below. - Appointment times are displayed in{' '} - {`${getTimezoneDescByFacilityId(referral.provider)}`}. -

-
-
- - } - onChange={onChange} - onNextMonth={null} - onPreviousMonth={null} - minDate={format(new Date(), 'yyyy-MM-dd')} - maxDate={format(latestAvailableSlot, 'yyyy-MM-dd')} - required - requiredMessage={error} - startMonth={startMonth} - showValidation={error.length > 0} - showWeekends - overrideMaxDays - /> -
- onBack()} - onSubmit={() => onSubmit()} - loadingText="Page change in progress" - /> - + ); }; +ChooseDateAndTime.propTypes = { + currentReferral: PropTypes.object.isRequired, +}; + export default ChooseDateAndTime; diff --git a/src/applications/vaos/referral-appointments/ChooseDateAndTime.unit.spec.js b/src/applications/vaos/referral-appointments/ChooseDateAndTime.unit.spec.js index 395d5c81d81b..5635d8dbef26 100644 --- a/src/applications/vaos/referral-appointments/ChooseDateAndTime.unit.spec.js +++ b/src/applications/vaos/referral-appointments/ChooseDateAndTime.unit.spec.js @@ -1,35 +1,180 @@ import React from 'react'; import { expect } from 'chai'; -// import sinon from 'sinon'; -import userEvent from '@testing-library/user-event'; +import sinon from 'sinon'; import { - createTestStore, renderWithStoreAndRouter, + createTestStore, } from '../tests/mocks/setup'; import ChooseDateAndTime from './ChooseDateAndTime'; +import { createReferral } from './utils/referrals'; +import { createProviderDetails } from './utils/provider'; +import confirmedV2 from '../services/mocks/v2/confirmed.json'; +import * as getProviderByIdModule from '../services/referral'; +import * as fetchAppointmentsModule from '../services/appointment'; +import { FETCH_STATUS } from '../utils/constants'; -// Tests skipped for now since there are issues with displaying TZ and redux/data in flux. -describe('VAOS referral-appointments', () => { - const store = createTestStore(); - // it.skip('should store the selected date', async () => { - // const screen = renderWithStoreAndRouter(, { - // store, - // }); - // }); - it.skip('should validate on submit', async () => { - const screen = renderWithStoreAndRouter(, { - store, - }); - const button = await screen.findByText(/^Continue/); - - userEvent.click(button); - expect( - screen.findByText( - 'Please choose your preferred date and time for your appointment', - ), - ).to.be.ok; +describe('VAOS ChoseDateAndTime component', () => { + const sandbox = sinon.createSandbox(); + const confirmed = [ + { + minutesDuration: 30, + version: 2, + description: 'VAOS_UNKNOWN', + id: '00C893va', + resourceType: 'Appointment', + start: '2023-12-21T09:00:00-08:00', + status: 'cancelled', + location: { + clinicId: '437', + stationId: '983', + vistaId: '983', + clinicName: null, + clinicPhone: null, + clinicPhoneExtension: null, + clinicPhysicalLocation: null, + }, + practitioners: [], + vaos: { + appointmentType: 'vaAppointment', + apiData: { + minutesDuration: 30, + priority: 0, + clinic: '437', + comment: 'Follow-up/Routine: I have a headache', + description: 'Upcoming COVID-19 appointment', + end: '2023-12-21T18:19:34', + id: '00C893va', + kind: 'clinic', + localStartTime: '2023-12-21T09:00:00.000-06:00', + locationId: '983', + serviceType: 'covid', + start: '2023-12-21T18:19:34', + status: 'cancelled', + contact: { + telecom: [ + { + type: 'email', + value: null, + }, + ], + }, + practitioners: [], + preferredTimesForPhoneCall: [], + reasonCode: { + text: 'Follow-up/Routine: Covid shot', + }, + cancelationReason: null, + cancellable: false, + patientIcn: null, + requestedPeriods: null, + slot: null, + telehealth: null, + }, + isCancellable: false, + isCommunityCare: false, + isCompAndPenAppointment: false, + isCOVIDVaccine: true, + isExpressCare: false, + isPastAppointment: true, + isPendingAppointment: false, + isPhoneAppointment: false, + isUpcomingAppointment: false, + isVideo: false, + }, + videoData: { + isVideo: false, + }, + cancelationReason: null, + communityCareProvider: null, + preferredProviderName: null, + }, + ]; + const initialFullState = { + featureToggles: { + vaOnlineSchedulingCCDirectScheduling: true, + }, + referral: { + selectedProvider: createProviderDetails(1), + providerFetchStatus: FETCH_STATUS.succeeded, + }, + appointments: { + confirmed, + confirmedStatus: FETCH_STATUS.succeeded, + }, + }; + const initialEmptyState = { + featureToggles: { + vaOnlineSchedulingCCDirectScheduling: true, + }, + referral: { + selectedProvider: [], + providerFetchStatus: FETCH_STATUS.notStarted, + }, + appointments: { + confirmed: [], + confirmedStatus: FETCH_STATUS.notStarted, + }, + }; + const failedState = { + featureToggles: { + vaOnlineSchedulingCCDirectScheduling: true, + }, + referral: { + selectedProvider: [], + providerFetchStatus: FETCH_STATUS.failed, + }, + appointments: { + confirmed, + confirmedStatus: FETCH_STATUS.succeeded, + }, + }; + beforeEach(() => { + global.XMLHttpRequest = sinon.useFakeXMLHttpRequest(); + sandbox + .stub(getProviderByIdModule, 'getProviderById') + .resolves({ data: createProviderDetails(1) }); + sandbox + .stub(fetchAppointmentsModule, 'fetchAppointments') + .resolves(confirmedV2); + }); + afterEach(() => { + sandbox.restore(); + }); + it('should fetch provider or appointments from store if it exists and not call API', async () => { + const screen = renderWithStoreAndRouter( + , + { + store: createTestStore(initialFullState), + }, + ); + expect(await screen.getByTestId('pick-heading')).to.exist; + sandbox.assert.notCalled(getProviderByIdModule.getProviderById); + sandbox.assert.notCalled(fetchAppointmentsModule.fetchAppointments); + }); + it('should call API for provider or appointment data if not in store', async () => { + const screen = renderWithStoreAndRouter( + , + { + store: createTestStore(initialEmptyState), + }, + ); + expect(await screen.getByTestId('loading')).to.exist; + sandbox.assert.calledOnce(getProviderByIdModule.getProviderById); + sandbox.assert.calledOnce(fetchAppointmentsModule.fetchAppointments); + }); + it('should show error if any fetch fails', async () => { + const screen = renderWithStoreAndRouter( + , + { + store: createTestStore(failedState), + }, + ); + expect(await screen.getByTestId('error')).to.exist; }); - // it('should display the timezone in the correct format', async () => { - - // }); }); diff --git a/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx new file mode 100644 index 000000000000..e5c487866a5d --- /dev/null +++ b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx @@ -0,0 +1,264 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { useDispatch, useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { format, addMinutes, isWithinInterval } from 'date-fns'; +import { zonedTimeToUtc } from 'date-fns-tz'; +import CalendarWidget from '../../components/calendar/CalendarWidget'; +import { onCalendarChange } from '../../new-appointment/redux/actions'; +import FormButtons from '../../components/FormButtons'; +import { getSelectedDate } from '../../new-appointment/redux/selectors'; +import { routeToNextReferralPage, routeToPreviousReferralPage } from '../flow'; +import { selectCurrentPage } from '../redux/selectors'; +import { + getTimezoneDescByFacilityId, + getTimezoneByFacilityId, +} from '../../utils/timezone'; + +export const DateAndTimeContent = props => { + const { currentReferral, provider, appointmentsByMonth } = props; + const dispatch = useDispatch(); + const history = useHistory(); + + const selectedDate = useSelector(state => getSelectedDate(state)); + const currentPage = useSelector(selectCurrentPage); + const [error, setError] = useState(''); + + const facilityTimeZone = getTimezoneByFacilityId( + currentReferral.ReferringFacilityInfo.FacilityCode, + ); + const selectedDateKey = `selected-date-referral-${currentReferral.UUID}`; + const latestAvailableSlot = new Date( + Math.max.apply( + null, + provider.slots.map(slot => { + return new Date(slot.start); + }), + ), + ); + const fullAddress = addressObject => { + let addressString = addressObject.street1; + if (addressObject.street2) { + addressString = `${addressString}, ${addressObject.street2}`; + } + if (addressObject.street3) { + addressString = `${addressString}, ${addressObject.street3}`; + } + addressString = `${addressString}, ${addressObject.city}, ${ + addressObject.state + }, ${addressObject.zip}`; + return addressString; + }; + + const hasConflict = useCallback( + () => { + let conflict = false; + const selectedMonth = format(new Date(selectedDate), 'yyyy-MM'); + if (!(selectedMonth in appointmentsByMonth)) { + return conflict; + } + const selectedDay = format(new Date(selectedDate), 'dd'); + const monthOfApts = appointmentsByMonth[selectedMonth]; + const daysWithApts = monthOfApts.map(apt => { + return format(new Date(apt.start), 'dd'); + }); + if (!daysWithApts.includes(selectedDay)) { + return conflict; + } + const unavailableTimes = monthOfApts.map(upcomingAppointment => { + return { + start: zonedTimeToUtc( + new Date(upcomingAppointment.start), + upcomingAppointment.timezone, + ), + end: zonedTimeToUtc( + addMinutes( + new Date(upcomingAppointment.start), + upcomingAppointment.minutesDuration, + ), + upcomingAppointment.timezone, + ), + }; + }); + unavailableTimes.forEach(range => { + if ( + isWithinInterval( + zonedTimeToUtc(new Date(selectedDate), facilityTimeZone), + { + start: range.start, + end: range.end, + }, + ) + ) { + conflict = true; + } + }); + return conflict; + }, + [facilityTimeZone, selectedDate, appointmentsByMonth], + ); + const onChange = useCallback( + value => { + setError(''); + dispatch(onCalendarChange(value)); + sessionStorage.setItem(selectedDateKey, value); + }, + [dispatch, selectedDateKey], + ); + const onBack = () => { + routeToPreviousReferralPage(history, currentPage, currentReferral.UUID); + }; + const onSubmit = () => { + if (error) { + return; + } + if (!selectedDate) { + setError( + 'Please choose your preferred date and time for your appointment', + ); + return; + } + if (appointmentsByMonth && hasConflict()) { + setError( + 'You already have an appointment at this time. Please select another day or time.', + ); + return; + } + routeToNextReferralPage(history, currentPage, currentReferral.UUID); + }; + useEffect( + () => { + const savedSelectedDate = sessionStorage.getItem(selectedDateKey); + + if (!savedSelectedDate) { + return; + } + + dispatch(onCalendarChange([savedSelectedDate])); + }, + [dispatch, selectedDateKey], + ); + return ( + <> +
+

+ Schedule an appointment with your provider +

+

+ You or your referring VA facility selected to schedule an appointment + online with this provider: +

+

+ {provider.providerName} +

+

{currentReferral.CategoryOfCare}

+

+ {provider.orgName} +

+
+

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

+ +
+

+ Phone:{' '} + +

+

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

+

Choose a date and time

+

+ Select an available date and time from the calendar below. Appointment + times are displayed in{' '} + {`${getTimezoneDescByFacilityId( + currentReferral.ReferringFacilityInfo.FacilityCode, + )}`} + . +

+
+
+ + } + onChange={onChange} + onNextMonth={null} + onPreviousMonth={null} + minDate={format(new Date(), 'yyyy-MM-dd')} + maxDate={format(latestAvailableSlot, 'yyyy-MM-dd')} + required + requiredMessage={error} + startMonth={format(new Date(), 'yyyy-MM')} + showValidation={error.length > 0} + showWeekends + overrideMaxDays + /> +
+ onBack()} + onSubmit={() => onSubmit()} + loadingText="Page change in progress" + /> + + ); +}; + +DateAndTimeContent.propTypes = { + appointmentsByMonth: PropTypes.object.isRequired, + currentReferral: PropTypes.object.isRequired, + provider: PropTypes.object.isRequired, +}; + +export default DateAndTimeContent; diff --git a/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js new file mode 100644 index 000000000000..9f9d65fb53cd --- /dev/null +++ b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js @@ -0,0 +1,152 @@ +import React from 'react'; +import { fireEvent } from '@testing-library/react'; +import { expect } from 'chai'; +import MockDate from 'mockdate'; +import DateAndTimeContent from './DateAndTimeContent'; +import { createReferral } from '../utils/referrals'; +import { createProviderDetails } from '../utils/provider'; +import { renderWithStoreAndRouter } from '../../tests/mocks/setup'; + +describe('VAOS Component: DateAndTimeContent', () => { + const initialState = { + featureToggles: { + vaOnlineSchedulingCCDirectScheduling: true, + }, + newAppointment: { + data: { + selectedDates: [], + }, + }, + referral: { + currentPage: 'scheduleAppointment', + }, + }; + const referral = createReferral( + '2024-12-05', + 'add2f0f4-a1ea-4dea-a504-a54ab57c68', + ); + const provider = createProviderDetails(1); + const appointmentsByMonth = { + '2024-12': [ + { + start: '2024-12-06T12:00:00-05:00', + timezone: 'America/New_York', + minutesDuration: 60, + }, + { + start: '2024-12-19T08:40:00-05:00', + timezone: 'America/New_York', + minutesDuration: 60, + }, + ], + '2025-01': [ + { + start: '2025-01-02T12:00:00-05:00', + timezone: 'America/New_York', + minutesDuration: 60, + }, + { + start: '2025-01-19T08:40:00-05:00', + timezone: 'America/New_York', + minutesDuration: 60, + }, + ], + }; + beforeEach(() => { + MockDate.set('2024-12-05T00:00:00Z'); + }); + afterEach(() => { + sessionStorage.clear(); + MockDate.reset(); + }); + it('should render DateAndTimeContent component', () => { + const screen = renderWithStoreAndRouter( + , + { + initialState, + }, + ); + + expect(screen.getByTestId('pick-heading')).to.exist; + expect(screen.getByTestId('cal-widget')).to.exist; + }); + it('should show error if no date selected', async () => { + const screen = renderWithStoreAndRouter( + , + { + initialState, + }, + ); + const continueButton = await screen.findByRole('button', { + name: /Continue/i, + }); + fireEvent.click(continueButton); + expect( + await screen.getByText( + 'Please choose your preferred date and time for your appointment', + ), + ).to.exist; + }); + it('should show error if conflicting appointment', async () => { + const selectedDateKey = `selected-date-referral-${referral.UUID}`; + sessionStorage.setItem(selectedDateKey, '2024-12-06T17:00:00.000Z'); + const initialStateWithSelect = { + featureToggles: { + vaOnlineSchedulingCCDirectScheduling: true, + }, + newAppointment: { + data: { + selectedDates: '2024-12-06T17:00:00.000Z', + }, + }, + }; + const screen = renderWithStoreAndRouter( + , + { + initialStateWithSelect, + }, + ); + const continueButton = await screen.findByRole('button', { + name: /Continue/i, + }); + fireEvent.click(continueButton); + expect( + await screen.getByText( + 'You already have an appointment at this time. Please select another day or time.', + ), + ).to.exist; + }); + it('should select date if value in session storage', async () => { + const selectedDateKey = `selected-date-referral-${referral.UUID}`; + sessionStorage.setItem(selectedDateKey, '2024-12-06T19:00:00.000Z'); + const screen = renderWithStoreAndRouter( + , + { + initialState, + path: '/schedule-referral/date-time', + }, + ); + const continueButton = await screen.findByRole('button', { + name: /Continue/i, + }); + fireEvent.click(continueButton); + // Routes to next page if selection exists + expect(screen.history.push.called).to.be.true; + }); +}); diff --git a/src/applications/vaos/referral-appointments/components/ReferralLayout.jsx b/src/applications/vaos/referral-appointments/components/ReferralLayout.jsx index e7e5d7949e38..bf1dd05251fb 100644 --- a/src/applications/vaos/referral-appointments/components/ReferralLayout.jsx +++ b/src/applications/vaos/referral-appointments/components/ReferralLayout.jsx @@ -75,6 +75,6 @@ export default function ReferralLayout({ children, hasEyebrow }) { } ReferralLayout.propTypes = { - children: PropTypes.array, + children: PropTypes.node, hasEyebrow: PropTypes.bool, }; diff --git a/src/applications/vaos/referral-appointments/index.jsx b/src/applications/vaos/referral-appointments/index.jsx index 03551dc2e889..091434cd26f5 100644 --- a/src/applications/vaos/referral-appointments/index.jsx +++ b/src/applications/vaos/referral-appointments/index.jsx @@ -51,6 +51,9 @@ export default function ReferralAppointments() { }, [referralFetchStatus], ); + if (referralNotFound || !featureCCDirectScheduling) { + return ; + } if ( (!referral || referralFetchStatus === FETCH_STATUS.loading) && !referralNotFound @@ -72,9 +75,6 @@ export default function ReferralAppointments() { ); } - if (referralNotFound || !featureCCDirectScheduling) { - return ; - } return ( <> @@ -83,11 +83,9 @@ export default function ReferralAppointments() { path={`${basePath.url}/review/`} component={ConfirmApprovedPage} /> - {/* TODO convert component to get referral as a prop */} - + + + {/* TODO: remove this mock page when referral complete page is built */} diff --git a/src/applications/vaos/referral-appointments/temp-data/referral.js b/src/applications/vaos/referral-appointments/temp-data/referral.js deleted file mode 100644 index ebfc65267a33..000000000000 --- a/src/applications/vaos/referral-appointments/temp-data/referral.js +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint-disable no-plusplus */ -const dateFns = require('date-fns'); - -const getAvailableSlots = (number = 2) => { - const slots = []; - const tomorrow = dateFns.addDays(dateFns.startOfDay(new Date()), 1); - let hourFromNow = 12; - for (let i = 0; i < number; 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 slots; -}; -const referral = { - id: 123456, - providerName: 'Dr. Face', - provider: '540', - typeOfCare: 'Dermatology', - appointmentCount: 2, - 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: getAvailableSlots(), - preferredDate: new Date(), - timezone: 'America/Denver', - expirationDate: dateFns.addDays(new Date(), 30), - location: 'New skin technologies bldg 2', - referralNumber: '1234567890', - referringFacility: { - name: 'Syracuse VA Medical Center', - phone: '555-555-5555', - }, -}; -referral.slots.push({ - end: '2024-11-22T16:30:00.000Z', - id: 35145, - start: '2024-11-22T16:00:00.000Z', -}); -module.exports = { getAvailableSlots, referral }; diff --git a/src/applications/vaos/referral-appointments/utils/referrals.js b/src/applications/vaos/referral-appointments/utils/referrals.js index ea7574a783c9..3b78781938b3 100644 --- a/src/applications/vaos/referral-appointments/utils/referrals.js +++ b/src/applications/vaos/referral-appointments/utils/referrals.js @@ -48,6 +48,7 @@ const createReferral = (startDate, uuid) => { numberOfAppointments: 1, providerName: 'Dr. Face', providerLocation: 'New skin technologies bldg 2', + providerId: '111', }; }; diff --git a/src/applications/vaos/utils/featureFlags.js b/src/applications/vaos/utils/featureFlags.js index e702e8e64056..b5aa2fd5b257 100644 --- a/src/applications/vaos/utils/featureFlags.js +++ b/src/applications/vaos/utils/featureFlags.js @@ -21,7 +21,7 @@ module.exports = [ { name: 'vaOnlineSchedulingBookingExclusion', value: false }, { name: 'vaOnlineSchedulingStaticLandingPage', value: true }, { name: 'vaOnlineSchedulingDatadogRum', value: true }, - { name: 'vaOnlineSchedulingCCDirectScheduling', value: false }, + { name: 'vaOnlineSchedulingCCDirectScheduling', value: true }, { name: 'vaOnlineSchedulingRecentLocationsFilter', value: false }, { name: 'vaOnlineSchedulingOhDirectSchedule', value: false }, { name: 'vaOnlineSchedulingOhRequest', value: false },