Skip to content

Commit

Permalink
Merge pull request #360 from aehrc/feature/questionnaire-launch
Browse files Browse the repository at this point in the history
Feature/questionnaire launch
  • Loading branch information
fongsean authored Aug 2, 2023
2 parents 579a33a + d6ab3bb commit cc52e37
Show file tree
Hide file tree
Showing 44 changed files with 924 additions and 307 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 @@ -27,7 +27,6 @@ import {
import useDebounce from '../../../../renderer/hooks/useDebounce.ts';
import useFetchQuestionnaires from '../../../hooks/useFetchQuestionnaires.ts';
import { createQuestionnaireTableColumns } from '../../../utils/tableColumns.ts';
import type { Questionnaire } from 'fhir/r4';
import QuestionnaireTableView from './QuestionnaireTableView.tsx';

function QuestionnaireTable() {
Expand All @@ -39,26 +38,15 @@ function QuestionnaireTable() {
const [searchInput, setSearchInput] = useState('');
const debouncedInput = useDebounce(searchInput, 300);

const {
remoteQuestionnaires,
questionnaireListItems,
fetchStatus,
fetchError,
isInitialLoading,
isFetching
} = useFetchQuestionnaires(searchInput, debouncedInput);
const { questionnaires, fetchStatus, fetchError, isInitialLoading, isFetching } =
useFetchQuestionnaires(searchInput, debouncedInput);

const columns = useMemo(() => createQuestionnaireTableColumns(), []);

const [sorting, setSorting] = useState<SortingState>([
{
id: 'date',
desc: true
}
]);
const [sorting, setSorting] = useState<SortingState>([]);

const table = useReactTable({
data: questionnaireListItems,
data: questionnaires,
columns: columns,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
Expand All @@ -70,24 +58,13 @@ function QuestionnaireTable() {
});

function handleRowClick(id: string) {
const selectedItem = questionnaireListItems.find((item) => item.id === id);
const questionnaire = questionnaires.find((questionnaire) => questionnaire.id === id);

if (selectedItem) {
if (selectedItem.id === selectedQuestionnaire?.listItem.id) {
if (questionnaire) {
if (questionnaire.id === selectedQuestionnaire?.id) {
setSelectedQuestionnaire(null);
} else {
const resource = remoteQuestionnaires?.entry?.find(
(entry) => entry.resource?.id === id
)?.resource;

if (resource) {
setSelectedQuestionnaire({
listItem: selectedItem,
resource: resource as Questionnaire
});
} else {
setSelectedQuestionnaire(null);
}
setSelectedQuestionnaire(questionnaire);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,21 @@ import QuestionnaireTableRow from './TableComponents/QuestionnaireTableRow.tsx';
import QuestionnaireListFeedback from './TableComponents/QuestionnaireListFeedback.tsx';
import DashboardTablePagination from '../DashboardTablePagination.tsx';
import type { Table } from '@tanstack/react-table';
import type {
QuestionnaireListItem,
SelectedQuestionnaire
} from '../../../types/list.interface.ts';
import type { Questionnaire } from 'fhir/r4';
import { createQuestionnaireListItem } from '../../../utils/dashboard.ts';

interface QuestionnaireTableViewProps {
table: Table<QuestionnaireListItem>;
table: Table<Questionnaire>;
searchInput: string;
debouncedInput: string;
fetchStatus: 'error' | 'success' | 'loading';
isInitialLoading: boolean;
isFetching: boolean;
fetchError: unknown;
selectedQuestionnaire: SelectedQuestionnaire | null;
selectedQuestionnaire: Questionnaire | null;
onSearch: (input: string) => void;
onRowClick: (id: string) => void;
onSelectQuestionnaire: (selected: SelectedQuestionnaire | null) => void;
onSelectQuestionnaire: (selected: Questionnaire | null) => void;
}

function QuestionnaireTableView(props: QuestionnaireTableViewProps) {
Expand All @@ -64,7 +62,7 @@ function QuestionnaireTableView(props: QuestionnaireTableViewProps) {
return (
<>
<QuestionnaireListToolbar
selected={selectedQuestionnaire?.listItem}
selected={selectedQuestionnaire}
searchInput={searchInput}
onClearSelection={() => onSelectQuestionnaire(null)}
onSearch={onSearch}
Expand All @@ -74,16 +72,17 @@ function QuestionnaireTableView(props: QuestionnaireTableViewProps) {
<MuiTable stickyHeader>
<DashboardTableHead headers={headers} />
<TableBody>
{table.getRowModel().rows.map((row) => {
{table.getRowModel().rows.map((row, index) => {
const rowData = row.original;
const isSelected = selectedQuestionnaire?.listItem.id === rowData.id;
const listItem = createQuestionnaireListItem(rowData, index);
const isSelected = selectedQuestionnaire?.id === listItem.id;

return (
<QuestionnaireTableRow
key={rowData.id}
row={rowData}
key={listItem.id}
row={listItem}
isSelected={isSelected}
onRowClick={() => onRowClick(rowData.id)}
onRowClick={() => onRowClick(listItem.id)}
/>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@

import { alpha, styled } from '@mui/material/styles';
import { OutlinedInput, Toolbar } from '@mui/material';
import type { QuestionnaireResponse } from 'fhir/r4';
import type {
QuestionnaireListItem,
ResponseListItem,
SelectedQuestionnaire
} from '../../../../types/list.interface.ts';
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';

export const StyledRoot = styled(Toolbar)(({ theme }) => ({
height: 80,
Expand All @@ -49,8 +44,8 @@ export const StyledSearch = styled(OutlinedInput)(({ theme }) => ({
}));

export function getResponseToolBarColors(
selected: ResponseListItem | undefined,
selectedQuestionnaire: SelectedQuestionnaire | null,
selected: QuestionnaireResponse | null,
selectedQuestionnaire: Questionnaire | null,
existingResponses: QuestionnaireResponse[]
) {
return {
Expand All @@ -68,11 +63,25 @@ export function getResponseToolBarColors(
};
}

export function getQuestionnaireToolBarColors(selected: QuestionnaireListItem | undefined) {
export function getQuestionnaireToolBarColors(selected: Questionnaire | null) {
return {
...(selected && {
color: 'primary.main',
bgcolor: 'pale.primary'
})
};
}

export function getExistingResponsesToolBarColors(selected: QuestionnaireResponse | null) {
return {
...(selected
? {
color: 'primary.main',
bgcolor: 'pale.primary'
}
: {
color: 'secondary.main',
bgcolor: 'pale.secondary'
})
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ import {
StyledRoot,
StyledSearch
} from './QuestionnaireListToolbar.styles.ts';
import type { QuestionnaireListItem } from '../../../../types/list.interface.ts';
import { StyledAlert } from '../../../../../../components/Nav/Nav.styles.ts';
import QuestionnaireListToolbarButtons from './QuestionnaireListToolbarButtons.tsx';
import type { Questionnaire } from 'fhir/r4';

interface QuestionnaireListToolbarProps {
selected: QuestionnaireListItem | undefined;
selected: Questionnaire | null;
searchInput: string;
onClearSelection: () => void;
onSearch: (searchInput: string) => void;
Expand All @@ -46,7 +46,7 @@ function QuestionnaireListToolbar(props: QuestionnaireListToolbarProps) {
return (
<StyledRoot sx={{ ...toolBarColors }}>
{selected ? (
<Typography variant="subtitle1">{selected.title} selected</Typography>
<Typography variant="subtitle1">{selected.title ?? 'Undefined title'} selected</Typography>
) : (
<StyledSearch
value={searchInput}
Expand Down
Loading

0 comments on commit cc52e37

Please sign in to comment.