Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/aehrc/smart-forms
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Sep 11, 2024
2 parents 5fa9a6e + 89bff2a commit 262d0e9
Show file tree
Hide file tree
Showing 19 changed files with 2,706 additions and 1,902 deletions.
22 changes: 11 additions & 11 deletions apps/demo-renderer-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions apps/smart-forms-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
"dependencies": {
"@aehrc/sdc-assemble": "^1.3.1",
"@aehrc/sdc-populate": "^2.3.1",
"@aehrc/smart-forms-renderer": "^0.39.0",
"@aehrc/smart-forms-renderer": "^0.40.0",
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.11.5",
"@emotion/styled": "^11.13.0",
"@fontsource/material-icons": "^5.0.18",
"@fontsource/roboto": "^5.0.12",
"@iconify/react": "^4.1.1",
Expand Down Expand Up @@ -84,7 +84,7 @@
"@storybook/addon-links": "^8.0.3",
"@storybook/addon-onboarding": "^8.0.0",
"@storybook/addon-styling": "^1.3.7",
"@storybook/blocks": "^7.5.2",
"@storybook/blocks": "^8.2.9",
"@storybook/react": "^8.2.6",
"@storybook/react-vite": "^7.4.5",
"@storybook/testing-library": "^0.2.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/smart-forms-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import DebugModeContextProvider from './contexts/DebugModeContext.tsx';
function App() {
return (
<ThemeProvider>
<SnackbarProvider>
<SnackbarProvider maxSnack={1}>
<SmartClientContextProvider>
<DebugModeContextProvider>
<CssBaseline />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2024 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 Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown';
import type { SetStateAction } from 'react';
import { useState } from 'react';
import { CircularProgress, Fade, ListItemIcon, ListItemText, Tooltip } from '@mui/material';
import Typography from '@mui/material/Typography';
import { useExtractOperationStore } from '../stores/extractOperationStore.ts';
import { FORMS_SERVER_URL } from '../../../globals.ts';
import Iconify from '../../../components/Iconify/Iconify.tsx';

interface ExtractMenuProps {
isExtracting: boolean;
onObservationExtract: () => void;
onStructureMapExtract: () => void;
}

function ExtractMenu(props: ExtractMenuProps) {
const { isExtracting, onObservationExtract, onStructureMapExtract } = props;

const targetStructureMap = useExtractOperationStore.use.targetStructureMap();

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const handleClick = (event: { currentTarget: SetStateAction<HTMLElement | null> }) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};

const structuredMapExtractEnabled = targetStructureMap !== null;
const structuredMapToolTipText = structuredMapExtractEnabled
? ''
: `The current questionnaire does not have a target StructureMap for $extract, or the target StructureMap cannot be found on ${FORMS_SERVER_URL}`;

return (
<>
<Button
id="extract-button"
aria-controls={open ? 'extract-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
sx={{ my: 0.5 }}
endIcon={
isExtracting ? (
<CircularProgress size={16} color="inherit" sx={{ ml: 0.25 }} />
) : (
<KeyboardArrowDown />
)
}
disabled={isExtracting}
onClick={handleClick}>
Extract
</Button>
<Menu
id="extract-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'extract-button'
}}>
<MenuItem
onClick={() => {
onObservationExtract();
handleClose();
}}>
<ListItemIcon>
<Iconify icon="ion:binoculars" />
</ListItemIcon>
<ListItemText>Observation-based $extract</ListItemText>
</MenuItem>
<Tooltip title="Not implemented" placement="right">
<span>
<MenuItem disabled={true}>
<ListItemIcon>
<Iconify icon="ion:document-text-outline" />
</ListItemIcon>
<ListItemText>Definition-based $extract</ListItemText>
</MenuItem>
</span>
</Tooltip>
<Tooltip title={structuredMapToolTipText} placement="right">
<span>
<MenuItem
disabled={!structuredMapExtractEnabled}
onClick={() => {
onStructureMapExtract();
handleClose();
}}>
<ListItemIcon>
<Iconify icon="tabler:transform" />
</ListItemIcon>
<ListItemText>StructureMap-based $extract</ListItemText>
</MenuItem>
</span>
</Tooltip>
</Menu>

{isExtracting ? (
<Fade in={true} timeout={100}>
<Typography variant="body2" color="text.secondary">
Performing extraction...
</Typography>
</Fade>
) : null}
</>
);
}

export default ExtractMenu;
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { isQuestionnaire } from '../typePredicates/isQuestionnaire.ts';
import type { BuildState } from '../types/buildState.interface.ts';
import { useLocalStorage } from 'usehooks-ts';
import {
extractObservationBased,
removeEmptyAnswersFromResponse,
useQuestionnaireResponseStore,
useQuestionnaireStore
Expand Down Expand Up @@ -152,8 +153,30 @@ function Playground() {
};
}

// $extract
async function handleExtract() {
// Observation $extract
function handleObservationExtract() {
const observations = extractObservationBased(sourceQuestionnaire, updatableResponse);
setExtractedResource(observations);

if (observations.length > 0) {
enqueueSnackbar(
'Observation-based extraction successful. See Advanced Properties > Extracted to view extracted resource.',
{
preventDuplicate: true,
action: <CloseSnackbar />,
autoHideDuration: 8000
}
);
} else {
enqueueSnackbar('Extraction performed but no observations are extracted.', {
preventDuplicate: true,
action: <CloseSnackbar />
});
}
}

// StructureMap $extract
async function handleStructureMapExtract() {
setExtracting(true);

const response = await fetch(defaultExtractEndpoint + '/QuestionnaireResponse/$extract', {
Expand Down Expand Up @@ -209,7 +232,8 @@ function Playground() {
patient={patient}
user={user}
isExtracting={isExtracting}
onExtract={handleExtract}
onObservationExtract={handleObservationExtract}
onStructureMapExtract={handleStructureMapExtract}
/>
) : buildingState === 'building' ? (
<PopulationProgressSpinner message={'Building form'} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,30 @@ import type { Patient, Practitioner } from 'fhir/r4';
import { Box, Typography } from '@mui/material';
import useLaunchContextNames from '../hooks/useLaunchContextNames.ts';
import { TERMINOLOGY_SERVER_URL } from '../../../globals.ts';
import ExtractButtonForPlayground from './ExtractButtonForPlayground.tsx';
import { useExtractOperationStore } from '../stores/extractOperationStore.ts';
import { buildFormWrapper } from '../../../utils/manageForm.ts';
import ExtractMenu from './ExtractMenu.tsx';

interface PlaygroundRendererProps {
endpointUrl: string | null;
patient: Patient | null;
user: Practitioner | null;
isExtracting: boolean;
onExtract: () => void;
onObservationExtract: () => void;
onStructureMapExtract: () => void;
}

function PlaygroundRenderer(props: PlaygroundRendererProps) {
const { endpointUrl, patient, user, isExtracting, onExtract } = props;
const { endpointUrl, patient, user, isExtracting, onObservationExtract, onStructureMapExtract } =
props;

const sourceQuestionnaire = useQuestionnaireStore.use.sourceQuestionnaire();
const targetStructureMap = useExtractOperationStore.use.targetStructureMap();
const setPopulatedContext = useQuestionnaireStore.use.setPopulatedContext();

const [isPopulating, setIsPopulating] = useState(false);

const { patientName, userName } = useLaunchContextNames(patient, user);

const prePopEnabled = endpointUrl !== null && patient !== null;
const extractEnabled = targetStructureMap !== null;

function handlePrepopulate() {
if (!prePopEnabled) {
Expand Down Expand Up @@ -97,10 +96,10 @@ function PlaygroundRenderer(props: PlaygroundRendererProps) {
isPopulating={isPopulating}
onPopulate={handlePrepopulate}
/>
<ExtractButtonForPlayground
extractEnabled={extractEnabled}
<ExtractMenu
isExtracting={isExtracting}
onExtract={onExtract}
onObservationExtract={onObservationExtract}
onStructureMapExtract={onStructureMapExtract}
/>
<Box flexGrow={1} />

Expand Down
Loading

0 comments on commit 262d0e9

Please sign in to comment.