Skip to content
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

101218 Warn user of appointment conflicts on DS calendar #34943

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/applications/vaos/components/calendar/CalendarWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';

Check warning on line 7 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:7:1:Use date-fns or Native Date methods instead of moment.js
import classNames from 'classnames';

import CalendarRow from './CalendarRow';
Expand Down Expand Up @@ -47,10 +47,10 @@
* @returns {string} YYYY-MM
*/
export function getMaxMonth(maxDate, overrideMaxDays) {
const defaultMaxMonth = moment()

Check warning on line 50 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:50:27:Consider using date-fns addDays(date, amount) or dayjs().add(number, unit)

Check warning on line 50 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:50:27:Consider using Native new Date().
.add(DEFAULT_MAX_DAYS_AHEAD, 'days')
.format('YYYY-MM');
const maxMonth = moment(maxDate).startOf('month');

Check warning on line 53 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:53:20:Consider using date-fns startOfMonth(date) or dayjs().startOf(unit)

Check warning on line 53 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:53:20:Consider using Native new Date().

if (maxDate && (maxMonth.isAfter(defaultMaxMonth) || overrideMaxDays)) {
return maxMonth.format('YYYY-MM');
Expand Down Expand Up @@ -209,6 +209,16 @@
setMonths(updatedMonths);
}

function checkSelection(date, upcomingAppointments) {
const d1 = moment(date, 'YYYY-MM-DDTHH:mm:ss');

Check warning on line 213 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:213:14:Consider using date-fns, e.g. parse("2010-10-20 4:30", "yyyy-MM-dd H:mm", new Date()).
const appointments = upcomingAppointments[d1.format('YYYY-MM')];

return appointments?.some(appointment => {
const d2 = moment(appointment.start, 'YYYY-MM-DDTHH:mm:ss');

Check warning on line 217 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:217:16:Consider using date-fns, e.g. parse("2010-10-20 4:30", "yyyy-MM-dd H:mm", new Date()).
return d1.isSame(d2);
});
}

/**
* Calendar widget
*
Expand Down Expand Up @@ -238,6 +248,7 @@
* @returns {JSX.Element} props.Calendar Calendar Widget
*/
function CalendarWidget({
appointmentSelectionErrorMsg = 'You already have an appointment scheduled at this time. Please select another day or time.',
availableSlots,
id,
disabled,
Expand All @@ -260,6 +271,7 @@
timezone,
value = [],
showWeekends = false,
upcomingAppointments = [],
}) {
const [currentlySelectedDate, setCurrentlySelectedDate] = useState(() => {
if (value.length > 0) {
Expand All @@ -268,11 +280,17 @@

return null;
});
const currentDate = moment();

Check warning on line 283 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:283:23:Consider using Native new Date().
const maxMonth = getMaxMonth(maxDate, overrideMaxDays);
const [months, setMonths] = useState([moment(startMonth || minDate)]);

Check warning on line 285 in src/applications/vaos/components/calendar/CalendarWidget.jsx

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.jsx:285:41:Consider using Native new Date().
const exceededMaximumSelections = value.length > maxSelections;
const hasError = (required && showValidation) || exceededMaximumSelections;
let hasError = (required && showValidation) || exceededMaximumSelections;

const isAppointmentSelectionError = checkSelection(
value[0],
upcomingAppointments,
);
if (isAppointmentSelectionError) hasError = isAppointmentSelectionError;

const calendarCss = classNames('vaos-calendar__calendars vads-u-flex--1', {
'vaos-calendar__disabled': disabled,
Expand Down Expand Up @@ -302,6 +320,7 @@
>
{showValidation && requiredMessage}
{exceededMaximumSelections && maxSelectionsError}
{isAppointmentSelectionError && appointmentSelectionErrorMsg}
</span>
)}
{months.map(
Expand Down Expand Up @@ -392,6 +411,7 @@

CalendarWidget.propTypes = {
id: PropTypes.string.isRequired,
appointmentSelectionErrorMsg: PropTypes.string,
availableSlots: PropTypes.arrayOf(
PropTypes.shape({
start: PropTypes.string.isRequired,
Expand All @@ -414,6 +434,7 @@
showWeekends: PropTypes.bool,
startMonth: PropTypes.string,
timezone: PropTypes.string,
upcomingAppointments: PropTypes.object,
value: PropTypes.array,
onChange: PropTypes.func,
onNextMonth: PropTypes.func,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { waitFor } from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
import { expect } from 'chai';
import moment from 'moment-timezone';

Check warning on line 4 in src/applications/vaos/components/calendar/CalendarWidget.unit.spec.js

View workflow job for this annotation

GitHub Actions / Linting (Files Changed)

src/applications/vaos/components/calendar/CalendarWidget.unit.spec.js:4:1:Use date-fns or Native Date methods instead of moment.js
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { onCalendarChange } from '../../new-appointment/redux/actions';
import {
createTestStore,
renderWithStoreAndRouter,
} from '../../tests/mocks/setup';
import CalendarWidget from './CalendarWidget';

describe('VAOS Component: CalendarWidget', () => {
it('should display scheduling duplicate appointment error message', async () => {
// Arrange
const now = moment()
.tz('America/Denver')
.add(1, 'month')
.startOf('month')
.hour(12);
const availableSlots = [
{
start: now.format('YYYY-MM-DDTHH:mm:ss'),
},
{
start: moment(now)
.hour(13)
.format('YYYY-MM-DDTHH:mm:ss'),
},
];
const startMonth = now.format('YYYY-MM');
const submitted = false;
// Grouped by YYYY-MM
const upcomingAppointments = {
[now.format('YYYY-MM')]: [
{
start: now.format('YYYY-MM-DDTHH:mm:ss'),
},
],
};

const TestPageStub = () => {
const dispatch = useDispatch();
const data = useSelector(state => state.newAppointment.data);

return (
<CalendarWidget
maxSelections="1"
availableSlots={availableSlots}
value={data?.selectedDates}
id="dateTime"
timezone="America/Denver"
additionalOptions={{
required: true,
}}
disabled={false}
onChange={(...args) => {
return dispatch(onCalendarChange(...args));
}}
minDate={moment(now).format('YYYY-MM-DD')}
maxDate={moment(now)
.add(395, 'days')
.format('YYYY-MM-DD')}
renderIndicator={_ => undefined}
required
requiredMessage="Please choose your preferred date and time for your appointment"
startMonth={startMonth}
showValidation={submitted && !data?.selectedDates?.length}
showWeekends
upcomingAppointments={upcomingAppointments}
/>
);
};

// Act
const store = createTestStore({});
const screen = renderWithStoreAndRouter(<TestPageStub />, {
store,
});

userEvent.click(screen.getByText('1'));
userEvent.click(screen.getByText(now.format('h:mm')));

// Assert
await waitFor(() => {
expect(
screen.queryByText(
/You already have an appointment scheduled at this time. Please select another day or time/i,
),
).to.be.ok;
});

userEvent.click(
screen.getByText(
moment(now)
.hour(13)
.format('h:mm'),
),
);

await waitFor(() => {
expect(
screen.queryByText(
/You already have an appointment scheduled at this time. Please select another day or time/i,
),
).not.to.exist;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { getRealFacilityId } from '../../../utils/appointment';
import NewTabAnchor from '../../../components/NewTabAnchor';
import useIsInitialLoad from '../../../hooks/useIsInitialLoad';
import { getPageTitle } from '../../newAppointmentFlow';
import { selectUpcomingAppointments } from '../../../appointment-list/redux/selectors';

const pageKey = 'selectDateTime';

Expand Down Expand Up @@ -130,6 +131,7 @@ export default function DateTimeSelectPage() {
const isInitialLoad = useIsInitialLoad(loadingSlots);
const eligibility = useSelector(selectEligibility);
const clinic = useSelector(state => getChosenClinicInfo(state));
const upcomingAppointments = useSelector(selectUpcomingAppointments);

useEffect(
() => {
Expand Down Expand Up @@ -244,6 +246,7 @@ export default function DateTimeSelectPage() {
startMonth={startMonth}
showValidation={submitted && !selectedDates?.length}
showWeekends
upcomingAppointments={upcomingAppointments}
/>
</>
)}
Expand Down
Loading