@aehrc/smart-forms-renderer 0.36.0 → 0.37.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/lib/components/FormComponents/Button.styles.d.ts +4 -0
- package/lib/components/FormComponents/Button.styles.js +10 -0
- package/lib/components/FormComponents/Button.styles.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/GroupHeading.d.ts +1 -0
- package/lib/components/FormComponents/GroupItem/GroupHeading.js +3 -2
- package/lib/components/FormComponents/GroupItem/GroupHeading.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/GroupItem.d.ts +4 -0
- package/lib/components/FormComponents/GroupItem/GroupItem.js +3 -3
- package/lib/components/FormComponents/GroupItem/GroupItem.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/GroupItemView.d.ts +4 -0
- package/lib/components/FormComponents/GroupItem/GroupItemView.js +8 -5
- package/lib/components/FormComponents/GroupItem/GroupItemView.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/NextPageButton.d.ts +7 -0
- package/lib/components/FormComponents/GroupItem/NextPageButton.js +26 -0
- package/lib/components/FormComponents/GroupItem/NextPageButton.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/PageButtonWrapper.d.ts +8 -0
- package/lib/components/FormComponents/GroupItem/PageButtonWrapper.js +46 -0
- package/lib/components/FormComponents/GroupItem/PageButtonWrapper.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/PreviousPageButton.d.ts +7 -0
- package/lib/components/FormComponents/GroupItem/PreviousPageButton.js +26 -0
- package/lib/components/FormComponents/GroupItem/PreviousPageButton.js.map +1 -0
- package/lib/components/Renderer/BaseRenderer.js +8 -0
- package/lib/components/Renderer/BaseRenderer.js.map +1 -1
- package/lib/components/Renderer/FormBodyPage.d.ts +9 -0
- package/lib/components/Renderer/FormBodyPage.js +43 -0
- package/lib/components/Renderer/FormBodyPage.js.map +1 -0
- package/lib/components/Renderer/FormTopLevelItem.js +7 -0
- package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
- package/lib/components/Renderer/FormTopLevelPage.d.ts +9 -0
- package/lib/components/Renderer/FormTopLevelPage.js +29 -0
- package/lib/components/Renderer/FormTopLevelPage.js.map +1 -0
- package/lib/hooks/useCodingCalculatedExpression.js +11 -0
- package/lib/hooks/useCodingCalculatedExpression.js.map +1 -1
- package/lib/hooks/useNextAndPreviousVisiblePages.d.ts +7 -0
- package/lib/hooks/useNextAndPreviousVisiblePages.js +47 -0
- package/lib/hooks/useNextAndPreviousVisiblePages.js.map +1 -0
- package/lib/interfaces/page.interface.d.ts +16 -0
- package/lib/interfaces/page.interface.js +2 -0
- package/lib/interfaces/page.interface.js.map +1 -0
- package/lib/interfaces/questionnaireStore.interface.d.ts +2 -0
- package/lib/stores/questionnaireStore.d.ts +13 -0
- package/lib/stores/questionnaireStore.js +19 -3
- package/lib/stores/questionnaireStore.js.map +1 -1
- package/lib/utils/calculatedExpression.js +1 -1
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/fhirpath.js +0 -4
- package/lib/utils/fhirpath.js.map +1 -1
- package/lib/utils/initialise.d.ts +3 -0
- package/lib/utils/initialise.js +6 -1
- package/lib/utils/initialise.js.map +1 -1
- package/lib/utils/page.d.ts +43 -0
- package/lib/utils/page.js +101 -0
- package/lib/utils/page.js.map +1 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js +4 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js.map +1 -1
- package/lib/utils/questionnaireStoreUtils/extractPages.d.ts +3 -0
- package/lib/utils/questionnaireStoreUtils/extractPages.js +18 -0
- package/lib/utils/questionnaireStoreUtils/extractPages.js.map +1 -0
- package/package.json +4 -4
- package/src/components/FormComponents/Button.styles.ts +10 -0
- package/src/components/FormComponents/GroupItem/GroupHeading.tsx +5 -3
- package/src/components/FormComponents/GroupItem/GroupItem.tsx +11 -1
- package/src/components/FormComponents/GroupItem/GroupItemView.tsx +12 -0
- package/src/components/FormComponents/GroupItem/NextPageButton.tsx +37 -0
- package/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx +78 -0
- package/src/components/FormComponents/GroupItem/PreviousPageButton.tsx +41 -0
- package/src/components/Renderer/BaseRenderer.tsx +21 -0
- package/src/components/Renderer/FormBodyPage.tsx +93 -0
- package/src/components/Renderer/FormTopLevelItem.tsx +17 -0
- package/src/components/Renderer/FormTopLevelPage.tsx +70 -0
- package/src/hooks/useCodingCalculatedExpression.ts +14 -1
- package/src/hooks/useNextAndPreviousVisiblePages.ts +69 -0
- package/src/interfaces/page.interface.ts +13 -0
- package/src/interfaces/questionnaireStore.interface.ts +2 -0
- package/src/stores/questionnaireStore.ts +33 -2
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreAllergyIntolerance.json +209 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreCondition.json +220 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreEncounter.json +299 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreImmunization.json +175 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreMedicationRequest.json +229 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBP.json +359 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBodyHeight.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBodyWeight.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationHeartRate.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationSmokingStatus.json +174 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePatient.json +495 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePractitioner.json +139 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePractitionerRole.json +216 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreProcedure.json +199 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreRespirationRate.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreWaistCircumference.json +195 -0
- package/src/stories/assets/questionnaires/QAuCoreTesting.ts +3796 -0
- package/src/stories/assets/questionnaires/QItemControlGroup.ts +673 -0
- package/src/stories/assets/questionnaires/QPrePopTester.ts +266 -71
- package/src/stories/sdc/ItemControlGroup.stories.tsx +22 -1
- package/src/stories/testing/AuCoreTester.stories.tsx +196 -0
- package/src/utils/calculatedExpression.ts +1 -1
- package/src/utils/fhirpath.ts +0 -5
- package/src/utils/initialise.ts +11 -0
- package/src/utils/page.ts +134 -0
- package/src/utils/questionnaireStoreUtils/createQuestionaireModel.ts +5 -0
- package/src/utils/questionnaireStoreUtils/extractPages.ts +24 -0
- package/stats.html +0 -4842
- package/stats1.html +0 -4842
- package/stats3.html +0 -4842
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
import React from 'react';
|
|
19
19
|
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
20
20
|
import FormBodyTabbed from './FormBodyTabbed';
|
|
21
|
+
import FormBodyPage from './FormBodyPage';
|
|
21
22
|
import { containsTabs, isTabContainer } from '../../utils/tabs';
|
|
23
|
+
import { containsPages, isPage } from '../../utils/page';
|
|
22
24
|
import GroupItem from '../FormComponents/GroupItem/GroupItem';
|
|
23
25
|
import SingleItem from '../FormComponents/SingleItem/SingleItem';
|
|
24
26
|
import type {
|
|
@@ -53,6 +55,9 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
|
|
|
53
55
|
const itemIsTabContainer = isTabContainer(topLevelQItem);
|
|
54
56
|
const itemContainsTabs = containsTabs(topLevelQItem);
|
|
55
57
|
|
|
58
|
+
const itemIsPageContainer = isPage(topLevelQItem);
|
|
59
|
+
const itemContainsPages = containsPages(topLevelQItem);
|
|
60
|
+
|
|
56
61
|
const isTablet = useResponsive('up', 'md');
|
|
57
62
|
|
|
58
63
|
const itemIsGroup = topLevelQItem.type === 'group';
|
|
@@ -106,6 +111,18 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
|
|
|
106
111
|
);
|
|
107
112
|
}
|
|
108
113
|
|
|
114
|
+
if (itemContainsPages || itemIsPageContainer) {
|
|
115
|
+
return (
|
|
116
|
+
<FormBodyPage
|
|
117
|
+
key={topLevelQItem.linkId}
|
|
118
|
+
topLevelQItem={topLevelQItem}
|
|
119
|
+
topLevelQRItem={topLevelQRItem}
|
|
120
|
+
parentIsReadOnly={readOnly}
|
|
121
|
+
onQrItemChange={onQrItemChange}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
109
126
|
// If form is untabbed, it is rendered as a regular group
|
|
110
127
|
if (itemIsGroup) {
|
|
111
128
|
return (
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Grid from '@mui/material/Grid';
|
|
3
|
+
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
4
|
+
import TabContext from '@mui/lab/TabContext';
|
|
5
|
+
import TabPanel from '@mui/lab/TabPanel';
|
|
6
|
+
import GroupItem from '../FormComponents/GroupItem/GroupItem';
|
|
7
|
+
import type {
|
|
8
|
+
PropsWithParentIsReadOnlyAttribute,
|
|
9
|
+
PropsWithQrItemChangeHandler
|
|
10
|
+
} from '../../interfaces/renderProps.interface';
|
|
11
|
+
import { useQuestionnaireStore } from '../../stores';
|
|
12
|
+
|
|
13
|
+
interface FormTopLevelPageProps
|
|
14
|
+
extends PropsWithQrItemChangeHandler,
|
|
15
|
+
PropsWithParentIsReadOnlyAttribute {
|
|
16
|
+
topLevelQItems: QuestionnaireItem[];
|
|
17
|
+
topLevelQRItems: (QuestionnaireResponseItem | QuestionnaireResponseItem[] | undefined)[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function FormTopLevelPage(props: FormTopLevelPageProps) {
|
|
21
|
+
const { topLevelQItems, topLevelQRItems, parentIsReadOnly, onQrItemChange } = props;
|
|
22
|
+
|
|
23
|
+
const pages = useQuestionnaireStore.use.pages();
|
|
24
|
+
const currentPage = useQuestionnaireStore.use.currentPageIndex();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Grid container spacing={1.5}>
|
|
28
|
+
<TabContext value={currentPage.toString()}>
|
|
29
|
+
<Grid item xs={12} md={12} lg={12}>
|
|
30
|
+
{topLevelQItems.map((qItem, i) => {
|
|
31
|
+
const qrItem = topLevelQRItems[i];
|
|
32
|
+
|
|
33
|
+
const isNotRepeatGroup = !Array.isArray(qrItem);
|
|
34
|
+
const isPage = !!pages[qItem.linkId];
|
|
35
|
+
|
|
36
|
+
if (!isPage || !isNotRepeatGroup) {
|
|
37
|
+
// Something has gone horribly wrong
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const isRepeated = qItem.repeats ?? false;
|
|
42
|
+
const pageIsMarkedAsComplete = pages[qItem.linkId].isComplete ?? false;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<TabPanel
|
|
46
|
+
key={qItem.linkId}
|
|
47
|
+
sx={{ p: 0 }}
|
|
48
|
+
value={i.toString()}
|
|
49
|
+
data-test="renderer-page-panel">
|
|
50
|
+
<GroupItem
|
|
51
|
+
qItem={qItem}
|
|
52
|
+
qrItem={qrItem ?? null}
|
|
53
|
+
isRepeated={isRepeated}
|
|
54
|
+
groupCardElevation={1}
|
|
55
|
+
pageIsMarkedAsComplete={pageIsMarkedAsComplete}
|
|
56
|
+
pages={pages}
|
|
57
|
+
currentPageIndex={currentPage}
|
|
58
|
+
parentIsReadOnly={parentIsReadOnly}
|
|
59
|
+
onQrItemChange={onQrItemChange}
|
|
60
|
+
/>
|
|
61
|
+
</TabPanel>
|
|
62
|
+
);
|
|
63
|
+
})}
|
|
64
|
+
</Grid>
|
|
65
|
+
</TabContext>
|
|
66
|
+
</Grid>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default FormTopLevelPage;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { useEffect, useState } from 'react';
|
|
19
|
-
import type { QuestionnaireItem } from 'fhir/r4';
|
|
19
|
+
import type { Coding, QuestionnaireItem } from 'fhir/r4';
|
|
20
20
|
import { useQuestionnaireStore } from '../stores';
|
|
21
21
|
|
|
22
22
|
interface UseCodingCalculatedExpression {
|
|
@@ -56,6 +56,7 @@ function useCodingCalculatedExpression(
|
|
|
56
56
|
calcExpression.value !== valueInString &&
|
|
57
57
|
(typeof calcExpression.value === 'string' ||
|
|
58
58
|
typeof calcExpression.value === 'number' ||
|
|
59
|
+
typeof calcExpression.value === 'object' ||
|
|
59
60
|
calcExpression.value === null)
|
|
60
61
|
) {
|
|
61
62
|
// update ui to show calculated value changes
|
|
@@ -70,6 +71,14 @@ function useCodingCalculatedExpression(
|
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
// calculatedExpression value is object, check if it is a Coding object
|
|
75
|
+
if (typeof calcExpression.value === 'object' && objectIsCoding(calcExpression.value)) {
|
|
76
|
+
if (calcExpression.value.code) {
|
|
77
|
+
onChangeByCalcExpressionString(calcExpression.value.code);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
// calculatedExpression value is a string or number
|
|
74
83
|
const newValueString =
|
|
75
84
|
typeof calcExpression.value === 'string'
|
|
@@ -87,4 +96,8 @@ function useCodingCalculatedExpression(
|
|
|
87
96
|
return { calcExpUpdated: calcExpUpdated };
|
|
88
97
|
}
|
|
89
98
|
|
|
99
|
+
function objectIsCoding(obj: any): obj is Coding {
|
|
100
|
+
return obj && obj.code && typeof obj.code === 'string';
|
|
101
|
+
}
|
|
102
|
+
|
|
90
103
|
export default useCodingCalculatedExpression;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useQuestionnaireStore } from '../stores';
|
|
2
|
+
import type { Pages } from '../interfaces/page.interface';
|
|
3
|
+
import { constructPagesWithVisibility } from '../utils/page';
|
|
4
|
+
|
|
5
|
+
function useNextAndPreviousVisiblePages(
|
|
6
|
+
currentPageIndex?: number,
|
|
7
|
+
pages?: Pages
|
|
8
|
+
): { previousPageIndex: number | null; nextPageIndex: number | null; numOfVisiblePages: number } {
|
|
9
|
+
const enableWhenIsActivated = useQuestionnaireStore.use.enableWhenIsActivated();
|
|
10
|
+
const enableWhenItems = useQuestionnaireStore.use.enableWhenItems();
|
|
11
|
+
const enableWhenExpressions = useQuestionnaireStore.use.enableWhenExpressions();
|
|
12
|
+
|
|
13
|
+
const pagesNotDefined = currentPageIndex === undefined || pages === undefined;
|
|
14
|
+
|
|
15
|
+
if (pagesNotDefined) {
|
|
16
|
+
return { previousPageIndex: null, nextPageIndex: null, numOfVisiblePages: 0 };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const pagesWithVisibility = constructPagesWithVisibility({
|
|
20
|
+
pages,
|
|
21
|
+
enableWhenIsActivated,
|
|
22
|
+
enableWhenItems,
|
|
23
|
+
enableWhenExpressions
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
previousPageIndex: getPreviousPageIndex(currentPageIndex, pagesWithVisibility),
|
|
28
|
+
nextPageIndex: getNextPageIndex(currentPageIndex, pagesWithVisibility),
|
|
29
|
+
numOfVisiblePages: pagesWithVisibility.filter((tab) => tab.isVisible).length
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getPreviousPageIndex(
|
|
34
|
+
currentPageIndex: number,
|
|
35
|
+
pagesWithVisibility: { linkId: string; isVisible: boolean }[]
|
|
36
|
+
): number | null {
|
|
37
|
+
const previousPages = pagesWithVisibility.slice(0, currentPageIndex);
|
|
38
|
+
const foundIndex = previousPages.reverse().findIndex((tab) => tab.isVisible);
|
|
39
|
+
|
|
40
|
+
// Previous visible tab not found
|
|
41
|
+
if (foundIndex === -1) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Previous visible tab less than 0
|
|
46
|
+
const previousPageIndex = currentPageIndex - foundIndex - 1;
|
|
47
|
+
if (previousPageIndex < 0) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return previousPageIndex;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getNextPageIndex(
|
|
55
|
+
currentPageIndex: number,
|
|
56
|
+
pagesWithVisibility: { linkId: string; isVisible: boolean }[]
|
|
57
|
+
): number | null {
|
|
58
|
+
const subsequentPages = pagesWithVisibility.slice(currentPageIndex + 1);
|
|
59
|
+
const foundIndex = subsequentPages.findIndex((tab) => tab.isVisible);
|
|
60
|
+
|
|
61
|
+
// Next visible tab not found, something is wrong
|
|
62
|
+
if (foundIndex === -1) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return currentPageIndex + foundIndex + 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default useNextAndPreviousVisiblePages;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page interface
|
|
3
|
+
*
|
|
4
|
+
* @property pageIndex - The index of the page
|
|
5
|
+
* @property isComplete - Whether the page is marked as complete
|
|
6
|
+
* @property isHidden - Whether the page is hidden
|
|
7
|
+
*/
|
|
8
|
+
export type Page = { pageIndex: number; isComplete: boolean; isHidden: boolean };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Key-value pair of pages `Record<linkId, Page>`
|
|
12
|
+
*/
|
|
13
|
+
export type Pages = Record<string, Page>;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { Tabs } from './tab.interface';
|
|
19
|
+
import type { Pages } from './page.interface';
|
|
19
20
|
import type { Variables } from './variables.interface';
|
|
20
21
|
import type { LaunchContext } from './populate.interface';
|
|
21
22
|
import type { EnableWhenExpressions, EnableWhenItems } from './enableWhen.interface';
|
|
@@ -27,6 +28,7 @@ import type { InitialExpression } from './initialExpression.interface';
|
|
|
27
28
|
export interface QuestionnaireModel {
|
|
28
29
|
itemTypes: Record<string, string>;
|
|
29
30
|
tabs: Tabs;
|
|
31
|
+
pages: Pages;
|
|
30
32
|
variables: Variables;
|
|
31
33
|
launchContexts: Record<string, LaunchContext>;
|
|
32
34
|
enableWhenItems: EnableWhenItems;
|
|
@@ -28,6 +28,7 @@ import type { CalculatedExpression } from '../interfaces/calculatedExpression.in
|
|
|
28
28
|
import type { EnableWhenExpressions, EnableWhenItems } from '../interfaces/enableWhen.interface';
|
|
29
29
|
import type { AnswerExpression } from '../interfaces/answerExpression.interface';
|
|
30
30
|
import type { Tabs } from '../interfaces/tab.interface';
|
|
31
|
+
import type { Pages } from '../interfaces/page.interface';
|
|
31
32
|
import {
|
|
32
33
|
mutateRepeatEnableWhenItemInstances,
|
|
33
34
|
updateEnableWhenItemAnswer
|
|
@@ -58,6 +59,8 @@ import type { InitialExpression } from '../interfaces/initialExpression.interfac
|
|
|
58
59
|
* @property itemTypes - Key-value pair of item types `Record<linkId, item.type>`
|
|
59
60
|
* @property tabs - Key-value pair of tabs `Record<linkId, Tab>`
|
|
60
61
|
* @property currentTabIndex - Index of the current tab
|
|
62
|
+
* @property pages - Key-value pair of pages `Record<linkId, Page>`
|
|
63
|
+
* @property currentPageIndex - Index of the current page
|
|
61
64
|
* @property variables - Questionnaire variables object containing FHIRPath and x-fhir-query variables
|
|
62
65
|
* @property launchContexts - Key-value pair of launch contexts `Record<launch context name, launch context properties>`
|
|
63
66
|
* @property enableWhenItems - EnableWhenItems object containing enableWhen items and their linked questions
|
|
@@ -76,7 +79,9 @@ import type { InitialExpression } from '../interfaces/initialExpression.interfac
|
|
|
76
79
|
* @property buildSourceQuestionnaire - Used to build the source questionnaire with the provided questionnaire and optionally questionnaire response, additional variables, terminology server url and readyOnly flag
|
|
77
80
|
* @property destroySourceQuestionnaire - Used to destroy the source questionnaire and reset all properties
|
|
78
81
|
* @property switchTab - Used to switch the current tab index
|
|
82
|
+
* @property switchPage - Used to switch the current page index
|
|
79
83
|
* @property markTabAsComplete - Used to mark a tab index as complete
|
|
84
|
+
* @property markPageAsComplete - Used to mark a page index as complete
|
|
80
85
|
* @property updateEnableWhenItem - Used to update linked enableWhen items by updating a question with a new answer
|
|
81
86
|
* @property mutateRepeatEnableWhenItems - Used to add or remove instances of repeating enableWhen items
|
|
82
87
|
* @property toggleEnableWhenActivation - Used to toggle enableWhen checks on/off
|
|
@@ -94,6 +99,8 @@ export interface QuestionnaireStoreType {
|
|
|
94
99
|
itemTypes: Record<string, string>;
|
|
95
100
|
tabs: Tabs;
|
|
96
101
|
currentTabIndex: number;
|
|
102
|
+
pages: Pages;
|
|
103
|
+
currentPageIndex: number;
|
|
97
104
|
variables: Variables;
|
|
98
105
|
launchContexts: Record<string, LaunchContext>;
|
|
99
106
|
enableWhenItems: EnableWhenItems;
|
|
@@ -119,7 +126,9 @@ export interface QuestionnaireStoreType {
|
|
|
119
126
|
) => Promise<void>;
|
|
120
127
|
destroySourceQuestionnaire: () => void;
|
|
121
128
|
switchTab: (newTabIndex: number) => void;
|
|
129
|
+
switchPage: (newPageIndex: number) => void;
|
|
122
130
|
markTabAsComplete: (tabLinkId: string) => void;
|
|
131
|
+
markPageAsComplete: (pageLinkId: string) => void;
|
|
123
132
|
updateEnableWhenItem: (
|
|
124
133
|
linkId: string,
|
|
125
134
|
newAnswer: QuestionnaireResponseItemAnswer[] | undefined,
|
|
@@ -155,6 +164,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
155
164
|
itemTypes: {},
|
|
156
165
|
tabs: {},
|
|
157
166
|
currentTabIndex: 0,
|
|
167
|
+
pages: {},
|
|
168
|
+
currentPageIndex: 0,
|
|
158
169
|
variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
|
|
159
170
|
launchContexts: {},
|
|
160
171
|
calculatedExpressions: {},
|
|
@@ -197,6 +208,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
197
208
|
initialEnableWhenExpressions,
|
|
198
209
|
initialCalculatedExpressions,
|
|
199
210
|
firstVisibleTab,
|
|
211
|
+
firstVisiblePage,
|
|
200
212
|
updatedFhirPathContext
|
|
201
213
|
} = initialiseFormFromResponse({
|
|
202
214
|
questionnaireResponse,
|
|
@@ -205,6 +217,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
205
217
|
calculatedExpressions: questionnaireModel.calculatedExpressions,
|
|
206
218
|
variablesFhirPath: questionnaireModel.variables.fhirPathVariables,
|
|
207
219
|
tabs: questionnaireModel.tabs,
|
|
220
|
+
pages: questionnaireModel.pages,
|
|
208
221
|
fhirPathContext: questionnaireModel.fhirPathContext
|
|
209
222
|
});
|
|
210
223
|
|
|
@@ -213,6 +226,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
213
226
|
itemTypes: questionnaireModel.itemTypes,
|
|
214
227
|
tabs: questionnaireModel.tabs,
|
|
215
228
|
currentTabIndex: firstVisibleTab,
|
|
229
|
+
pages: questionnaireModel.pages,
|
|
230
|
+
currentPageIndex: firstVisiblePage,
|
|
216
231
|
variables: questionnaireModel.variables,
|
|
217
232
|
launchContexts: questionnaireModel.launchContexts,
|
|
218
233
|
enableWhenItems: initialEnableWhenItems,
|
|
@@ -233,6 +248,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
233
248
|
itemTypes: {},
|
|
234
249
|
tabs: {},
|
|
235
250
|
currentTabIndex: 0,
|
|
251
|
+
pages: {},
|
|
252
|
+
currentPageIndex: 0,
|
|
236
253
|
variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
|
|
237
254
|
launchContexts: {},
|
|
238
255
|
enableWhenItems: { singleItems: {}, repeatItems: {} },
|
|
@@ -246,6 +263,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
246
263
|
fhirPathContext: {}
|
|
247
264
|
}),
|
|
248
265
|
switchTab: (newTabIndex: number) => set(() => ({ currentTabIndex: newTabIndex })),
|
|
266
|
+
switchPage: (newPageIndex: number) => set(() => ({ currentPageIndex: newPageIndex })),
|
|
249
267
|
markTabAsComplete: (tabLinkId: string) => {
|
|
250
268
|
const tabs = get().tabs;
|
|
251
269
|
set(() => ({
|
|
@@ -255,6 +273,15 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
255
273
|
}
|
|
256
274
|
}));
|
|
257
275
|
},
|
|
276
|
+
markPageAsComplete: (pageLinkId: string) => {
|
|
277
|
+
const pages = get().pages;
|
|
278
|
+
set(() => ({
|
|
279
|
+
pages: {
|
|
280
|
+
...pages,
|
|
281
|
+
[pageLinkId]: { ...pages[pageLinkId], isComplete: !pages[pageLinkId].isComplete }
|
|
282
|
+
}
|
|
283
|
+
}));
|
|
284
|
+
},
|
|
258
285
|
updateEnableWhenItem: (
|
|
259
286
|
linkId: string,
|
|
260
287
|
newAnswer: QuestionnaireResponseItemAnswer[] | undefined,
|
|
@@ -355,7 +382,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
355
382
|
updatePopulatedProperties: (
|
|
356
383
|
populatedResponse: QuestionnaireResponse,
|
|
357
384
|
populatedContext?: Record<string, any>,
|
|
358
|
-
persistTabIndex?: boolean
|
|
385
|
+
persistTabIndex?: boolean,
|
|
386
|
+
persistPageIndex?: boolean
|
|
359
387
|
) => {
|
|
360
388
|
const initialResponseItemMap = createQuestionnaireResponseItemMap(populatedResponse);
|
|
361
389
|
|
|
@@ -379,7 +407,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
379
407
|
initialEnableWhenItems,
|
|
380
408
|
initialEnableWhenLinkedQuestions,
|
|
381
409
|
initialEnableWhenExpressions,
|
|
382
|
-
firstVisibleTab
|
|
410
|
+
firstVisibleTab,
|
|
411
|
+
firstVisiblePage
|
|
383
412
|
} = initialiseFormFromResponse({
|
|
384
413
|
questionnaireResponse: updatedResponse,
|
|
385
414
|
enableWhenItems: get().enableWhenItems,
|
|
@@ -387,6 +416,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
387
416
|
calculatedExpressions: initialCalculatedExpressions,
|
|
388
417
|
variablesFhirPath: get().variables.fhirPathVariables,
|
|
389
418
|
tabs: get().tabs,
|
|
419
|
+
pages: get().pages,
|
|
390
420
|
fhirPathContext: updatedFhirPathContext
|
|
391
421
|
});
|
|
392
422
|
updatedFhirPathContext = evaluateInitialCalculatedExpressionsResult.updatedFhirPathContext;
|
|
@@ -397,6 +427,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
397
427
|
enableWhenExpressions: initialEnableWhenExpressions,
|
|
398
428
|
calculatedExpressions: initialCalculatedExpressions,
|
|
399
429
|
currentTabIndex: persistTabIndex ? get().currentTabIndex : firstVisibleTab,
|
|
430
|
+
currentPageIndex: persistPageIndex ? get().currentPageIndex : firstVisiblePage,
|
|
400
431
|
fhirPathContext: updatedFhirPathContext,
|
|
401
432
|
populatedContext: populatedContext ?? get().populatedContext
|
|
402
433
|
}));
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
{
|
|
2
|
+
"resourceType": "Questionnaire",
|
|
3
|
+
"id": "AuCoreAllergyIntolerance",
|
|
4
|
+
"extension": [
|
|
5
|
+
{
|
|
6
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext",
|
|
7
|
+
"extension": [
|
|
8
|
+
{
|
|
9
|
+
"url": "name",
|
|
10
|
+
"valueCoding": {
|
|
11
|
+
"system": "http://hl7.org/fhir/uv/sdc/CodeSystem/launchContext",
|
|
12
|
+
"code": "patient"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"url": "type",
|
|
17
|
+
"valueCode": "Patient"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"url": "description",
|
|
21
|
+
"valueString": "The patient that is to be used to pre-populate the form"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"url": "http://hl7.org/fhir/StructureDefinition/variable",
|
|
27
|
+
"valueExpression": {
|
|
28
|
+
"name": "AllergyIntolerance",
|
|
29
|
+
"language": "application/x-fhir-query",
|
|
30
|
+
"expression": "AllergyIntolerance?patient={{%patient.id}}"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"version": "0.5.0-ci-build",
|
|
35
|
+
"name": "AU Core AllergyIntolerance",
|
|
36
|
+
"title": "AU Core AllergyIntolerance",
|
|
37
|
+
"status": "draft",
|
|
38
|
+
"date": "2024-07-15",
|
|
39
|
+
"item": [
|
|
40
|
+
{
|
|
41
|
+
"linkId": "allergy-intolerance",
|
|
42
|
+
"text": "AU Core AllergyIntolerance",
|
|
43
|
+
"type": "group",
|
|
44
|
+
"repeats": false,
|
|
45
|
+
"item": [
|
|
46
|
+
{
|
|
47
|
+
"linkId": "patient-details",
|
|
48
|
+
"text": "Patient Details",
|
|
49
|
+
"type": "group",
|
|
50
|
+
"item": [
|
|
51
|
+
{
|
|
52
|
+
"extension": [
|
|
53
|
+
{
|
|
54
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
55
|
+
"valueExpression": {
|
|
56
|
+
"language": "text/fhirpath",
|
|
57
|
+
"expression": "%patient.id"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"linkId": "patient-id",
|
|
62
|
+
"text": "Patient ID",
|
|
63
|
+
"type": "string",
|
|
64
|
+
"required": true,
|
|
65
|
+
"readOnly": true
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"extension": [
|
|
69
|
+
{
|
|
70
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
71
|
+
"valueExpression": {
|
|
72
|
+
"language": "text/fhirpath",
|
|
73
|
+
"expression": "(%patient.name.where(use='official').select((family | (given | prefix).join(' ')).join(', ').where($this != '') | text)).first()"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"linkId": "patient-name",
|
|
78
|
+
"text": "Patient Name",
|
|
79
|
+
"type": "string",
|
|
80
|
+
"required": true,
|
|
81
|
+
"readOnly": true
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"linkId": "first-resource-note",
|
|
87
|
+
"text": "This questionnaire only uses the first AllergyTolerance resource of a reference patient.",
|
|
88
|
+
"_text": {
|
|
89
|
+
"extension": [
|
|
90
|
+
{
|
|
91
|
+
"url": "http://hl7.org/fhir/StructureDefinition/rendering-xhtml",
|
|
92
|
+
"valueString": "<div xmlns=\"http://www.w3.org/1999/xhtml\" style=\"padding-bottom: 8px;\">\r\n <b>This questionnaire only uses the first AllergyIntolerance resource of a reference patient.</b>\r\n</div>"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
"type": "display"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"extension": [
|
|
100
|
+
{
|
|
101
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
102
|
+
"valueExpression": {
|
|
103
|
+
"language": "text/fhirpath",
|
|
104
|
+
"expression": "%AllergyIntolerance.entry[0].resource.clinicalStatus.coding"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
"linkId": "clinical-status",
|
|
109
|
+
"text": "Clinical Status",
|
|
110
|
+
"type": "choice",
|
|
111
|
+
"answerValueSet": "http://hl7.org/fhir/ValueSet/allergyintolerance-clinical"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"extension": [
|
|
115
|
+
{
|
|
116
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
117
|
+
"valueExpression": {
|
|
118
|
+
"language": "text/fhirpath",
|
|
119
|
+
"expression": "%AllergyIntolerance.entry[0].resource.verificationStatus.coding"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"linkId": "verification-status",
|
|
124
|
+
"text": "Verification Status",
|
|
125
|
+
"type": "choice",
|
|
126
|
+
"answerValueSet": "http://hl7.org/fhir/ValueSet/allergyintolerance-verification"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"extension": [
|
|
130
|
+
{
|
|
131
|
+
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
|
|
132
|
+
"valueCodeableConcept": {
|
|
133
|
+
"coding": [
|
|
134
|
+
{
|
|
135
|
+
"system": "http://hl7.org/fhir/questionnaire-item-control",
|
|
136
|
+
"code": "autocomplete"
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
143
|
+
"valueExpression": {
|
|
144
|
+
"language": "text/fhirpath",
|
|
145
|
+
"expression": "%AllergyIntolerance.entry[0].resource.code.coding"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"linkId": "code",
|
|
150
|
+
"text": "Code",
|
|
151
|
+
"type": "open-choice",
|
|
152
|
+
"required": true,
|
|
153
|
+
"answerValueSet": "https://healthterminologies.gov.au/fhir/ValueSet/indicator-hypersensitivity-intolerance-to-substance-2"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"extension": [
|
|
157
|
+
{
|
|
158
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
159
|
+
"valueExpression": {
|
|
160
|
+
"language": "text/fhirpath",
|
|
161
|
+
"expression": "%AllergyIntolerance.entry[0].resource.onset"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
"linkId": "onset",
|
|
166
|
+
"text": "Onset[x]",
|
|
167
|
+
"type": "string"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"linkId": "reaction",
|
|
171
|
+
"text": "Reaction",
|
|
172
|
+
"type": "group",
|
|
173
|
+
"item": [
|
|
174
|
+
{
|
|
175
|
+
"extension": [
|
|
176
|
+
{
|
|
177
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
178
|
+
"valueExpression": {
|
|
179
|
+
"language": "text/fhirpath",
|
|
180
|
+
"expression": "%AllergyIntolerance.entry[0].resource.reaction.manifestation[0].coding"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
"linkId": "reaction-manifestation",
|
|
185
|
+
"text": "Reaction Manifestation",
|
|
186
|
+
"type": "choice",
|
|
187
|
+
"answerValueSet": "https://healthterminologies.gov.au/fhir/ValueSet/clinical-finding-1"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"extension": [
|
|
191
|
+
{
|
|
192
|
+
"url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
|
|
193
|
+
"valueExpression": {
|
|
194
|
+
"language": "text/fhirpath",
|
|
195
|
+
"expression": "%AllergyIntolerance.entry[0].resource.reaction.severity"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
"linkId": "reaction-severity",
|
|
200
|
+
"text": "Reaction Severity",
|
|
201
|
+
"type": "choice",
|
|
202
|
+
"answerValueSet": "http://hl7.org/fhir/ValueSet/reaction-event-severity"
|
|
203
|
+
}
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|