diff --git a/apps/smart-forms-app/.env.production b/apps/smart-forms-app/.env.production index fbdf8011f..a58e3ecc7 100644 --- a/apps/smart-forms-app/.env.production +++ b/apps/smart-forms-app/.env.production @@ -2,7 +2,7 @@ VITE_ONTOSERVER_URL=https://r4.ontoserver.csiro.au/fhir VITE_FORMS_SERVER_URL=https://smartforms.csiro.au/api/fhir VITE_LAUNCH_SCOPE=fhirUser online_access openid profile patient/Condition.rs patient/Observation.rs launch patient/Encounter.rs patient/QuestionnaireResponse.cruds patient/Patient.rs -VITE_LAUNCH_CLIENT_ID=csiro-smart-forms +VITE_LAUNCH_CLIENT_ID=9cbba311-bf9b-4200-8dd3-8459046fc522 VITE_IN_APP_POPULATE=true diff --git a/apps/smart-forms-app/src/api/saveQr.ts b/apps/smart-forms-app/src/api/saveQr.ts index a8e19c9d7..4900ac20c 100644 --- a/apps/smart-forms-app/src/api/saveQr.ts +++ b/apps/smart-forms-app/src/api/saveQr.ts @@ -137,17 +137,15 @@ function addQuestionnaireReference( questionnaireResponseToSave: QuestionnaireResponse, endpointUrl: string ): QuestionnaireResponse { - let questionnaireReference: string; if (endpointUrl.includes('https://launch.smarthealthit.org/v/r4/fhir')) { - // Plugging questionnaire.id in because SMART Health IT has these weird requirements for canonicals - questionnaireReference = questionnaire.id ? `Questionnaire/${questionnaire.id}` : ''; - } else { - questionnaireReference = questionnaire.url ?? ''; + // Plugging questionnaire.id in because SMART Health IT requires QRs to have Questionnaire/{id} as reference + questionnaireResponseToSave.questionnaire = questionnaire.id + ? `Questionnaire/${questionnaire.id}` + : ''; } // Add questionnaire reference if it is not an empty string - if (questionnaireReference) { - questionnaireResponseToSave.questionnaire = questionnaireReference; + if (questionnaireResponseToSave.questionnaire) { questionnaireResponseToSave._questionnaire = { extension: [ { diff --git a/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx b/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx index c5c66306d..7b08eeb04 100644 --- a/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx +++ b/apps/smart-forms-app/src/features/prepopulate/hooks/usePopulate.tsx @@ -90,8 +90,8 @@ function usePopulate(spinner: RendererSpinner, onStopSpinner: () => void): void onStopSpinner(); if (hasWarnings) { enqueueSnackbar( - 'Form partially populated, there might be issues while populating the form. View console for details.', - { action: , variant: 'warning' } + 'Form partially populated, there might be pre-population issues. View console for details.', + { action: } ); return; } diff --git a/apps/smart-forms-app/src/features/renderer/components/RendererActions/RepopulateAction.tsx b/apps/smart-forms-app/src/features/renderer/components/RendererActions/RepopulateAction.tsx index effffe261..69ad9f140 100644 --- a/apps/smart-forms-app/src/features/renderer/components/RendererActions/RepopulateAction.tsx +++ b/apps/smart-forms-app/src/features/renderer/components/RendererActions/RepopulateAction.tsx @@ -99,7 +99,7 @@ function RepopulateAction(props: RepopulateActionProps) { if (hasWarnings) { enqueueSnackbar( 'There might be issues while retrieving the latest information, data is partially retrieved. View console for details.', - { action: , variant: 'warning' } + { action: } ); return; } diff --git a/packages/smart-forms-renderer/README.md b/packages/smart-forms-renderer/README.md index 959054ed1..373553fe9 100644 --- a/packages/smart-forms-renderer/README.md +++ b/packages/smart-forms-renderer/README.md @@ -28,7 +28,7 @@ export default function App () { ) } ``` -Note: The SmartFormsRenderer component is extremely basic and experimental, do not use it in production! +Note: The SmartFormsRenderer component trades customisability for simplicity. If you need more control over the rendering engine, refer to the Advanced Usage section. ### SmartFormsRenderer Props diff --git a/packages/smart-forms-renderer/package.json b/packages/smart-forms-renderer/package.json index 5346c6d06..0f489e36d 100644 --- a/packages/smart-forms-renderer/package.json +++ b/packages/smart-forms-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@aehrc/smart-forms-renderer", - "version": "0.10.2", + "version": "0.10.3", "description": "FHIR Structured Data Captured (SDC) rendering engine for Smart Forms", "main": "lib/index.js", "scripts": { diff --git a/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx b/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx index c053a4373..1f12da0f5 100644 --- a/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx +++ b/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx @@ -45,14 +45,20 @@ const FormBodyTabListWrapper = memo(function FormBodyTabListWrapper( return ( - + {completedDisplayItemExists ? ( - + <> + + + ) : null} - { - buildSourceResponse(createEmptyQuestionnaireResponse(questionnaire)); + buildSourceResponse(initialiseQuestionnaireResponse(questionnaire)); if (questionnaireResponse) { const updatedResponse = updatePopulatedProperties(questionnaireResponse); diff --git a/packages/smart-forms-renderer/src/index.ts b/packages/smart-forms-renderer/src/index.ts index 11e1c3cc0..42e3aeea6 100644 --- a/packages/smart-forms-renderer/src/index.ts +++ b/packages/smart-forms-renderer/src/index.ts @@ -1,6 +1,6 @@ import { questionnaireResponseStore, questionnaireStore } from './stores'; import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4'; -import { createEmptyQuestionnaireResponse } from './utils/qrItem'; +import { initialiseQuestionnaireResponse } from './utils/qrItem'; import { removeHiddenAnswers } from './utils/removeHidden'; import type { ItemToRepopulate } from './utils/repopulateItems'; import { getItemsToRepopulate } from './utils/repopulateItems'; @@ -25,13 +25,16 @@ export async function buildForm( await questionnaireStore.getState().buildSourceQuestionnaire(questionnaire); if (!questionnaireResponse) { - questionnaireResponseStore - .getState() - .buildSourceResponse(createEmptyQuestionnaireResponse(questionnaire)); + const initialisedQuestionnaireResponse = initialiseQuestionnaireResponse(questionnaire); + questionnaireResponseStore.getState().buildSourceResponse(initialisedQuestionnaireResponse); return; } - questionnaireResponseStore.getState().buildSourceResponse(questionnaireResponse); + const initialisedQuestionnaireResponse = initialiseQuestionnaireResponse( + questionnaire, + questionnaireResponse + ); + questionnaireResponseStore.getState().buildSourceResponse(initialisedQuestionnaireResponse); questionnaireStore.getState().updatePopulatedProperties(questionnaireResponse); } diff --git a/packages/smart-forms-renderer/src/utils/qrItem.ts b/packages/smart-forms-renderer/src/utils/qrItem.ts index 0958b2d25..ada7ddc4e 100644 --- a/packages/smart-forms-renderer/src/utils/qrItem.ts +++ b/packages/smart-forms-renderer/src/utils/qrItem.ts @@ -25,20 +25,28 @@ import type { import type { QrRepeatGroup } from '../interfaces/repeatGroup.interface'; /** - * Create an empty questionnaireResponse from a given questionnaire + * Initialise a conformant questionnaireResponse from a given questionnaire + * optionally takes in an existing questionnaireResponse to be initialised * * @author Sean Fong */ -export function createEmptyQuestionnaireResponse( - questionnaire: Questionnaire -): QuestionnaireResponse { - const questionnaireResponse: QuestionnaireResponse = { - resourceType: 'QuestionnaireResponse', - status: 'in-progress' - }; - const firstTopLevelItem = questionnaire?.item?.[0]; +export function initialiseQuestionnaireResponse( + questionnaire: Questionnaire, + questionnaireResponse?: QuestionnaireResponse +) { + if (!questionnaireResponse) { + questionnaireResponse = { + resourceType: 'QuestionnaireResponse', + status: 'in-progress' + }; + } + + if (!questionnaireResponse.status) { + questionnaireResponse.status = 'in-progress'; + } - if (firstTopLevelItem) { + const firstTopLevelItem = questionnaire?.item?.[0]; + if (firstTopLevelItem && !questionnaireResponse.item) { questionnaireResponse.item = [ { linkId: firstTopLevelItem.linkId, @@ -48,13 +56,31 @@ export function createEmptyQuestionnaireResponse( ]; } - if (questionnaire.id) { - questionnaireResponse.questionnaire = `Questionnaire/${questionnaire.id}`; + if (!questionnaireResponse.questionnaire) { + questionnaireResponse.questionnaire = setQuestionnaireReference(questionnaire); } return questionnaireResponse; } +export function setQuestionnaireReference(questionnaire: Questionnaire) { + // Use {url}|{version} - the ideal way + if (questionnaire.url) { + let questionnaireReference = questionnaire.url; + if (questionnaire.version) { + questionnaireReference += '|' + questionnaire.version; + } + return questionnaireReference; + } + + // If no url exists, use Questionnaire/{id} + if (questionnaire.id) { + return `Questionnaire/${questionnaire.id}`; + } + + return ''; +} + /** * Remove items with no answers from a given questionnaireResponse * Also remove any starting or trailing whitespace from valueStrings