diff --git a/documentation/docusaurus.config.ts b/documentation/docusaurus.config.ts index a7f1b34f..e71ce9c9 100644 --- a/documentation/docusaurus.config.ts +++ b/documentation/docusaurus.config.ts @@ -22,6 +22,8 @@ const config: Config = { onBrokenLinks: 'warn', onBrokenMarkdownLinks: 'warn', + trailingSlash: false, + // Even if you don't use internationalization, you can use this field to set // useful metadata like html lang. For example, if your site is Chinese, you // may want to replace "en" with "zh-Hans". @@ -153,6 +155,28 @@ const config: Config = { copyright: `Copyright © ${new Date().getFullYear()} Commonwealth Scientific and Industrial Research - Organisation (CSIRO).` }, + // Refer to https://docusaurus.io/docs/search#connecting-algolia + algolia: { + // The application ID provided by Algolia + appId: 'SL7YXI16RH', + + // Public API key: it is safe to commit it + apiKey: 'a4c401a7bac65bc81b7dd7efe958b951', + + indexName: 'smartforms-csiro', + + // Optional: see doc section below + contextualSearch: true, + + // Optional: Algolia search parameters + searchParameters: {}, + + // Optional: path for search page that enabled by default (`false` to disable it) + searchPagePath: 'search', + + // Optional: whether the insights feature is enabled or not on Docsearch (`false` by default) + insights: false + }, prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula diff --git a/packages/smart-forms-renderer/src/utils/calculatedExpression.ts b/packages/smart-forms-renderer/src/utils/calculatedExpression.ts index bc2a5ceb..43a5aaf5 100644 --- a/packages/smart-forms-renderer/src/utils/calculatedExpression.ts +++ b/packages/smart-forms-renderer/src/utils/calculatedExpression.ts @@ -34,7 +34,7 @@ import { getQrItemsIndex, mapQItemsIndex } from './mapItem'; import { updateQrItemsInGroup } from './qrItem'; import cloneDeep from 'lodash.clonedeep'; import dayjs from 'dayjs'; -import { qrItemHasItemsOrAnswer } from './manageForm'; +import { updateQuestionnaireResponse } from './updateQr'; interface EvaluateInitialCalculatedExpressionsParams { initialResponse: QuestionnaireResponse; @@ -165,15 +165,6 @@ export function initialiseCalculatedExpressionValues( populatedResponse: QuestionnaireResponse, calculatedExpressions: Record ): QuestionnaireResponse { - if ( - !questionnaire.item || - questionnaire.item.length === 0 || - !populatedResponse.item || - populatedResponse.item.length === 0 - ) { - return populatedResponse; - } - // Filter calculated expressions, only preserve key-value pairs with values const calculatedExpressionsWithValues: Record = {}; for (const linkId in calculatedExpressions) { @@ -186,41 +177,12 @@ export function initialiseCalculatedExpressionValues( } } - // Populate calculated expression values into QR - const qItemsIndexMap = mapQItemsIndex(questionnaire); - const topLevelQRItemsByIndex = getQrItemsIndex( - questionnaire.item, - populatedResponse.item, - qItemsIndexMap + return updateQuestionnaireResponse( + questionnaire, + populatedResponse, + initialiseItemCalculatedExpressionValueRecursive, + calculatedExpressionsWithValues ); - - const topLevelQrItems: QuestionnaireResponseItem[] = []; - for (const [index, topLevelQItem] of questionnaire.item.entries()) { - const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? { - linkId: topLevelQItem.linkId, - text: topLevelQItem.text, - item: [] - }; - - const updatedTopLevelQRItem = initialiseItemCalculatedExpressionValueRecursive( - topLevelQItem, - topLevelQRItemOrItems, - calculatedExpressionsWithValues - ); - - if (Array.isArray(updatedTopLevelQRItem)) { - if (updatedTopLevelQRItem.length > 0) { - topLevelQrItems.push(...updatedTopLevelQRItem); - } - continue; - } - - if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) { - topLevelQrItems.push(updatedTopLevelQRItem); - } - } - - return { ...populatedResponse, item: topLevelQrItems }; } function initialiseItemCalculatedExpressionValueRecursive( diff --git a/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts b/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts index 936d7105..57210c38 100644 --- a/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts +++ b/packages/smart-forms-renderer/src/utils/repopulateIntoResponse.ts @@ -1,14 +1,9 @@ -import type { - Questionnaire, - QuestionnaireItem, - QuestionnaireResponse, - QuestionnaireResponseItem -} from 'fhir/r4'; +import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4'; import type { ItemToRepopulate } from './repopulateItems'; import { getQrItemsIndex, mapQItemsIndex } from './mapItem'; import { isSpecificItemControl } from './itemControl'; import { questionnaireResponseStore, questionnaireStore } from '../stores'; -import { qrItemHasItemsOrAnswer } from './manageForm'; +import { updateQuestionnaireResponse } from './updateQr'; /** * Re-populate checked items in the re-population dialog into the current QuestionnaireResponse @@ -19,63 +14,14 @@ export function repopulateResponse(checkedItemsToRepopulate: Record -): QuestionnaireResponse { - if ( - !questionnaire.item || - questionnaire.item.length === 0 || - !updatableResponse.item || - updatableResponse.item.length === 0 - ) { - return updatableResponse; - } - - const qItemsIndexMap = mapQItemsIndex(questionnaire); - const topLevelQRItemsByIndex = getQrItemsIndex( - questionnaire.item, - updatableResponse.item, - qItemsIndexMap - ); - - const topLevelQrItems: QuestionnaireResponseItem[] = []; - for (const [index, topLevelQItem] of questionnaire.item.entries()) { - const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? { - linkId: topLevelQItem.linkId, - text: topLevelQItem.text, - item: [] - }; - - const updatedTopLevelQRItem = repopulateItemRecursive( - topLevelQItem, - topLevelQRItemOrItems, - checkedItemsToRepopulate - ); - - if (Array.isArray(updatedTopLevelQRItem)) { - if (updatedTopLevelQRItem.length > 0) { - topLevelQrItems.push(...updatedTopLevelQRItem); - } - continue; - } - - if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) { - topLevelQrItems.push(updatedTopLevelQRItem); - } - } - - return { ...updatableResponse, item: topLevelQrItems }; -} - function repopulateItemRecursive( qItem: QuestionnaireItem, qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null, diff --git a/packages/smart-forms-renderer/src/utils/updateQr.ts b/packages/smart-forms-renderer/src/utils/updateQr.ts new file mode 100644 index 00000000..1c3b0c39 --- /dev/null +++ b/packages/smart-forms-renderer/src/utils/updateQr.ts @@ -0,0 +1,88 @@ +/* + * 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 type { + Questionnaire, + QuestionnaireItem, + QuestionnaireResponse, + QuestionnaireResponseItem +} from 'fhir/r4'; +import { getQrItemsIndex, mapQItemsIndex } from './mapItem'; +import { qrItemHasItemsOrAnswer } from './manageForm'; + +export type RepopulateFunction = ( + qItem: QuestionnaireItem, + qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null, + extraData: T +) => QuestionnaireResponseItem | QuestionnaireResponseItem[] | null; + +/** + * A generic (and safe) way to update a QuestionnaireResponse given a recursive function and a set of data i.e. Record, Record + * This function relies heavily on mapQItemsIndex() and getQrItemsIndex() to accurately pinpoint the locations of QR items based on their positions in the Q, taking into account repeating group answers, non-filled questions, etc + * + * @author Sean Fong + */ +export function updateQuestionnaireResponse( + questionnaire: Questionnaire, + questionnaireResponse: QuestionnaireResponse, + recursiveUpdateFunction: RepopulateFunction, + extraData: T +) { + if ( + !questionnaire.item || + questionnaire.item.length === 0 || + !questionnaireResponse.item || + questionnaireResponse.item.length === 0 + ) { + return questionnaireResponse; + } + + const qItemsIndexMap = mapQItemsIndex(questionnaire); + const topLevelQRItemsByIndex = getQrItemsIndex( + questionnaire.item, + questionnaireResponse.item, + qItemsIndexMap + ); + + const topLevelQrItems = []; + for (const [index, topLevelQItem] of questionnaire.item.entries()) { + const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? { + linkId: topLevelQItem.linkId, + text: topLevelQItem.text, + item: [] + }; + + const updatedTopLevelQRItem = recursiveUpdateFunction( + topLevelQItem, + topLevelQRItemOrItems, + extraData + ); + + if (Array.isArray(updatedTopLevelQRItem)) { + if (updatedTopLevelQRItem.length > 0) { + topLevelQrItems.push(...updatedTopLevelQRItem); + } + continue; + } + + if (updatedTopLevelQRItem && qrItemHasItemsOrAnswer(updatedTopLevelQRItem)) { + topLevelQrItems.push(updatedTopLevelQRItem); + } + } + + return { ...questionnaireResponse, item: topLevelQrItems }; +}