Skip to content

Commit

Permalink
Clean up code dependencies for dashboard tables
Browse files Browse the repository at this point in the history
  • Loading branch information
fongsean committed Aug 1, 2023
1 parent 07630a7 commit 489d98b
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
NavSectionWrapper
} from '../../../../components/Nav/Nav.styles.ts';
import useConfigStore from '../../../../stores/useConfigStore.ts';
import { useLocation } from 'react-router-dom';

interface DashboardNavSectionProps {
onCloseNav: () => void;
Expand All @@ -35,6 +36,11 @@ const DashboardNavSection = memo(function DashboardNavSection(props: DashboardNa
const { onCloseNav } = props;

const smartClient = useConfigStore((state) => state.smartClient);
const { pathname } = useLocation();

if (pathname === '/dashboard/existing') {
return null;
}

return (
<NavSectionWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,20 @@ function CreateNewResponseButton() {
const buildSourceResponse = useQuestionnaireResponseStore((state) => state.buildSourceResponse);

const { selectedQuestionnaire } = useContext(SelectedQuestionnaireContext);
const launchQuestionnaire = useConfigStore((state) => state.launchQuestionnaire);
const questionnaire = selectedQuestionnaire ?? launchQuestionnaire;

const navigate = useNavigate();

const [isLoading, setIsLoading] = useState(false);

async function handleClick() {
if (!selectedQuestionnaire) return;
if (!questionnaire) return;

setIsLoading(true);

const questionnaire = selectedQuestionnaire.resource;

// Post questionnaire to client if it is SMART Health IT and its variants
if (smartClient?.state.serverUrl.includes('/v/r4/fhir')) {
if (smartClient?.state.serverUrl.includes('https://launch.smarthealthit.org/v/r4/fhir')) {
questionnaire.id = questionnaire.id + '-SMARTcopy';
postQuestionnaireToSMARTHealthIT(smartClient, questionnaire);
}
Expand All @@ -69,7 +69,7 @@ function CreateNewResponseButton() {
setIsLoading(false);
}

const buttonIsDisabled = !selectedQuestionnaire?.listItem || isLoading;
const buttonIsDisabled = !questionnaire || isLoading;

return (
<Stack alignItems="center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,70 +15,31 @@
* limitations under the License.
*/

import { useContext, useMemo } from 'react';
import { getClientBundlePromise, getResponsesFromBundle } from '../../../../utils/dashboard.ts';
import { useQuery } from '@tanstack/react-query';
import type { Bundle, QuestionnaireResponse } from 'fhir/r4';
import { useContext } from 'react';
import { SelectedQuestionnaireContext } from '../../../../contexts/SelectedQuestionnaireContext.tsx';
import { useNavigate } from 'react-router-dom';
import { useSnackbar } from 'notistack';
import useConfigStore from '../../../../../../stores/useConfigStore.ts';
import { CircularProgress, IconButton, Stack, Typography } from '@mui/material';
import HighlightOffIcon from '@mui/icons-material/HighlightOff';
import GradingIcon from '@mui/icons-material/Grading';
import useFetchExistingResponses from '../../../../hooks/useFetchExistingResponses.ts';

function ViewExistingResponsesButton() {
const { selectedQuestionnaire, setExistingResponses } = useContext(SelectedQuestionnaireContext);

const smartClient = useConfigStore((state) => state.smartClient);
const patient = useConfigStore((state) => state.patient);

const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();

// search responses from selected questionnaire
let questionnaireRefParam = '';

// Have different questionnaireRef config due to SMART Health IT limitation
if (smartClient) {
const questionnaireRef = smartClient.state.serverUrl.includes('/v/r4/fhir')
? `Questionnaire/${selectedQuestionnaire?.resource?.id}-SMARTcopy`
: selectedQuestionnaire?.resource?.url;

if (questionnaireRef) {
questionnaireRefParam = `questionnaire=${questionnaireRef}&`;
}
}

const patientIdParam = patient?.id ? `patient=${patient?.id}&` : '';
const queryUrl = '/QuestionnaireResponse?' + questionnaireRefParam + patientIdParam;
const { existingResponses, fetchError, isFetching } = useFetchExistingResponses();

const { data, isFetching, error } = useQuery<Bundle>(
['existingResponses', queryUrl],
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
() => getClientBundlePromise(smartClient!, queryUrl),
{
enabled:
!!selectedQuestionnaire &&
questionnaireRefParam !== '' &&
patientIdParam !== '' &&
!!smartClient
}
);

if (error) {
console.error(error);
if (fetchError) {
console.error(fetchError);
enqueueSnackbar('An error occurred while fetching existing responses', {
variant: 'error',
preventDuplicate: true
});
}

const existingResponses: QuestionnaireResponse[] = useMemo(
() => getResponsesFromBundle(data),
[data]
);

function handleClick() {
setExistingResponses(existingResponses);
navigate('/dashboard/responses');
Expand All @@ -95,7 +56,7 @@ function ViewExistingResponsesButton() {
data-test="button-view-responses">
{isFetching && selectedQuestionnaire ? (
<CircularProgress size={20} color="inherit" sx={{ mb: 0.5 }} />
) : data && existingResponses.length === 0 ? (
) : existingResponses.length === 0 ? (
<HighlightOffIcon />
) : (
<GradingIcon />
Expand All @@ -110,7 +71,7 @@ function ViewExistingResponsesButton() {
sx={{ mt: -0.5, mb: 0.5 }}>
{isFetching && selectedQuestionnaire
? 'Loading responses'
: data && existingResponses.length === 0
: existingResponses.length === 0
? 'No responses found'
: 'View responses'}
</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@

import { useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import type { Bundle, Questionnaire } from 'fhir/r4';
import type { Bundle, Questionnaire, QuestionnaireResponse } from 'fhir/r4';
import {
getFormsServerBundleOrQuestionnairePromise,
getReferencedQuestionnaire
} from '../../../../utils/dashboard.ts';
import { useNavigate } from 'react-router-dom';
import { useSnackbar } from 'notistack';
import { postQuestionnaireToSMARTHealthIT } from '../../../../../save/api/saveQr.ts';
import type { SelectedResponse } from '../../../../types/list.interface.ts';
import { assembleIfRequired } from '../../../../../assemble/utils/assemble.ts';
import useConfigStore from '../../../../../../stores/useConfigStore.ts';
import useQuestionnaireStore from '../../../../../../stores/useQuestionnaireStore.ts';
Expand All @@ -34,7 +33,7 @@ import { CircularProgress, IconButton, Stack, Typography } from '@mui/material';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';

interface Props {
selectedResponse: SelectedResponse | null;
selectedResponse: QuestionnaireResponse | null;
}
function OpenResponseButton(props: Props) {
const { selectedResponse } = props;
Expand All @@ -53,7 +52,7 @@ function OpenResponseButton(props: Props) {
const { enqueueSnackbar } = useSnackbar();

// reference could either be a canonical or an id
const questionnaireRef = selectedResponse?.resource.questionnaire;
const questionnaireRef = selectedResponse?.questionnaire;

let queryUrl = '';
if (questionnaireRef) {
Expand Down Expand Up @@ -109,7 +108,7 @@ function OpenResponseButton(props: Props) {
}

// Post questionnaire to client if it is SMART Health IT
if (smartClient?.state.serverUrl.includes('/v/r4/fhir')) {
if (smartClient?.state.serverUrl.includes('https://launch.smarthealthit.org/v/r4/fhir')) {
referencedQuestionnaire.id = referencedQuestionnaire.id + '-SMARTcopy';
postQuestionnaireToSMARTHealthIT(smartClient, referencedQuestionnaire);
}
Expand All @@ -118,8 +117,8 @@ function OpenResponseButton(props: Props) {
await buildSourceQuestionnaire(referencedQuestionnaire);

// Assign questionnaireResponse to questionnaireResponse provider
buildSourceResponse(selectedResponse.resource);
updatePopulatedProperties(selectedResponse.resource);
buildSourceResponse(selectedResponse);
updatePopulatedProperties(selectedResponse);

navigate('/viewer');
setIsLoading(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { alpha, styled } from '@mui/material/styles';
import type { Theme } from '@mui/material';
import { Box } from '@mui/material';

import type { ResponseListItem } from '../../../../types/list.interface.ts';
import type { QuestionnaireResponse } from 'fhir/r4';

const handleColorType = (color: ResponseListItem['status'], theme: Theme) => {
const handleColorType = (color: QuestionnaireResponse['status'], theme: Theme) => {
switch (color) {
case 'in-progress':
return theme.palette.primary.dark;
Expand All @@ -36,7 +36,7 @@ const handleColorType = (color: ResponseListItem['status'], theme: Theme) => {
}
};

const handleBgColorType = (color: ResponseListItem['status'], theme: Theme) => {
const handleBgColorType = (color: QuestionnaireResponse['status'], theme: Theme) => {
switch (color) {
case 'in-progress':
return alpha(theme.palette.primary.main, 0.16);
Expand All @@ -53,7 +53,7 @@ const handleBgColorType = (color: ResponseListItem['status'], theme: Theme) => {

export const ResponseStyledLabel = styled(Box, {
shouldForwardProp: (prop) => prop !== 'color'
})<{ color: ResponseListItem['status'] }>(({ theme, color }) => ({
})<{ color: QuestionnaireResponse['status'] }>(({ theme, color }) => ({
height: 24,
minWidth: 22,
lineHeight: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import { useTheme } from '@mui/material/styles';
import type { SxProps, Theme } from '@mui/material';
import { Box } from '@mui/material';
import { ResponseStyledLabel } from './ResponseLabel.styles.ts';
import type { ResponseListItem } from '../../../../types/list.interface.ts';
import type { QuestionnaireResponse } from 'fhir/r4';

interface Props {
color: ResponseListItem['status'];
color: QuestionnaireResponse['status'];
children: ReactNode;
endIcon?: ReactNode;
startIcon?: ReactNode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@

import type { ReactNode } from 'react';
import { createContext, useState } from 'react';
import type { QuestionnaireResponse } from 'fhir/r4';
import type { SelectedQuestionnaire } from '../types/list.interface.ts';
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';

export interface SelectedQuestionnaireContextType {
selectedQuestionnaire: SelectedQuestionnaire | null;
selectedQuestionnaire: Questionnaire | null;
existingResponses: QuestionnaireResponse[];
setSelectedQuestionnaire: (selected: SelectedQuestionnaire | null) => unknown;
setSelectedQuestionnaire: (selected: Questionnaire | null) => unknown;
setExistingResponses: (responses: QuestionnaireResponse[]) => unknown;
clearSelectedQuestionnaire: () => unknown;
}
Expand All @@ -39,9 +38,7 @@ export const SelectedQuestionnaireContext = createContext<SelectedQuestionnaireC
function SelectedQuestionnaireContextProvider(props: { children: ReactNode }) {
const { children } = props;

const [selectedQuestionnaire, setSelectedQuestionnaire] = useState<SelectedQuestionnaire | null>(
null
);
const [selectedQuestionnaire, setSelectedQuestionnaire] = useState<Questionnaire | null>(null);
const [existingResponses, setExistingResponses] = useState<QuestionnaireResponse[]>([]);

const selectedQuestionnaireContext: SelectedQuestionnaireContextType = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2023 Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { useQuery } from '@tanstack/react-query';
import type { Bundle, QuestionnaireResponse } from 'fhir/r4';
import { getClientBundlePromise, getResponsesFromBundle } from '../utils/dashboard.ts';
import { useContext, useMemo } from 'react';
import useConfigStore from '../../../stores/useConfigStore.ts';
import { SelectedQuestionnaireContext } from '../contexts/SelectedQuestionnaireContext.tsx';

interface useFetchExistingResponsesReturnParams {
existingResponses: QuestionnaireResponse[];
fetchError: unknown;
isFetching: boolean;
}

function useFetchExistingResponses(): useFetchExistingResponsesReturnParams {
const { selectedQuestionnaire } = useContext(SelectedQuestionnaireContext);
const launchQuestionnaire = useConfigStore((state) => state.launchQuestionnaire);
const questionnaire = selectedQuestionnaire ?? launchQuestionnaire;

const smartClient = useConfigStore((state) => state.smartClient);
const patient = useConfigStore((state) => state.patient);

// search responses from selected questionnaire
let questionnaireRefParam = '';

// Have different questionnaireRef config due to SMART Health IT limitation
if (smartClient) {
const questionnaireRef = smartClient.state.serverUrl.includes(
'https://launch.smarthealthit.org/v/r4/fhir'
)
? `Questionnaire/${questionnaire?.id}-SMARTcopy`
: questionnaire?.url;

if (questionnaireRef) {
questionnaireRefParam = `questionnaire=${questionnaireRef}&`;
}
}

const patientIdParam = patient?.id ? `patient=${patient?.id}&` : '';
const queryUrl = '/QuestionnaireResponse?' + questionnaireRefParam + patientIdParam;

const { data, isFetching, error } = useQuery<Bundle>(
['existingResponses', queryUrl],
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
() => getClientBundlePromise(smartClient!, queryUrl),
{
enabled:
!!questionnaire && questionnaireRefParam !== '' && patientIdParam !== '' && !!smartClient
}
);

const existingResponses: QuestionnaireResponse[] = useMemo(
() => getResponsesFromBundle(data),
[data]
);
return { existingResponses, fetchError: error, isFetching };
}

export default useFetchExistingResponses;
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@
*/

import { useQuery } from '@tanstack/react-query';
import type { Bundle } from 'fhir/r4';
import { getFormsServerBundlePromise, getQuestionnaireListItems } from '../utils/dashboard.ts';
import type { Bundle, Questionnaire } from 'fhir/r4';
import { filterQuestionnaires, getFormsServerBundlePromise } from '../utils/dashboard.ts';
import { useMemo } from 'react';
import type { QuestionnaireListItem } from '../types/list.interface.ts';

interface useFetchQuestionnairesReturnParams {
remoteQuestionnaires: Bundle | undefined;
questionnaireListItems: QuestionnaireListItem[];
questionnaires: Questionnaire[];
fetchStatus: 'error' | 'success' | 'loading';
fetchError: unknown;
isInitialLoading: boolean;
Expand All @@ -42,7 +40,7 @@ function useFetchQuestionnaires(
}

const {
data: remoteQuestionnaires,
data: bundle,
status,
isInitialLoading,
error,
Expand All @@ -52,14 +50,10 @@ function useFetchQuestionnaires(
});

// construct questionnaire list items for data display
const questionnaireListItems: QuestionnaireListItem[] = useMemo(
() => getQuestionnaireListItems(remoteQuestionnaires),
[remoteQuestionnaires]
);
const questionnaires: Questionnaire[] = useMemo(() => filterQuestionnaires(bundle), [bundle]);

return {
remoteQuestionnaires,
questionnaireListItems,
questionnaires,
fetchStatus: status,
fetchError: error,
isInitialLoading,
Expand Down
Loading

0 comments on commit 489d98b

Please sign in to comment.