@aehrc/smart-forms-renderer 0.7.0 → 0.7.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/README.md +5 -5
- package/lib/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.js +1 -1
- package/lib/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.js.map +1 -1
- package/lib/components/FormComponents/DecimalItem/DecimalItem.js +3 -2
- package/lib/components/FormComponents/DecimalItem/DecimalItem.js.map +1 -1
- package/lib/components/FormComponents/GridGroup/GridGroup.js +2 -2
- package/lib/components/FormComponents/GridGroup/GridGroup.js.map +1 -1
- package/lib/components/FormComponents/GridGroup/GridRow.js +2 -2
- package/lib/components/FormComponents/GridGroup/GridRow.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/GroupChildItemSwitcher.d.ts +10 -0
- package/lib/components/FormComponents/GroupItem/GroupChildItemSwitcher.js +73 -0
- package/lib/components/FormComponents/GroupItem/GroupChildItemSwitcher.js.map +1 -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/ItemSwitcher.d.ts +10 -0
- package/lib/components/FormComponents/GroupItem/ItemSwitcher.js +81 -0
- package/lib/components/FormComponents/GroupItem/ItemSwitcher.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/TopLevelGroupItemSwitcher.d.ts +10 -0
- package/lib/components/FormComponents/GroupItem/TopLevelGroupItemSwitcher.js +45 -0
- package/lib/components/FormComponents/GroupItem/TopLevelGroupItemSwitcher.js.map +1 -0
- package/lib/components/FormComponents/IntegerItem/IntegerItem.js +3 -2
- package/lib/components/FormComponents/IntegerItem/IntegerItem.js.map +1 -1
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.js +2 -2
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js +4 -3
- package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/index.d.ts +1 -0
- package/lib/{theme/overrides/Typography.js → components/FormComponents/RepeatGroup/index.js} +2 -15
- package/lib/components/FormComponents/RepeatGroup/index.js.map +1 -0
- package/lib/components/FormComponents/RepeatItem/RepeatItem.js +1 -1
- package/lib/components/FormComponents/RepeatItem/RepeatItem.js.map +1 -1
- package/lib/components/FormComponents/SingleItem/index.d.ts +1 -0
- package/lib/components/FormComponents/SingleItem/index.js +18 -0
- package/lib/components/FormComponents/SingleItem/index.js.map +1 -0
- package/lib/components/FormComponents/StringItem/StringItem.js +3 -2
- package/lib/components/FormComponents/StringItem/StringItem.js.map +1 -1
- package/lib/components/FormComponents/Tables/QItemGroupTable.js +7 -5
- package/lib/components/FormComponents/Tables/QItemGroupTable.js.map +1 -1
- package/lib/components/FormComponents/Tables/QItemGroupTableRow.js +2 -2
- package/lib/components/FormComponents/Tables/QItemGroupTableRow.js.map +1 -1
- package/lib/components/FormComponents/Tables/index.d.ts +1 -0
- package/lib/components/FormComponents/Tables/index.js +18 -0
- package/lib/components/FormComponents/Tables/index.js.map +1 -0
- package/lib/components/FormComponents/TextItem/TextItem.js +3 -2
- package/lib/components/FormComponents/TextItem/TextItem.js.map +1 -1
- package/lib/components/FormComponents/index.d.ts +3 -0
- package/lib/components/FormComponents/index.js +20 -0
- package/lib/components/FormComponents/index.js.map +1 -0
- package/lib/components/Renderer/BaseRenderer.js +26 -14
- package/lib/components/Renderer/BaseRenderer.js.map +1 -1
- package/lib/components/Renderer/FormBodyCollapsible.js +2 -2
- package/lib/components/Renderer/FormBodyCollapsible.js.map +1 -1
- package/lib/components/Renderer/FormBodyTabbed.js +2 -2
- package/lib/components/Renderer/FormBodyTabbed.js.map +1 -1
- package/lib/components/Renderer/FormTopLevelItem.d.ts +4 -4
- package/lib/components/Renderer/FormTopLevelItem.js +14 -1
- package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/index.js.map +1 -1
- package/lib/hooks/useDecimalUpdateFromProp.d.ts +2 -0
- package/lib/{utils/isHidden.js → hooks/useDecimalUpdateFromProp.js} +12 -13
- package/lib/hooks/useDecimalUpdateFromProp.js.map +1 -0
- package/lib/hooks/useInitialiseRepeatAnswers.d.ts +2 -2
- package/lib/hooks/useInitialiseRepeatAnswers.js +21 -15
- package/lib/hooks/useInitialiseRepeatAnswers.js.map +1 -1
- package/lib/hooks/useInitialiseRepeatGroups.d.ts +2 -1
- package/lib/hooks/useInitialiseRepeatGroups.js +21 -15
- package/lib/hooks/useInitialiseRepeatGroups.js.map +1 -1
- package/lib/hooks/useIntegerUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useIntegerUpdateFromProp.js +29 -0
- package/lib/hooks/useIntegerUpdateFromProp.js.map +1 -0
- package/lib/hooks/useNumberInput.d.ts +3 -0
- package/lib/hooks/useNumberInput.js +31 -0
- package/lib/hooks/useNumberInput.js.map +1 -0
- package/lib/hooks/useNumberUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useNumberUpdateFromProp.js +29 -0
- package/lib/hooks/useNumberUpdateFromProp.js.map +1 -0
- package/lib/hooks/useRepeatItemState.d.ts +5 -0
- package/lib/hooks/useRepeatItemState.js +35 -0
- package/lib/hooks/useRepeatItemState.js.map +1 -0
- package/lib/hooks/useRepeatItemUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useRepeatItemUpdateFromProp.js +29 -0
- package/lib/hooks/useRepeatItemUpdateFromProp.js.map +1 -0
- package/lib/hooks/useStringField.d.ts +2 -0
- package/lib/hooks/useStringField.js +29 -0
- package/lib/hooks/useStringField.js.map +1 -0
- package/lib/hooks/useStringInput.d.ts +3 -0
- package/lib/hooks/useStringInput.js +31 -0
- package/lib/hooks/useStringInput.js.map +1 -0
- package/lib/hooks/useStringUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useStringUpdateFromProp.js +29 -0
- package/lib/hooks/useStringUpdateFromProp.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.js +27 -0
- package/lib/index.js.map +1 -1
- package/lib/interfaces/questionnaireStore.interface.d.ts +1 -0
- package/lib/stores/useQuestionnaireResponseStore.d.ts +1 -1
- package/lib/stores/useQuestionnaireResponseStore.js +15 -10
- package/lib/stores/useQuestionnaireResponseStore.js.map +1 -1
- package/lib/stores/useQuestionnaireStore.d.ts +2 -1
- package/lib/stores/useQuestionnaireStore.js +5 -2
- package/lib/stores/useQuestionnaireStore.js.map +1 -1
- package/lib/theme/overrides/Overrides.js +1 -2
- package/lib/theme/overrides/Overrides.js.map +1 -1
- package/lib/utils/calculatedExpression.js +2 -2
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/formChanges.d.ts +18 -0
- package/lib/utils/formChanges.js +91 -0
- package/lib/utils/formChanges.js.map +1 -0
- package/lib/utils/formChangesOld.d.ts +18 -0
- package/lib/utils/formChangesOld.js +91 -0
- package/lib/utils/formChangesOld.js.map +1 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +18 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/mapItem.d.ts +2 -2
- package/lib/utils/mapItem.js +3 -3
- package/lib/utils/mapItem.js.map +1 -1
- package/lib/utils/qItem.d.ts +2 -0
- package/lib/utils/qItem.js +22 -0
- package/lib/utils/qItem.js.map +1 -1
- package/lib/utils/qrItem.d.ts +1 -1
- package/lib/utils/qrItem.js +92 -93
- package/lib/utils/qrItem.js.map +1 -1
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js +4 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js.map +1 -1
- package/lib/utils/repopulate.d.ts +9 -0
- package/lib/utils/repopulate.js +133 -0
- package/lib/utils/repopulate.js.map +1 -0
- package/lib/utils/repopulateGenerateItems.d.ts +9 -0
- package/lib/utils/repopulateGenerateItems.js +133 -0
- package/lib/utils/repopulateGenerateItems.js.map +1 -0
- package/lib/utils/repopulateIntoResponse.d.ts +3 -0
- package/lib/utils/repopulateIntoResponse.js +92 -0
- package/lib/utils/repopulateIntoResponse.js.map +1 -0
- package/lib/utils/repopulateItems.d.ts +9 -0
- package/lib/utils/repopulateItems.js +131 -0
- package/lib/utils/repopulateItems.js.map +1 -0
- package/lib/utils/repopulateRepeatGroup.d.ts +4 -0
- package/lib/utils/repopulateRepeatGroup.js +54 -0
- package/lib/utils/repopulateRepeatGroup.js.map +1 -0
- package/package.json +3 -1
- package/src/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.tsx +1 -1
- package/src/components/FormComponents/DecimalItem/DecimalItem.tsx +3 -2
- package/src/components/FormComponents/GridGroup/GridGroup.tsx +2 -2
- package/src/components/FormComponents/GridGroup/GridRow.tsx +2 -2
- package/src/components/FormComponents/GroupItem/GroupItem.tsx +3 -3
- package/src/components/FormComponents/IntegerItem/IntegerItem.tsx +3 -2
- package/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx +1 -1
- package/src/components/FormComponents/RepeatGroup/RepeatGroup.tsx +4 -3
- package/src/{theme/overrides/Backdrop.ts → components/FormComponents/RepeatGroup/index.ts} +1 -17
- package/src/components/FormComponents/RepeatItem/RepeatItem.tsx +1 -1
- package/src/components/FormComponents/SingleItem/index.ts +18 -0
- package/src/components/FormComponents/StringItem/StringItem.tsx +3 -2
- package/src/components/FormComponents/Tables/QItemGroupTable.tsx +15 -6
- package/src/components/FormComponents/Tables/QItemGroupTableRow.tsx +2 -2
- package/src/components/FormComponents/Tables/index.ts +18 -0
- package/src/components/FormComponents/TextItem/TextItem.tsx +3 -2
- package/src/components/FormComponents/index.ts +20 -0
- package/src/components/Renderer/BaseRenderer.tsx +39 -18
- package/src/components/Renderer/FormBodyCollapsible.tsx +2 -2
- package/src/components/Renderer/FormBodyTabbed.tsx +2 -2
- package/src/components/Renderer/FormTopLevelItem.tsx +33 -4
- package/src/components/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useInitialiseRepeatAnswers.ts +28 -17
- package/src/hooks/useInitialiseRepeatGroups.ts +28 -17
- package/src/hooks/useNumberInput.ts +37 -0
- package/src/hooks/useStringInput.ts +37 -0
- package/src/index.ts +43 -0
- package/src/interfaces/questionnaireStore.interface.ts +1 -0
- package/src/stores/useQuestionnaireResponseStore.ts +14 -10
- package/src/stores/useQuestionnaireStore.ts +10 -3
- package/src/theme/overrides/Overrides.ts +0 -2
- package/src/utils/calculatedExpression.ts +2 -2
- package/src/utils/formChanges.ts +141 -0
- package/src/utils/index.ts +18 -0
- package/src/utils/mapItem.ts +6 -4
- package/src/utils/qItem.ts +29 -0
- package/src/utils/qrItem.ts +104 -94
- package/src/utils/questionnaireStoreUtils/createQuestionaireModel.ts +4 -0
- package/src/utils/repopulateIntoResponse.ts +153 -0
- package/src/utils/repopulateItems.ts +207 -0
- package/src/utils/repopulateRepeatGroup.ts +68 -0
- package/lib/theme/overrides/Typography.d.ts +0 -13
- package/lib/theme/overrides/Typography.js.map +0 -1
- package/lib/utils/isHidden.d.ts +0 -3
- package/lib/utils/isHidden.js.map +0 -1
|
@@ -42,6 +42,7 @@ import useTerminologyServerStore from './useTerminologyServerStore';
|
|
|
42
42
|
|
|
43
43
|
export interface UseQuestionnaireStoreType {
|
|
44
44
|
sourceQuestionnaire: Questionnaire;
|
|
45
|
+
itemTypes: Record<string, string>;
|
|
45
46
|
tabs: Tabs;
|
|
46
47
|
currentTabIndex: number;
|
|
47
48
|
variables: Variables;
|
|
@@ -68,11 +69,15 @@ export interface UseQuestionnaireStoreType {
|
|
|
68
69
|
toggleEnableWhenActivation: (isActivated: boolean) => void;
|
|
69
70
|
updateExpressions: (updatedResponse: QuestionnaireResponse) => void;
|
|
70
71
|
addCodingToCache: (valueSetUrl: string, codings: Coding[]) => void;
|
|
71
|
-
updatePopulatedProperties: (
|
|
72
|
+
updatePopulatedProperties: (
|
|
73
|
+
populatedResponse: QuestionnaireResponse,
|
|
74
|
+
persistTabIndex?: boolean
|
|
75
|
+
) => QuestionnaireResponse;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
const useQuestionnaireStore = create<UseQuestionnaireStoreType>()((set, get) => ({
|
|
75
79
|
sourceQuestionnaire: cloneDeep(emptyQuestionnaire),
|
|
80
|
+
itemTypes: {},
|
|
76
81
|
tabs: {},
|
|
77
82
|
currentTabIndex: 0,
|
|
78
83
|
variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
|
|
@@ -115,6 +120,7 @@ const useQuestionnaireStore = create<UseQuestionnaireStoreType>()((set, get) =>
|
|
|
115
120
|
|
|
116
121
|
set({
|
|
117
122
|
sourceQuestionnaire: questionnaire,
|
|
123
|
+
itemTypes: questionnaireModel.itemTypes,
|
|
118
124
|
tabs: questionnaireModel.tabs,
|
|
119
125
|
currentTabIndex: firstVisibleTab,
|
|
120
126
|
variables: questionnaireModel.variables,
|
|
@@ -131,6 +137,7 @@ const useQuestionnaireStore = create<UseQuestionnaireStoreType>()((set, get) =>
|
|
|
131
137
|
destroySourceQuestionnaire: () =>
|
|
132
138
|
set({
|
|
133
139
|
sourceQuestionnaire: cloneDeep(emptyQuestionnaire),
|
|
140
|
+
itemTypes: {},
|
|
134
141
|
tabs: {},
|
|
135
142
|
currentTabIndex: 0,
|
|
136
143
|
variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
|
|
@@ -197,7 +204,7 @@ const useQuestionnaireStore = create<UseQuestionnaireStoreType>()((set, get) =>
|
|
|
197
204
|
[valueSetUrl]: codings
|
|
198
205
|
}
|
|
199
206
|
})),
|
|
200
|
-
updatePopulatedProperties: (populatedResponse: QuestionnaireResponse) => {
|
|
207
|
+
updatePopulatedProperties: (populatedResponse: QuestionnaireResponse, persistTabIndex) => {
|
|
201
208
|
const initialCalculatedExpressions = evaluateInitialCalculatedExpressions({
|
|
202
209
|
initialResponse: populatedResponse,
|
|
203
210
|
calculatedExpressions: get().calculatedExpressions,
|
|
@@ -229,7 +236,7 @@ const useQuestionnaireStore = create<UseQuestionnaireStoreType>()((set, get) =>
|
|
|
229
236
|
enableWhenLinkedQuestions: initialEnableWhenLinkedQuestions,
|
|
230
237
|
enableWhenExpressions: initialEnableWhenExpressions,
|
|
231
238
|
calculatedExpressions: initialCalculatedExpressions,
|
|
232
|
-
currentTabIndex: firstVisibleTab
|
|
239
|
+
currentTabIndex: persistTabIndex ? get().currentTabIndex : firstVisibleTab
|
|
233
240
|
}));
|
|
234
241
|
|
|
235
242
|
return updatedResponse;
|
|
@@ -20,7 +20,6 @@ import Paper from './Paper';
|
|
|
20
20
|
import Input from './Input';
|
|
21
21
|
import Table from './Table';
|
|
22
22
|
import Button from './Button';
|
|
23
|
-
import Backdrop from './Backdrop';
|
|
24
23
|
import Autocomplete from './Autocomplete';
|
|
25
24
|
import Accordion from './Accordion';
|
|
26
25
|
import SpeedDial from './SpeedDial';
|
|
@@ -34,7 +33,6 @@ function ComponentsOverrides(theme: Theme) {
|
|
|
34
33
|
Input(theme),
|
|
35
34
|
Paper(),
|
|
36
35
|
Button(theme),
|
|
37
|
-
Backdrop(theme),
|
|
38
36
|
Autocomplete(theme),
|
|
39
37
|
SpeedDial(theme)
|
|
40
38
|
);
|
|
@@ -30,7 +30,7 @@ import _isEqual from 'lodash/isEqual';
|
|
|
30
30
|
import { emptyResponse } from './emptyResource';
|
|
31
31
|
import { createFhirPathContext } from './fhirpath';
|
|
32
32
|
import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
|
|
33
|
-
import {
|
|
33
|
+
import { updateQrItemsInGroup } from './qrItem';
|
|
34
34
|
import cloneDeep from 'lodash.clonedeep';
|
|
35
35
|
|
|
36
36
|
interface EvaluateInitialCalculatedExpressionsParams {
|
|
@@ -213,7 +213,7 @@ function initialiseItemCalculatedExpressionValueRecursive(
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
if (newQrItem) {
|
|
216
|
-
|
|
216
|
+
updateQrItemsInGroup(
|
|
217
217
|
newQrItem,
|
|
218
218
|
null,
|
|
219
219
|
qrItem ?? { linkId: qItem.linkId, text: qItem.text, item: [] },
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 Commonwealth Scientific and Industrial Research
|
|
3
|
+
* Organisation (CSIRO) ABN 41 687 119 230.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { QuestionnaireResponseItemAnswer } from 'fhir/r4';
|
|
19
|
+
|
|
20
|
+
type JsonDiffOperator = '+' | '-' | '~' | ' ';
|
|
21
|
+
|
|
22
|
+
type QuestionnaireResponseItemDiffArray = [JsonDiffOperator, QuestionnaireResponseDiffItem];
|
|
23
|
+
type QuestionnaireResponseItemAnswerDiffArray = [JsonDiffOperator, QuestionnaireResponseItemAnswer];
|
|
24
|
+
|
|
25
|
+
interface QuestionnaireResponseDiffItem {
|
|
26
|
+
linkId: string;
|
|
27
|
+
item: QuestionnaireResponseItemDiffArray[] | undefined;
|
|
28
|
+
answer:
|
|
29
|
+
| QuestionnaireResponseItemAnswer[]
|
|
30
|
+
| QuestionnaireResponseItemAnswerDiffArray[]
|
|
31
|
+
| undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ItemChange {
|
|
35
|
+
linkId: string;
|
|
36
|
+
itemType: string;
|
|
37
|
+
operation: 'add' | 'remove' | 'update';
|
|
38
|
+
value: QuestionnaireResponseItemAnswer;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function readFormChanges(formChanges: object, itemTypes: Record<string, string>) {
|
|
42
|
+
if (!('item' in formChanges)) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const changedItems: Record<string, ItemChange | null> = {};
|
|
47
|
+
const diffArrays = formChanges.item as QuestionnaireResponseItemDiffArray[];
|
|
48
|
+
for (const diffArray of diffArrays) {
|
|
49
|
+
readItemChangesRecursive(diffArray, itemTypes, changedItems);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return changedItems;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function readItemChangesRecursive(
|
|
56
|
+
diffArray: QuestionnaireResponseItemDiffArray,
|
|
57
|
+
itemTypes: Record<string, string>,
|
|
58
|
+
changedItems: Record<string, ItemChange | null>
|
|
59
|
+
) {
|
|
60
|
+
const operator = diffArray[0];
|
|
61
|
+
const diffItem = diffArray[1];
|
|
62
|
+
|
|
63
|
+
if (!operator || !diffItem) {
|
|
64
|
+
return changedItems;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
changedItems[diffItem.linkId] = null;
|
|
68
|
+
|
|
69
|
+
const childDiffArrays = diffItem.item;
|
|
70
|
+
if (childDiffArrays && childDiffArrays.length > 0) {
|
|
71
|
+
for (const childDiffArray of childDiffArrays) {
|
|
72
|
+
readItemChangesRecursive(childDiffArray, itemTypes, changedItems);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// if (operator !== ' ') {
|
|
77
|
+
// console.log(operator, diffItem);
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
// explore answer diff arrays
|
|
81
|
+
// exmaple: [["+", {"valueString": "test"}], ["-", {"valueString": "test"}]]
|
|
82
|
+
// or it could be a regular answer array
|
|
83
|
+
// exmaple: [{"valueString": "test"}, {"valueString": "test"}]
|
|
84
|
+
|
|
85
|
+
const answerDiffArrays = diffItem.answer;
|
|
86
|
+
if (answerDiffArrays && isAnswerDiffArray(answerDiffArrays)) {
|
|
87
|
+
for (const answerDiffArray of answerDiffArrays) {
|
|
88
|
+
getItemChange(diffItem, answerDiffArray, itemTypes, changedItems);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return changedItems;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getItemChange(
|
|
96
|
+
diffItem: QuestionnaireResponseDiffItem,
|
|
97
|
+
answerDiffArray: QuestionnaireResponseItemAnswerDiffArray,
|
|
98
|
+
itemTypes: Record<string, string>,
|
|
99
|
+
changedItems: Record<string, ItemChange | null>
|
|
100
|
+
) {
|
|
101
|
+
const operator = answerDiffArray[0];
|
|
102
|
+
const answer = answerDiffArray[1];
|
|
103
|
+
|
|
104
|
+
if (operator === ' ') {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const itemType = itemTypes[diffItem.linkId];
|
|
109
|
+
const operation = answerDiffOperationSwitcher(operator);
|
|
110
|
+
if (operation) {
|
|
111
|
+
changedItems[diffItem.linkId] = {
|
|
112
|
+
linkId: diffItem.linkId,
|
|
113
|
+
itemType: itemType,
|
|
114
|
+
operation: operation,
|
|
115
|
+
value: answer
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function answerDiffOperationSwitcher(operator: JsonDiffOperator): ItemChange['operation'] | null {
|
|
121
|
+
switch (operator) {
|
|
122
|
+
case '+':
|
|
123
|
+
return 'add';
|
|
124
|
+
case '-':
|
|
125
|
+
return 'remove';
|
|
126
|
+
case '~':
|
|
127
|
+
return 'update';
|
|
128
|
+
default:
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// type predicates
|
|
134
|
+
function isAnswerDiffArray(answer: any[]): answer is QuestionnaireResponseItemAnswerDiffArray[] {
|
|
135
|
+
return answer.every(
|
|
136
|
+
(answerItem) =>
|
|
137
|
+
Array.isArray(answerItem) &&
|
|
138
|
+
typeof answerItem[0] === 'string' &&
|
|
139
|
+
typeof answerItem[1] === 'object'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 Commonwealth Scientific and Industrial Research
|
|
3
|
+
* Organisation (CSIRO) ABN 41 687 119 230.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export { isSpecificItemControl } from './itemControl';
|
package/src/utils/mapItem.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
18
|
+
import type { Questionnaire, QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
19
19
|
import { isRepeatItemAndNotCheckbox } from './qItem';
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -84,13 +84,15 @@ export function getQrItemsIndex(
|
|
|
84
84
|
*
|
|
85
85
|
* @author Sean Fong
|
|
86
86
|
*/
|
|
87
|
-
export function mapQItemsIndex(
|
|
88
|
-
|
|
87
|
+
export function mapQItemsIndex(
|
|
88
|
+
questionnaireOrQItem: QuestionnaireItem | Questionnaire
|
|
89
|
+
): Record<string, number> {
|
|
90
|
+
if (!questionnaireOrQItem.item) {
|
|
89
91
|
return {};
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
// generate a <linkId, QItemIndex> dictionary
|
|
93
|
-
return
|
|
95
|
+
return questionnaireOrQItem.item.reduce((mapping: Record<string, number>, item, i) => {
|
|
94
96
|
mapping[item.linkId] = i;
|
|
95
97
|
return mapping;
|
|
96
98
|
}, {});
|
package/src/utils/qItem.ts
CHANGED
|
@@ -101,3 +101,32 @@ export function getXHtmlStringFromQuestionnaire(questionnaire: Questionnaire): s
|
|
|
101
101
|
}
|
|
102
102
|
return null;
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
export function getLinkIdTypeTuples(questionnaire: Questionnaire): [string, string][] {
|
|
106
|
+
if (!questionnaire.item || questionnaire.item.length === 0) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const linkIds: [string, string][] = [];
|
|
111
|
+
for (const topLevelItem of questionnaire.item) {
|
|
112
|
+
linkIds.push(...getLinkIdTypeTuplesFromItemRecursive(topLevelItem));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return linkIds;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function getLinkIdTypeTuplesFromItemRecursive(qItem: QuestionnaireItem): [string, string][] {
|
|
119
|
+
const linkIds: [string, string][] = [];
|
|
120
|
+
|
|
121
|
+
if (qItem.linkId) {
|
|
122
|
+
linkIds.push([qItem.linkId, qItem.type]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (qItem.item) {
|
|
126
|
+
for (const childItem of qItem.item) {
|
|
127
|
+
linkIds.push(...getLinkIdTypeTuplesFromItemRecursive(childItem));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return linkIds;
|
|
132
|
+
}
|
package/src/utils/qrItem.ts
CHANGED
|
@@ -134,116 +134,126 @@ export function createEmptyQrItemWithUnit(
|
|
|
134
134
|
*
|
|
135
135
|
* @author Sean Fong
|
|
136
136
|
*/
|
|
137
|
-
export function
|
|
137
|
+
export function updateQrItemsInGroup(
|
|
138
138
|
newQrItem: QuestionnaireResponseItem | null,
|
|
139
139
|
newQrRepeatGroup: QrRepeatGroup | null,
|
|
140
|
-
|
|
140
|
+
questionnaireResponseOrQrItem: QuestionnaireResponseItem | QuestionnaireResponse,
|
|
141
141
|
qItemsIndexMap: Record<string, number>
|
|
142
142
|
): void {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (newQrItem && newQrItem.linkId in qItemsIndexMap) {
|
|
149
|
-
if (qrGroup.item.length === 0) {
|
|
150
|
-
qrGroup.item.push(newQrItem);
|
|
151
|
-
} else {
|
|
152
|
-
// Get actual sequence index of qrItem within qrGroup
|
|
153
|
-
const newQrItemIndex = qItemsIndexMap[newQrItem.linkId];
|
|
154
|
-
|
|
155
|
-
for (let i = 0; i < qrItemsRealIndexArr.length; i++) {
|
|
156
|
-
// Add qrItem at the end of qrGroup if it is larger than the other indexes
|
|
157
|
-
if (newQrItemIndex > qrItemsRealIndexArr[i]) {
|
|
158
|
-
if (i === qrItemsRealIndexArr.length - 1) {
|
|
159
|
-
qrGroup.item.push(newQrItem);
|
|
160
|
-
}
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
143
|
+
const qrItems = questionnaireResponseOrQrItem.item;
|
|
144
|
+
if (!qrItems) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
163
147
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// newQrItem has answer value
|
|
168
|
-
qrGroup.item[i] = newQrItem;
|
|
169
|
-
} else {
|
|
170
|
-
// newQrItem has no answer value
|
|
171
|
-
qrGroup.item.splice(i, 1);
|
|
172
|
-
}
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
148
|
+
// Get actual sequence indexes of qrItems present within a qrGroup
|
|
149
|
+
// e.g. qrGroup has 4 fields but only the 2nd and 3rd field have values - resulting array is [1, 2]
|
|
150
|
+
const qrItemsRealIndexArr = qrItems.map((qrItem) => qItemsIndexMap[qrItem.linkId]);
|
|
175
151
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
152
|
+
if (newQrItem && newQrItem.linkId in qItemsIndexMap) {
|
|
153
|
+
if (qrItems.length === 0) {
|
|
154
|
+
qrItems.push(newQrItem);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Get actual sequence index of qrItem within qrGroup
|
|
159
|
+
const newQrItemIndex = qItemsIndexMap[newQrItem.linkId];
|
|
160
|
+
for (let i = 0; i < qrItemsRealIndexArr.length; i++) {
|
|
161
|
+
// Add qrItem at the end of qrGroup if it is larger than the other indexes
|
|
162
|
+
if (newQrItemIndex > qrItemsRealIndexArr[i]) {
|
|
163
|
+
if (i === qrItemsRealIndexArr.length - 1) {
|
|
164
|
+
qrItems.push(newQrItem);
|
|
181
165
|
}
|
|
166
|
+
continue;
|
|
182
167
|
}
|
|
183
|
-
} else if (newQrRepeatGroup && newQrRepeatGroup.linkId in qItemsIndexMap) {
|
|
184
|
-
const newQrItems = newQrRepeatGroup.qrItems;
|
|
185
|
-
if (qrGroup.item.length === 0) {
|
|
186
|
-
qrGroup.item.push(...newQrItems);
|
|
187
|
-
} else {
|
|
188
|
-
// Get actual sequence index of qrItems within qrGroup
|
|
189
|
-
const newQrItemIndex = qItemsIndexMap[newQrRepeatGroup.linkId];
|
|
190
|
-
|
|
191
|
-
for (let i = 0; i < qrItemsRealIndexArr.length; i++) {
|
|
192
|
-
// Add qrItem at the end of qrGroup if it is larger than the other indexes
|
|
193
|
-
if (newQrItemIndex > qrItemsRealIndexArr[i]) {
|
|
194
|
-
if (i === qrItemsRealIndexArr.length - 1) {
|
|
195
|
-
qrGroup.item.push(...newQrItems);
|
|
196
|
-
}
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
168
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
169
|
+
// Replace or delete qrItem at its supposed position if its index is already present within qrGroup
|
|
170
|
+
if (newQrItemIndex === qrItemsRealIndexArr[i]) {
|
|
171
|
+
// newQrItem has answer value
|
|
172
|
+
if (newQrItem.item?.length || newQrItem.answer?.length) {
|
|
173
|
+
qrItems[i] = newQrItem;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
207
176
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
177
|
+
// newQrItem has no answer value
|
|
178
|
+
qrItems.splice(i, 1);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Add qrItem at its supposed position if its index is not present within qrGroup
|
|
183
|
+
if (newQrItemIndex < qrItemsRealIndexArr[i]) {
|
|
184
|
+
qrItems.splice(i, 0, newQrItem);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (newQrRepeatGroup && newQrRepeatGroup.linkId in qItemsIndexMap) {
|
|
191
|
+
const newQrItems = newQrRepeatGroup.qrItems;
|
|
192
|
+
if (qrItems.length === 0) {
|
|
193
|
+
qrItems.push(...newQrItems);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Get actual sequence index of qrItems within qrGroup
|
|
198
|
+
const newQrItemIndex = qItemsIndexMap[newQrRepeatGroup.linkId];
|
|
199
|
+
for (let i = 0; i < qrItemsRealIndexArr.length; i++) {
|
|
200
|
+
// Add qrItem at the end of qrGroup if it is larger than the other indexes
|
|
201
|
+
if (newQrItemIndex > qrItemsRealIndexArr[i]) {
|
|
202
|
+
if (i === qrItemsRealIndexArr.length - 1) {
|
|
203
|
+
qrItems.push(...newQrItems);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Replace or delete qrItem at its supposed position if its index is already present within qrGroup
|
|
209
|
+
if (newQrItemIndex === qrItemsRealIndexArr[i]) {
|
|
210
|
+
// Get number of repeatGroupItems that has the same linkId present in qrGroup
|
|
211
|
+
let repeatGroupItemCount = 0;
|
|
212
|
+
while (newQrItemIndex === qrItemsRealIndexArr[i + repeatGroupItemCount]) {
|
|
213
|
+
repeatGroupItemCount++;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Replace each repeat group qrItem with their new counterparts
|
|
217
|
+
if (newQrItems.length === repeatGroupItemCount) {
|
|
218
|
+
for (let j = 0; j < newQrItems.length; j++) {
|
|
219
|
+
qrItems[i + j] = newQrItems[j];
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (newQrItems.length > repeatGroupItemCount) {
|
|
225
|
+
// Replace each repeat group qrItem with their new counterparts,
|
|
226
|
+
// followed by adding an extra newQrItem behind the newly replaced qrItems
|
|
227
|
+
for (let j = 0, k = repeatGroupItemCount; j < newQrItems.length; j++, k--) {
|
|
228
|
+
if (k > 0) {
|
|
229
|
+
qrItems[i + j] = newQrItems[j];
|
|
230
|
+
} else {
|
|
231
|
+
qrItems.splice(i + j, 0, newQrItems[j]);
|
|
236
232
|
}
|
|
237
233
|
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
238
236
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
237
|
+
if (newQrItems.length < repeatGroupItemCount) {
|
|
238
|
+
// Replace each repeat group qrItem with their new counterparts (except the last one),
|
|
239
|
+
// followed by deleting the last newQrItem which wasn't replaced
|
|
240
|
+
for (let j = 0; j < repeatGroupItemCount; j++) {
|
|
241
|
+
if (j <= newQrItems.length - 1) {
|
|
242
|
+
qrItems[i + j] = newQrItems[j];
|
|
243
|
+
} else {
|
|
244
|
+
qrItems.splice(i + j, 1);
|
|
243
245
|
}
|
|
244
|
-
break;
|
|
245
246
|
}
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Add qrItem at its supposed position if its index is not present within qrGroup
|
|
252
|
+
if (newQrItemIndex < qrItemsRealIndexArr[i]) {
|
|
253
|
+
for (let j = 0; j < newQrItems.length; j++) {
|
|
254
|
+
qrItems.splice(i + j, 0, newQrItems[j]);
|
|
246
255
|
}
|
|
256
|
+
break;
|
|
247
257
|
}
|
|
248
258
|
}
|
|
249
259
|
}
|
|
@@ -27,6 +27,7 @@ import { extractOtherExtensions } from './extractOtherExtensions';
|
|
|
27
27
|
import type { Variables } from '../../interfaces/variables.interface';
|
|
28
28
|
import { resolveValueSets } from './resolveValueSets';
|
|
29
29
|
import { addAdditionalVariables } from './addAdditionalVariables';
|
|
30
|
+
import { getLinkIdTypeTuples } from '../qItem';
|
|
30
31
|
|
|
31
32
|
export async function createQuestionnaireModel(
|
|
32
33
|
questionnaire: Questionnaire,
|
|
@@ -37,6 +38,7 @@ export async function createQuestionnaireModel(
|
|
|
37
38
|
return createEmptyModel();
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
const itemTypes: Record<string, string> = Object.fromEntries(getLinkIdTypeTuples(questionnaire));
|
|
40
42
|
const tabs: Tabs = extractTabs(questionnaire);
|
|
41
43
|
|
|
42
44
|
const launchContexts: Record<string, LaunchContext> = extractLaunchContexts(questionnaire);
|
|
@@ -75,6 +77,7 @@ export async function createQuestionnaireModel(
|
|
|
75
77
|
processedValueSetCodings = resolveValueSetsResult.processedValueSetCodings;
|
|
76
78
|
|
|
77
79
|
return {
|
|
80
|
+
itemTypes,
|
|
78
81
|
tabs,
|
|
79
82
|
variables,
|
|
80
83
|
launchContexts,
|
|
@@ -89,6 +92,7 @@ export async function createQuestionnaireModel(
|
|
|
89
92
|
|
|
90
93
|
function createEmptyModel(): QuestionnaireModel {
|
|
91
94
|
return {
|
|
95
|
+
itemTypes: {},
|
|
92
96
|
tabs: {},
|
|
93
97
|
variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
|
|
94
98
|
launchContexts: {},
|