src/applications/mhv-medical-records/containers/Vitals.jsx
import React, { useEffect, useState, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { focusElement } from '@department-of-veterans-affairs/platform-utilities/ui';
import { format } from 'date-fns';
import { VaDate } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import {
updatePageTitle,
usePrintTitle,
} from '@department-of-veterans-affairs/mhv/exports';
import RecordList from '../components/RecordList/RecordList';
import { getVitals, reloadRecords } from '../actions/vitals';
import {
recordType,
vitalTypes,
pageTitles,
ALERT_TYPE_ERROR,
accessAlertTypes,
refreshExtractTypes,
} from '../util/constants';
import { getMonthFromSelectedDate } from '../util/helpers';
import { Actions } from '../util/actionTypes';
import * as Constants from '../util/constants';
import AccessTroubleAlertBox from '../components/shared/AccessTroubleAlertBox';
import useAlerts from '../hooks/use-alerts';
import NoRecordsMessage from '../components/shared/NoRecordsMessage';
import PrintHeader from '../components/shared/PrintHeader';
import useListRefresh from '../hooks/useListRefresh';
import NewRecordsIndicator from '../components/shared/NewRecordsIndicator';
import useAcceleratedData from '../hooks/useAcceleratedData';
const Vitals = () => {
const dispatch = useDispatch();
const updatedRecordList = useSelector(state => state.mr.vitals.updatedList);
const listState = useSelector(state => state.mr.vitals.listState);
const vitals = useSelector(state => state.mr.vitals.vitalsList);
const user = useSelector(state => state.user.profile);
const refresh = useSelector(state => state.mr.refresh);
const [cards, setCards] = useState(null);
const [acceleratedVitalsDate, setAcceleratedVitalsDate] = useState(
format(new Date(), 'yyyy-MM'),
);
const [displayDate, setDisplayDate] = useState(acceleratedVitalsDate);
const activeAlert = useAlerts(dispatch);
const vitalsCurrentAsOf = useSelector(
state => state.mr.vitals.listCurrentAsOf,
);
const { isLoading, isAcceleratingVitals } = useAcceleratedData();
const isLoadingAcceleratedData =
isAcceleratingVitals && listState === Constants.loadStates.FETCHING;
const dispatchAction = useMemo(
() => {
return isCurrent => {
return getVitals(
isCurrent,
isAcceleratingVitals,
acceleratedVitalsDate,
);
};
},
[acceleratedVitalsDate, isAcceleratingVitals],
);
useListRefresh({
listState,
listCurrentAsOf: vitalsCurrentAsOf,
refreshStatus: refresh.status,
extractType: refreshExtractTypes.VPR,
dispatchAction,
dispatch,
});
useEffect(
/**
* @returns a callback to automatically load any new records when unmounting this component
*/
() => {
return () => {
dispatch(reloadRecords());
};
},
[dispatch, listState],
);
useEffect(
() => {
focusElement(document.querySelector('h1'));
updatePageTitle(pageTitles.VITALS_PAGE_TITLE);
},
[dispatch],
);
usePrintTitle(
pageTitles.VITALS_PAGE_TITLE,
user.userFullName,
user.dob,
updatePageTitle,
);
useEffect(
() => {
if (vitals?.length) {
// create vital type cards based on the types of records present
const firstOfEach = [];
for (const [key, types] of Object.entries(vitalTypes)) {
const firstOfType = vitals.find(item => types.includes(item.type));
if (firstOfType) firstOfEach.push(firstOfType);
else firstOfEach.push({ type: key, noRecords: true });
}
setCards(firstOfEach);
}
},
[vitals],
);
const accessAlert = activeAlert && activeAlert.type === ALERT_TYPE_ERROR;
const content = () => {
if (accessAlert) {
return (
<AccessTroubleAlertBox
alertType={accessAlertTypes.VITALS}
className="vads-u-margin-bottom--9"
/>
);
}
if (refresh.initialFhirLoad && !vitalsCurrentAsOf) {
return (
<div className="vads-u-margin-y--8">
<va-loading-indicator
class="hydrated initial-fhir-load"
message="We're loading your records for the first time. This can take up to 2 minutes. Stay on this page until your records load."
setFocus
data-testid="initial-fhir-loading-indicator"
/>
</div>
);
}
if (vitals?.length === 0) {
return (
<>
<p>Vitals include:</p>
<ul>
<li>Blood pressure and blood oxygen level</li>
<li>Breathing rate and heart rate</li>
<li>Height and weight</li>
<li>Temperature</li>
</ul>
<NoRecordsMessage type={recordType.VITALS} />
</>
);
}
if (cards?.length) {
return (
<>
{!isAcceleratingVitals && (
<NewRecordsIndicator
refreshState={refresh}
extractType={refreshExtractTypes.VPR}
newRecordsFound={
Array.isArray(vitals) &&
Array.isArray(updatedRecordList) &&
vitals.length !== updatedRecordList.length
}
reloadFunction={() => {
dispatch(reloadRecords());
}}
/>
)}
{isAcceleratingVitals && (
<div className="vads-u-margin-top--2 ">
<hr className="vads-u-margin-y--1 vads-u-padding-0" />
<p className="vads-u-margin--0">
Showing most recent vitals from{' '}
<span
className="vads-u-font-weight--bold"
data-testid="current-date-display"
>
{getMonthFromSelectedDate({ date: displayDate })}
</span>
.
</p>
<hr className="vads-u-margin-y--1 vads-u-padding-0" />
</div>
)}
<RecordList
records={cards}
type={recordType.VITALS}
perPage={7}
hidePagination
domainOptions={{
isAccelerating: isAcceleratingVitals,
timeFrame: acceleratedVitalsDate,
}}
/>
</>
);
}
return (
<div className="vads-u-margin-y--8">
<va-loading-indicator
message="We’re loading your records. This could take up to a minute."
setFocus
data-testid="loading-indicator"
/>
</div>
);
};
const updateDate = event => {
const [year, month] = event.target.value.split('-');
// Ignore transient date changes.
if (year?.length === 4 && month?.length === 2) {
setAcceleratedVitalsDate(`${year}-${month}`);
}
};
const triggerApiUpdate = e => {
e.preventDefault();
setDisplayDate(acceleratedVitalsDate);
dispatch({
type: Actions.Vitals.UPDATE_LIST_STATE,
payload: Constants.loadStates.PRE_FETCH,
});
};
const datePicker = () => {
return (
<div className="vads-u-display--flex vads-u-flex-direction--column">
<div style={{ flex: 'inherit' }}>
<VaDate
label="Choose a month and year"
name="vitals-date-picker"
monthYearOnly
onDateChange={updateDate}
value={acceleratedVitalsDate}
data-testid="date-picker"
/>
</div>
<div className="vads-u-margin-top--2">
<va-button
text="Update time frame"
onClick={triggerApiUpdate}
disabled={isLoadingAcceleratedData}
data-testid="update-time-frame-button"
/>
</div>
</div>
);
};
return (
<div id="vitals">
<PrintHeader />
<h1 data-testid="vitals" className="vads-u-margin--0">
Vitals
</h1>
<p className="vads-u-margin-top--1 vads-u-margin-bottom--2">
{`Vitals are basic health numbers your providers check at your
appointments.`}
</p>
{isLoading && (
<div className="vads-u-margin-y--8">
<va-loading-indicator
message="We’re loading your vitals."
setFocus
data-testid="loading-indicator"
/>
</div>
)}
{!isLoading && (
<>
{isAcceleratingVitals && datePicker()}
{isLoadingAcceleratedData && (
<>
<div className="vads-u-margin-y--8">
<va-loading-indicator
message="We’re loading your records."
setFocus
data-testid="loading-indicator"
/>
</div>
</>
)}
{!isLoadingAcceleratedData && content()}
</>
)}
</div>
);
};
export default Vitals;