From bae53c800d677c0a01b75a86345ce52f311b6b53 Mon Sep 17 00:00:00 2001 From: Sean Fong Date: Wed, 2 Aug 2023 15:04:18 +0930 Subject: [PATCH] Support embedded launch intent from EHR --- apps/smart-forms-app/package.json | 2 +- .../Buttons/CreateNewResponseButton.tsx | 11 +-- .../Buttons/OpenResponseButton.tsx | 1 + .../Collapsible/FormBodySingleCollapsible.tsx | 2 +- .../components/RendererEmbeddedSpeedDial.tsx | 45 ++++++----- .../components/Authorisation.tsx | 7 ++ .../features/smartAppLaunch/utils/launch.ts | 14 +++- .../src/features/viewer/ResponsePreview.tsx | 2 +- .../src/theme/overrides/SpeedDial.ts | 2 +- package-lock.json | 78 +++++++++---------- 10 files changed, 89 insertions(+), 75 deletions(-) diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json index bff48f9e0..98eb45d2b 100644 --- a/apps/smart-forms-app/package.json +++ b/apps/smart-forms-app/package.json @@ -47,7 +47,7 @@ "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-markdown": "^8.0.7", - "react-router-dom": "6.9.0", + "react-router-dom": "6.8.1", "react-spinners": "^0.13.8", "react-to-print": "^2.14.13", "sdc-assemble": "^1.0.1", diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx index 2f67a0d9b..909f2df14 100644 --- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx +++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/Buttons/CreateNewResponseButton.tsx @@ -28,7 +28,6 @@ import EditNoteIcon from '@mui/icons-material/EditNote'; function CreateNewResponseButton() { const smartClient = useConfigStore((state) => state.smartClient); - const setLaunchIntent = useConfigStore((state) => state.setLaunchIntent); const buildSourceQuestionnaire = useQuestionnaireStore((state) => state.buildSourceQuestionnaire); const buildSourceResponse = useQuestionnaireResponseStore((state) => state.buildSourceResponse); @@ -58,13 +57,6 @@ function CreateNewResponseButton() { const questionnaireResponse = createQuestionnaireResponse(questionnaire); buildSourceResponse(questionnaireResponse); - // FIXME this is a hack to test tabs rendering in an embedded browser - if (questionnaire.id === 'TestIntentCollapsible') { - setLaunchIntent('embedded-browser'); - } else { - setLaunchIntent(null); - } - navigate('/renderer'); setIsLoading(false); } @@ -89,7 +81,8 @@ function CreateNewResponseButton() { fontSize={9} variant="subtitle2" color={buttonIsDisabled ? 'text.disabled' : 'secondary'} - sx={{ mt: -0.5, mb: 0.5 }}> + sx={{ mt: -0.5, mb: 0.5 }} + textAlign="center"> Create response diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx index 3c0127ac8..b2f9b5cb8 100644 --- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx +++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/ResponsesPage/Buttons/OpenResponseButton.tsx @@ -144,6 +144,7 @@ function OpenResponseButton(props: Props) { fontSize={9} variant="subtitle2" color={buttonIsDisabled ? 'text.disabled' : 'secondary'} + textAlign="center" sx={{ mt: -0.5, mb: 0.5 }}> Open Response diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/Collapsible/FormBodySingleCollapsible.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/Collapsible/FormBodySingleCollapsible.tsx index d9b5abc6d..01094c9b2 100644 --- a/apps/smart-forms-app/src/features/renderer/components/FormPage/Collapsible/FormBodySingleCollapsible.tsx +++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/Collapsible/FormBodySingleCollapsible.tsx @@ -51,7 +51,7 @@ const FormBodySingleCollapsible = memo(function FormBodySingleCollapsible( return ( onToggleExpand(index)}> state.smartClient); const patient = useConfigStore((state) => state.patient); const user = useConfigStore((state) => state.user); + const launchQuestionnaire = useConfigStore((state) => state.launchQuestionnaire); const sourceQuestionnaire = useQuestionnaireStore((state) => state.sourceQuestionnaire); const enableWhenIsActivated = useQuestionnaireStore((state) => state.enableWhenIsActivated); @@ -58,11 +60,6 @@ function RendererEmbeddedSpeedDial(props: RendererEmbeddedSpeedDialProps) { const navigate = useNavigate(); const { closeSnackbar } = useSnackbar(); - function handleViewResponses() { - closeSnackbar(); - navigate('/dashboard/responses'); - } - function handlePreview() { if (location.pathname === '/renderer/preview') { navigate('/renderer'); @@ -106,6 +103,7 @@ function RendererEmbeddedSpeedDial(props: RendererEmbeddedSpeedDialProps) { } const showSaveButtons = smartClient && sourceQuestionnaire.item; + const launchQuestionnaireExists = !!launchQuestionnaire; if (isPopulating) { return null; @@ -122,20 +120,27 @@ function RendererEmbeddedSpeedDial(props: RendererEmbeddedSpeedDialProps) { '& .MuiFab-primary': { width: 46, height: 46 } }} icon={}> - } - tooltipTitle="Back to Home (Debug)" - tooltipOpen - onClick={() => { - navigate('/dashboard/questionnaires'); - }} - /> - } - tooltipTitle="View Existing Responses" - tooltipOpen - onClick={handleViewResponses} - /> + {launchQuestionnaireExists ? ( + } + tooltipTitle="View Existing Responses" + tooltipOpen + onClick={() => { + closeSnackbar(); + navigate('/dashboard/existing'); + }} + /> + ) : ( + } + tooltipTitle="Back to Home" + tooltipOpen + onClick={() => { + closeSnackbar(); + navigate('/dashboard/questionnaires'); + }} + /> + )} : } tooltipTitle={location.pathname === '/renderer/preview' ? 'Editor' : 'Preview'} diff --git a/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx b/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx index 4800462eb..2aebd7bfc 100644 --- a/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx +++ b/apps/smart-forms-app/src/features/smartAppLaunch/components/Authorisation.tsx @@ -19,6 +19,7 @@ import { useEffect, useReducer } from 'react'; import { oauth2 } from 'fhirclient'; import { getEncounter, + getLaunchIntent, getPatient, getQuestionnaireContext, getQuestionnaireReferences, @@ -76,6 +77,7 @@ function Authorisation() { const setUser = useConfigStore((state) => state.setUser); const setEncounter = useConfigStore((state) => state.setEncounter); const setLaunchQuestionnaire = useConfigStore((state) => state.setLaunchQuestionnaire); + const setLaunchIntent = useConfigStore((state) => state.setLaunchIntent); const buildSourceQuestionnaire = useQuestionnaireStore((state) => state.buildSourceQuestionnaire); @@ -174,6 +176,11 @@ function Authorisation() { } else { dispatch({ type: 'UPDATE_HAS_QUESTIONNAIRE', payload: false }); } + + // Set launch intent if available + if (getLaunchIntent(client)) { + setLaunchIntent(getLaunchIntent(client)); + } }) .catch((error: Error) => { // Prompt user to launch app if app is unlaunched diff --git a/apps/smart-forms-app/src/features/smartAppLaunch/utils/launch.ts b/apps/smart-forms-app/src/features/smartAppLaunch/utils/launch.ts index 2dcae14d3..5edadac5a 100644 --- a/apps/smart-forms-app/src/features/smartAppLaunch/utils/launch.ts +++ b/apps/smart-forms-app/src/features/smartAppLaunch/utils/launch.ts @@ -51,12 +51,13 @@ interface FhirContext { identifier?: Identifier; } -interface tokenResponseWithFhirContext extends fhirclient.TokenResponse { - fhirContext: FhirContext[] | undefined; +interface tokenResponseCustomised extends fhirclient.TokenResponse { + fhirContext?: FhirContext[]; + intent?: string; } export function getQuestionnaireReferences(client: Client): FhirContext[] { - const tokenResponse = client.state.tokenResponse as tokenResponseWithFhirContext; + const tokenResponse = client.state.tokenResponse as tokenResponseCustomised; const fhirContext = tokenResponse.fhirContext; if (!fhirContext) return []; @@ -115,3 +116,10 @@ export function responseToQuestionnaireResource( console.error(response); } } + +export function getLaunchIntent(client: Client): string | null { + const tokenResponse = client.state.tokenResponse as tokenResponseCustomised; + const launchIntent = tokenResponse.intent; + + return launchIntent ?? null; +} diff --git a/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx b/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx index c43cae978..89c0e155d 100644 --- a/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx +++ b/apps/smart-forms-app/src/features/viewer/ResponsePreview.tsx @@ -68,7 +68,7 @@ function ResponsePreview() { {questionnaire.title ? questionnaire.title : 'Response Preview'} - + Response Preview diff --git a/apps/smart-forms-app/src/theme/overrides/SpeedDial.ts b/apps/smart-forms-app/src/theme/overrides/SpeedDial.ts index 1b568b53d..19b9ce1f8 100644 --- a/apps/smart-forms-app/src/theme/overrides/SpeedDial.ts +++ b/apps/smart-forms-app/src/theme/overrides/SpeedDial.ts @@ -27,7 +27,7 @@ export default function SpeedDial(theme: Theme) { fontSize: theme.typography.subtitle2.fontSize, fontWeight: theme.typography.subtitle2.fontWeight, boxShadow: theme.customShadows.z8, - maxWidth: 150, + maxWidth: 200, whiteSpace: 'nowrap' } } diff --git a/package-lock.json b/package-lock.json index 6576111b5..e0757cb73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "react-dom": "^18.2.0", "react-helmet": "^6.1.0", "react-markdown": "^8.0.7", - "react-router-dom": "6.9.0", + "react-router-dom": "6.8.1", "react-spinners": "^0.13.8", "react-to-print": "^2.14.13", "sdc-assemble": "^1.0.1", @@ -92,12 +92,50 @@ "yui-lint": "^0.2.0" } }, + "apps/smart-forms-app/node_modules/@remix-run/router": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz", + "integrity": "sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA==", + "engines": { + "node": ">=14" + } + }, "apps/smart-forms-app/node_modules/@types/fhir": { "version": "0.0.37", "resolved": "https://registry.npmjs.org/@types/fhir/-/fhir-0.0.37.tgz", "integrity": "sha512-fR1y6tPfDmxYDWN4JkJhuI5F5QpbaFVSoNo3pu9A6nzuoojANqg0UBnNZTVegTz/MilV3PSjyvFe6/vO55geKA==", "dev": true }, + "apps/smart-forms-app/node_modules/react-router": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz", + "integrity": "sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg==", + "dependencies": { + "@remix-run/router": "1.3.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "apps/smart-forms-app/node_modules/react-router-dom": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz", + "integrity": "sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ==", + "dependencies": { + "@remix-run/router": "1.3.2", + "react-router": "6.8.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "deployment/assemble": { "version": "1.0.1", "devDependencies": { @@ -6513,14 +6551,6 @@ "optional": true, "peer": true }, - "node_modules/@remix-run/router": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.4.0.tgz", - "integrity": "sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==", - "engines": { - "node": ">=14" - } - }, "node_modules/@rollup/pluginutils": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", @@ -20164,36 +20194,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-router": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.9.0.tgz", - "integrity": "sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==", - "dependencies": { - "@remix-run/router": "1.4.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.9.0.tgz", - "integrity": "sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==", - "dependencies": { - "@remix-run/router": "1.4.0", - "react-router": "6.9.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz",