Skip to content

Commit

Permalink
Merge pull request #501 from Harbor-Systems/additional_questionnaire_…
Browse files Browse the repository at this point in the history
…responses

Add questionnaire responses to additional questions patient column in ehr portal
  • Loading branch information
GiladSchneider authored Nov 4, 2024
2 parents 749d78f + fdbf768 commit 04c4ad9
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ export const mapPaperworkResponseItem = (item: QuestionnaireResponseItem): Quest
return item as QuestionnaireResponseItemWithValueArray;
};

export enum QuestionnaireLinkIds {
PREFERRED_LANGUAGE = 'preferred-language',
ALLERGIES = 'allergies',
REASON_FOR_VISIT = 'reason-for-visit',
VITALS_TEMPERATURE = 'vitals-temperature',
VITALS_PULSE = 'vitals-pulse',
VITALS_HR = 'vitals-hr',
VITALS_RR = 'vitals-rr',
VITALS_BP = 'vitals-bp',
MEDICAL_HISTORY = 'medical-history',
SURGICAL_HISTORY = 'surgical-history',
CURRENT_MEDICATION = 'current-medications',
PATIENT_STREET_ADDRESS = 'patient-street-address',
PATIENT_NUMBER = 'patient-number',
GUARDIAN_NUMBER = 'guardian-number',
RELAY_PHONE = 'relay-phone',
CONSENT_FORMS = 'consent-forms',
}

export const getQuestionnaireResponseByLinkId = (
linkId: string,
questionnaireResponse?: QuestionnaireResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
ApptStatus,
AppointmentMessaging,
UCAppointmentInformation,
QuestionnaireLinkIds,
} from 'ehr-utils';
import ChatModal from '../../../features/chat/ChatModal';
import { calculatePatientAge } from '../../../helpers/formatDateTime';
Expand Down Expand Up @@ -73,17 +74,23 @@ export const AppointmentSidePanel: FC<AppointmentSidePanelProps> = ({ appointmen
const [chatModalOpen, setChatModalOpen] = useState<boolean>(false);
const [isInviteParticipantOpen, setIsInviteParticipantOpen] = useState(false);

const reasonForVisit = getQuestionnaireResponseByLinkId('reason-for-visit', questionnaireResponse)?.answer?.[0]
.valueString;
const preferredLanguage = getQuestionnaireResponseByLinkId('preferred-language', questionnaireResponse)?.answer?.[0]
.valueString;
const relayPhone = getQuestionnaireResponseByLinkId('relay-phone', questionnaireResponse)?.answer?.[0].valueString;
const reasonForVisit = getQuestionnaireResponseByLinkId(QuestionnaireLinkIds.REASON_FOR_VISIT, questionnaireResponse)
?.answer?.[0].valueString;
const preferredLanguage = getQuestionnaireResponseByLinkId(
QuestionnaireLinkIds.PREFERRED_LANGUAGE,
questionnaireResponse,
)?.answer?.[0].valueString;
const relayPhone = getQuestionnaireResponseByLinkId(QuestionnaireLinkIds.RELAY_PHONE, questionnaireResponse)
?.answer?.[0].valueString;
const number =
getQuestionnaireResponseByLinkId('patient-number', questionnaireResponse)?.answer?.[0].valueString ||
getQuestionnaireResponseByLinkId('guardian-number', questionnaireResponse)?.answer?.[0].valueString;
const knownAllergies = getQuestionnaireResponseByLinkId('allergies', questionnaireResponse)?.answer[0].valueArray;
const address = getQuestionnaireResponseByLinkId('patient-street-address', questionnaireResponse)?.answer?.[0]
.valueString;
getQuestionnaireResponseByLinkId(QuestionnaireLinkIds.PATIENT_NUMBER, questionnaireResponse)?.answer?.[0]
.valueString ||
getQuestionnaireResponseByLinkId(QuestionnaireLinkIds.GUARDIAN_NUMBER, questionnaireResponse)?.answer?.[0]
.valueString;
const knownAllergies = getQuestionnaireResponseByLinkId(QuestionnaireLinkIds.ALLERGIES, questionnaireResponse)
?.answer[0].valueArray;
const address = getQuestionnaireResponseByLinkId(QuestionnaireLinkIds.PATIENT_STREET_ADDRESS, questionnaireResponse)
?.answer?.[0].valueString;

const handleERXLoadingStatusChange = useCallback<(status: boolean) => void>(
(status) => setIsERXLoading(status),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,59 @@
import React, { FC } from 'react';
import { Box, Divider, Skeleton, Typography } from '@mui/material';
import { getQuestionnaireResponseByLinkId } from 'ehr-utils';
import { FhirResource, Questionnaire, QuestionnaireItem, QuestionnaireResponse } from 'fhir/r4';
import { QuestionnaireLinkIds } from 'ehr-utils';
import { getSelectors } from '../../../../../shared/store/getSelectors';
import { useAppointmentStore } from '../../../../state';
import { useAppointmentStore, useGetQuestionnaireDetails } from '../../../../state';

const omitKnownQuestions = Object.values(QuestionnaireLinkIds) as string[];

export const AdditionalQuestionsPatientColumn: FC = () => {
const { questionnaireResponse, isAppointmentLoading } = getSelectors(useAppointmentStore, [
const { questionnaire, questionnaireResponse, isAppointmentLoading } = getSelectors(useAppointmentStore, [
'questionnaire',
'questionnaireResponse',
'isAppointmentLoading',
]);

const fluVaccine = getQuestionnaireResponseByLinkId('flu-vaccine', questionnaireResponse)?.answer[0].valueString;
const vaccinesUpToDate = getQuestionnaireResponseByLinkId('vaccines-up-to-date', questionnaireResponse)?.answer[0]
.valueString;
const travelUsa = getQuestionnaireResponseByLinkId('travel-usa', questionnaireResponse)?.answer[0].valueString;
const hospitalize = getQuestionnaireResponseByLinkId('hospitalize', questionnaireResponse)?.answer[0].valueString;
const { isFetching: isFetchingQuestionnaire } = useGetQuestionnaireDetails(
{
questionnaireName: questionnaireResponse?.questionnaire,
},
(data) => {
const questionnaire = data?.find(
(resource: FhirResource) => resource.resourceType === 'Questionnaire',
) as unknown as Questionnaire;
useAppointmentStore.setState({
questionnaire: questionnaire,
});
},
);

const getQuestionBlock = (
questionStructure: QuestionnaireItem,
questionnaireResponse: QuestionnaireResponse | undefined,
): JSX.Element | null => {
if (questionStructure.type == 'group') {
const answerBlocks = questionStructure.item?.map((question) => getQuestionBlock(question, questionnaireResponse));
return answerBlocks && answerBlocks.length > 0 ? (
<Box key={questionStructure.linkId} sx={{ paddingBottom: 2 }}>
<Typography variant="body1" sx={{ opacity: 0.6 }}>
{questionStructure.text}
</Typography>
{questionStructure.item?.map((question) => getQuestionBlock(question, questionnaireResponse))}
</Box>
) : null;
}
const answer = questionnaireResponse?.item?.find((q) => q.linkId === questionStructure.linkId)?.answer?.[0]
?.valueString;
return answer ? (
<Box key={questionStructure.linkId} sx={{ paddingTop: 2 }}>
<Typography variant="overline" sx={{ opacity: 0.6 }}>
{questionStructure.text}
</Typography>
<Typography>{answer}</Typography>
</Box>
) : null;
};

return (
<Box
Expand All @@ -24,69 +63,18 @@ export const AdditionalQuestionsPatientColumn: FC = () => {
gap: 1,
}}
>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<Typography>Have you or your child had your flu vaccine?</Typography>
{isAppointmentLoading ? (
<Skeleton>
<Typography>Yes</Typography>
</Skeleton>
) : (
<Typography>{fluVaccine}</Typography>
)}
</Box>
<Divider />
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<Typography>Are your or your child&apos;s vaccines up to date?</Typography>
{isAppointmentLoading ? (
<Skeleton>
<Typography>Yes</Typography>
</Skeleton>
) : (
<Typography>{vaccinesUpToDate}</Typography>
)}
</Box>
<Divider />
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<Typography>Have you traveled out of the USA in the last 2 weeks?</Typography>
{isAppointmentLoading ? (
<Skeleton>
<Typography>Yes</Typography>
</Skeleton>
) : (
<Typography>{travelUsa}</Typography>
)}
</Box>
<Divider />
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<Typography>Has the patient been hospitalized in the past 6 months?</Typography>
{isAppointmentLoading ? (
<Skeleton>
<Typography>Yes</Typography>
</Skeleton>
) : (
<Typography>{hospitalize}</Typography>
)}
</Box>
{isFetchingQuestionnaire || isAppointmentLoading ? (
<Skeleton />
) : (
questionnaire?.item
?.filter((question) => !omitKnownQuestions.includes(question.linkId))
.map((question, index, array) => (
<>
{getQuestionBlock(question, questionnaireResponse)}
{index < array.length - 1 && <Divider />}
</>
))
)}
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,41 @@ export const useGetAppointmentInformation = (
);
};

export const useGetQuestionnaireDetails = (
{
questionnaireName,
}: {
questionnaireName: string | undefined;
},
onSuccess: (data: Bundle<FhirResource>[]) => void,
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
) => {
const { fhirClient } = useApiClients();
return useQuery(
['telemed-questionnaire', { questionnaireName }],
() => {
if (fhirClient && questionnaireName) {
const questionnaireId = questionnaireName.split('/').pop();
if (questionnaireId) {
return fhirClient.searchResources<Bundle>({
resourceType: 'Questionnaire',
searchParams: [{ name: '_id', value: questionnaireId }],
});
}
throw new Error('questionnaireName not valid');
}
throw new Error('fhir client not defined or questionnaireName not provided');
},
{
onSuccess,
onError: (err) => {
console.error('Error during fetching get telemed appointment: ', err);
},
enabled: !!questionnaireName,
},
);
};

export const useGetMeetingData = (
getAccessTokenSilently: () => Promise<string>,
onSuccess: (data: MeetingData) => void,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Appointment, Encounter, Location, Patient, QuestionnaireResponse } from 'fhir/r4';
import { Appointment, Encounter, Location, Patient, Questionnaire, QuestionnaireResponse } from 'fhir/r4';
import { GetChartDataResponse } from 'ehr-utils';
import { create } from 'zustand';

Expand All @@ -8,6 +8,7 @@ type AppointmentState = {
location: Location | undefined;
encounter: Encounter;
questionnaireResponse: QuestionnaireResponse | undefined;
questionnaire: Questionnaire | undefined;
patientPhotoUrls: string[];
schoolWorkNoteUrls: string[];
isAppointmentLoading: boolean;
Expand All @@ -28,6 +29,7 @@ const APPOINTMENT_INITIAL: AppointmentState = {
location: undefined,
encounter: {} as Encounter,
questionnaireResponse: undefined,
questionnaire: undefined,
patientPhotoUrls: [],
schoolWorkNoteUrls: [],
isAppointmentLoading: false,
Expand Down
4 changes: 4 additions & 0 deletions packages/telemed-ehr/zambdas/src/shared/accessPolicies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const ADMINISTRATOR_RULES = [
'FHIR:Coverage',
'FHIR:RelatedPerson',
'FHIR:Organization',
'FHIR:Questionnaire',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
Expand Down Expand Up @@ -77,6 +78,7 @@ export const MANAGER_RULES = [
'FHIR:Coverage',
'FHIR:RelatedPerson',
'FHIR:Organization',
'FHIR:Questionnaire',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
Expand Down Expand Up @@ -125,6 +127,7 @@ export const STAFF_RULES = [
'FHIR:Organization',
'FHIR:Location',
'FHIR:Encounter',
'FHIR:Questionnaire',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
Expand Down Expand Up @@ -155,6 +158,7 @@ export const PROVIDER_RULES = [
'FHIR:Organization',
'FHIR:Location',
'FHIR:Encounter',
'FHIR:Questionnaire',
'FHIR:QuestionnaireResponse',
'FHIR:DocumentReference',
'FHIR:Person',
Expand Down

0 comments on commit 04c4ad9

Please sign in to comment.