@aehrc/smart-forms-renderer 0.37.2 → 0.38.4

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.
Files changed (104) hide show
  1. package/lib/components/FormComponents/GroupItem/GroupItemSwitcher.js +6 -2
  2. package/lib/components/FormComponents/GroupItem/GroupItemSwitcher.js.map +1 -1
  3. package/lib/components/FormComponents/QuantityItem/QuantityComparatorField.d.ts +12 -0
  4. package/lib/components/FormComponents/QuantityItem/QuantityComparatorField.js +13 -0
  5. package/lib/components/FormComponents/QuantityItem/QuantityComparatorField.js.map +1 -0
  6. package/lib/components/FormComponents/QuantityItem/QuantityField.d.ts +15 -0
  7. package/lib/components/FormComponents/QuantityItem/QuantityField.js +14 -0
  8. package/lib/components/FormComponents/QuantityItem/QuantityField.js.map +1 -0
  9. package/lib/components/FormComponents/QuantityItem/QuantityItem.d.ts +9 -0
  10. package/lib/components/FormComponents/QuantityItem/QuantityItem.js +144 -0
  11. package/lib/components/FormComponents/QuantityItem/QuantityItem.js.map +1 -0
  12. package/lib/components/FormComponents/QuantityItem/QuantityUnitField.d.ts +12 -0
  13. package/lib/components/FormComponents/QuantityItem/QuantityUnitField.js +10 -0
  14. package/lib/components/FormComponents/QuantityItem/QuantityUnitField.js.map +1 -0
  15. package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js +2 -1
  16. package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js.map +1 -1
  17. package/lib/components/FormComponents/Tables/GroupTable.d.ts +2 -2
  18. package/lib/components/FormComponents/Tables/GroupTable.js +2 -2
  19. package/lib/components/FormComponents/Tables/GroupTable.js.map +1 -1
  20. package/lib/components/FormComponents/Tables/GroupTableBody.d.ts +2 -2
  21. package/lib/components/FormComponents/Tables/GroupTableBody.js +2 -2
  22. package/lib/components/FormComponents/Tables/GroupTableBody.js.map +1 -1
  23. package/lib/components/FormComponents/Tables/GroupTableRow.d.ts +3 -2
  24. package/lib/components/FormComponents/Tables/GroupTableRow.js +16 -5
  25. package/lib/components/FormComponents/Tables/GroupTableRow.js.map +1 -1
  26. package/lib/components/FormComponents/Tables/GroupTableView.d.ts +2 -2
  27. package/lib/components/FormComponents/Tables/GroupTableView.js +10 -10
  28. package/lib/components/FormComponents/Tables/GroupTableView.js.map +1 -1
  29. package/lib/components/Renderer/FormTopLevelItem.js +6 -0
  30. package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
  31. package/lib/hooks/useDecimalCalculatedExpression.d.ts +2 -2
  32. package/lib/hooks/useQuantityCalculatedExpression.d.ts +14 -0
  33. package/lib/hooks/useQuantityCalculatedExpression.js +105 -0
  34. package/lib/hooks/useQuantityCalculatedExpression.js.map +1 -0
  35. package/lib/hooks/useRenderingExtensions.d.ts +2 -1
  36. package/lib/hooks/useRenderingExtensions.js +3 -2
  37. package/lib/hooks/useRenderingExtensions.js.map +1 -1
  38. package/lib/hooks/useStringInput.js +1 -0
  39. package/lib/hooks/useStringInput.js.map +1 -1
  40. package/lib/interfaces/valueSet.interface.d.ts +15 -0
  41. package/lib/tests/test-data/initialValueSample.d.ts +2 -0
  42. package/lib/tests/test-data/initialValueSample.js +802 -0
  43. package/lib/tests/test-data/initialValueSample.js.map +1 -0
  44. package/lib/utils/answerExpression.d.ts +18 -0
  45. package/lib/utils/answerExpression.js +133 -0
  46. package/lib/utils/answerExpression.js.map +1 -0
  47. package/lib/utils/calculatedExpression.js +5 -2
  48. package/lib/utils/calculatedExpression.js.map +1 -1
  49. package/lib/utils/dynamicValueSet.d.ts +5 -0
  50. package/lib/utils/dynamicValueSet.js +96 -0
  51. package/lib/utils/dynamicValueSet.js.map +1 -0
  52. package/lib/utils/fhirpathAsyncUtils/fhirpath-async.d.ts +14 -0
  53. package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js +639 -0
  54. package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js.map +1 -0
  55. package/lib/utils/fhirpathAsyncUtils/outcome-utils.d.ts +3 -0
  56. package/lib/utils/fhirpathAsyncUtils/outcome-utils.js +41 -0
  57. package/lib/utils/fhirpathAsyncUtils/outcome-utils.js.map +1 -0
  58. package/lib/utils/genericRecursive.d.ts +17 -0
  59. package/lib/utils/genericRecursive.js +82 -0
  60. package/lib/utils/genericRecursive.js.map +1 -0
  61. package/lib/utils/initialise.js +47 -57
  62. package/lib/utils/initialise.js.map +1 -1
  63. package/lib/utils/itemControl.d.ts +7 -1
  64. package/lib/utils/itemControl.js +14 -0
  65. package/lib/utils/itemControl.js.map +1 -1
  66. package/lib/utils/quantity.d.ts +4 -0
  67. package/lib/utils/quantity.js +49 -0
  68. package/lib/utils/quantity.js.map +1 -0
  69. package/lib/utils/repopulateIntoResponse.js +1 -1
  70. package/lib/utils/repopulateIntoResponse.js.map +1 -1
  71. package/lib/utils/valueSet.d.ts +2 -1
  72. package/lib/utils/valueSet.js +22 -0
  73. package/lib/utils/valueSet.js.map +1 -1
  74. package/package.json +1 -1
  75. package/src/components/FormComponents/GroupItem/GroupItemSwitcher.tsx +16 -0
  76. package/src/components/FormComponents/QuantityItem/QuantityComparatorField.tsx +40 -0
  77. package/src/components/FormComponents/QuantityItem/QuantityField.tsx +60 -0
  78. package/src/components/FormComponents/QuantityItem/QuantityItem.tsx +286 -0
  79. package/src/components/FormComponents/QuantityItem/QuantityUnitField.tsx +38 -0
  80. package/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx +2 -1
  81. package/src/components/FormComponents/Tables/GroupTable.tsx +4 -0
  82. package/src/components/FormComponents/Tables/GroupTableBody.tsx +5 -1
  83. package/src/components/FormComponents/Tables/GroupTableRow.tsx +98 -46
  84. package/src/components/FormComponents/Tables/GroupTableView.tsx +30 -26
  85. package/src/components/Renderer/FormTopLevelItem.tsx +17 -0
  86. package/src/hooks/useDecimalCalculatedExpression.ts +2 -2
  87. package/src/hooks/useQuantityCalculatedExpression.ts +177 -0
  88. package/src/hooks/useRenderingExtensions.ts +5 -2
  89. package/src/hooks/useStringInput.ts +1 -0
  90. package/src/interfaces/valueSet.interface.ts +19 -0
  91. package/src/stories/assets/questionnaires/QItemControlGroup.ts +229 -5
  92. package/src/stories/assets/questionnaires/QPrePopTester.ts +30 -0
  93. package/src/stories/assets/questionnaires/QQuantity.ts +283 -1
  94. package/src/stories/itemTypes/Quantity.stories.tsx +33 -1
  95. package/src/stories/sdc/ItemControlGroup.stories.tsx +10 -3
  96. package/src/tests/initial.test.ts +34 -0
  97. package/src/tests/test-data/initialValueSample.ts +826 -0
  98. package/src/utils/calculatedExpression.ts +6 -2
  99. package/src/utils/{updateQr.ts → genericRecursive.ts} +47 -2
  100. package/src/utils/initialise.ts +67 -73
  101. package/src/utils/itemControl.ts +19 -1
  102. package/src/utils/quantity.ts +62 -0
  103. package/src/utils/repopulateIntoResponse.ts +1 -1
  104. package/src/utils/valueSet.ts +32 -1
@@ -34,7 +34,7 @@ import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
34
34
  import { updateQrItemsInGroup } from './qrItem';
35
35
  import cloneDeep from 'lodash.clonedeep';
36
36
  import dayjs from 'dayjs';
37
- import { updateQuestionnaireResponse } from './updateQr';
37
+ import { updateQuestionnaireResponse } from './genericRecursive';
38
38
 
39
39
  interface EvaluateInitialCalculatedExpressionsParams {
40
40
  initialResponse: QuestionnaireResponse;
@@ -323,7 +323,11 @@ function parseValueToAnswer(qItem: QuestionnaireItem, value: any): Questionnaire
323
323
  }
324
324
  }
325
325
 
326
- if (typeof value === 'object') {
326
+ if (typeof value === 'object' && value.unit) {
327
+ return { valueQuantity: value };
328
+ }
329
+
330
+ if (typeof value === 'object' && value.system && value.code) {
327
331
  return { valueCoding: value };
328
332
  }
329
333
 
@@ -24,7 +24,7 @@ import type {
24
24
  import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
25
25
  import { qrItemHasItemsOrAnswer } from './manageForm';
26
26
 
27
- export type RepopulateFunction<T> = (
27
+ export type RecursiveUpdateFunction<T> = (
28
28
  qItem: QuestionnaireItem,
29
29
  qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null,
30
30
  extraData: T
@@ -39,7 +39,7 @@ export type RepopulateFunction<T> = (
39
39
  export function updateQuestionnaireResponse<T>(
40
40
  questionnaire: Questionnaire,
41
41
  questionnaireResponse: QuestionnaireResponse,
42
- recursiveUpdateFunction: RepopulateFunction<T>,
42
+ recursiveUpdateFunction: RecursiveUpdateFunction<T>,
43
43
  extraData: T
44
44
  ) {
45
45
  if (
@@ -86,3 +86,48 @@ export function updateQuestionnaireResponse<T>(
86
86
 
87
87
  return { ...questionnaireResponse, item: topLevelQrItems };
88
88
  }
89
+
90
+ export type RecursiveReadArrayFunction<T> = (
91
+ qItem: QuestionnaireItem,
92
+ qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null
93
+ ) => T[] | null;
94
+
95
+ /**
96
+ * A generic (and safe) way to read an array of element(s) i.e. QuestionnaireResponseItem[], extracted Observation[] from a QuestionnaireResponse given a recursive function.
97
+ * 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
98
+ *
99
+ * @author Sean Fong
100
+ */
101
+ export function readQuestionnaireResponse<T>(
102
+ questionnaire: Questionnaire,
103
+ questionnaireResponse: QuestionnaireResponse,
104
+ recursiveReadFunction: RecursiveReadArrayFunction<T>
105
+ ): T[] {
106
+ if (!questionnaire.item || questionnaire.item.length === 0) {
107
+ return [];
108
+ }
109
+
110
+ const qItemsIndexMap = mapQItemsIndex(questionnaire);
111
+ const topLevelQRItemsByIndex = getQrItemsIndex(
112
+ questionnaire.item,
113
+ questionnaireResponse.item ?? [],
114
+ qItemsIndexMap
115
+ );
116
+
117
+ const readValueArray = [];
118
+ for (const [index, topLevelQItem] of questionnaire.item.entries()) {
119
+ const topLevelQRItemOrItems = topLevelQRItemsByIndex[index] ?? {
120
+ linkId: topLevelQItem.linkId,
121
+ text: topLevelQItem.text,
122
+ item: []
123
+ };
124
+
125
+ const readValue = recursiveReadFunction(topLevelQItem, topLevelQRItemOrItems);
126
+
127
+ if (readValue) {
128
+ readValueArray.push(...readValue);
129
+ }
130
+ }
131
+
132
+ return readValueArray;
133
+ }
@@ -34,6 +34,8 @@ import { assignPopulatedAnswersToEnableWhen } from './enableWhen';
34
34
  import type { CalculatedExpression } from '../interfaces/calculatedExpression.interface';
35
35
  import { evaluateInitialCalculatedExpressions } from './calculatedExpression';
36
36
  import { createQuestionnaireResponseItemMap } from './questionnaireResponseStoreUtils/updatableResponseItems';
37
+ import { readQuestionnaireResponse } from './genericRecursive';
38
+ import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
37
39
 
38
40
  /**
39
41
  * Initialise a questionnaireResponse from a given questionnaire
@@ -58,7 +60,11 @@ export function initialiseQuestionnaireResponse(
58
60
 
59
61
  const firstTopLevelItem = questionnaire?.item?.[0];
60
62
  if (firstTopLevelItem && !questionnaireResponse.item) {
61
- const initialItems = readItemInitialValues(questionnaire);
63
+ const initialItems = readQuestionnaireResponse(
64
+ questionnaire,
65
+ questionnaireResponse,
66
+ readInitialValuesRecursive
67
+ );
62
68
 
63
69
  if (initialItems && initialItems.length > 0) {
64
70
  questionnaireResponse.item = initialItems;
@@ -97,77 +103,81 @@ function createQuestionnaireReference(questionnaire: Questionnaire) {
97
103
  return '';
98
104
  }
99
105
 
100
- function readItemInitialValues(questionnaire: Questionnaire) {
101
- if (!questionnaire.item || questionnaire.item.length === 0) {
102
- return null;
103
- }
104
-
105
- const topLevelQrItems: QuestionnaireResponseItem[] = [];
106
- for (const topLevelQItem of questionnaire.item) {
107
- const updatedTopLevelQRItem = readItemInitialValueRecursive(topLevelQItem);
108
-
109
- if (Array.isArray(updatedTopLevelQRItem)) {
110
- if (updatedTopLevelQRItem.length > 0) {
111
- topLevelQrItems.push(...updatedTopLevelQRItem);
112
- }
113
- continue;
114
- }
115
-
116
- if (updatedTopLevelQRItem) {
117
- topLevelQrItems.push(updatedTopLevelQRItem);
118
- }
119
- }
120
-
121
- if (topLevelQrItems.length === 0) {
122
- return null;
123
- }
124
-
125
- return topLevelQrItems;
126
- }
127
-
128
- function readItemInitialValueRecursive(
129
- qItem: QuestionnaireItem
130
- ): QuestionnaireResponseItem[] | QuestionnaireResponseItem | null {
106
+ function readInitialValuesRecursive(
107
+ qItem: QuestionnaireItem,
108
+ qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null
109
+ ): QuestionnaireResponseItem[] | null {
110
+ // Process items with child items
111
+ const initialValues: QuestionnaireResponseItem[] = [];
131
112
  const childQItems = qItem.item;
132
113
  if (childQItems && childQItems.length > 0) {
133
- // TODO No support for multiple rows at the moment
114
+ // Process repeating group items separately
134
115
  if (qItem.type === 'group' && qItem.repeats) {
135
- const initialItemsFromRepeatGroup = getInitialValueAnswersFromRepeatGroup(qItem);
136
- return createNewRepeatGroupQuestionnaireResponseItem(qItem, initialItemsFromRepeatGroup);
116
+ const initialItemsFromRepeatGroup = getInitialValueAnswersFromRepeatGroup(
117
+ qItem,
118
+ qrItemOrItems
119
+ );
120
+ const newRepeatGroupQuestionnaireResponseItem = createNewRepeatGroupQuestionnaireResponseItem(
121
+ qItem,
122
+ initialItemsFromRepeatGroup
123
+ );
124
+
125
+ return newRepeatGroupQuestionnaireResponseItem
126
+ ? [newRepeatGroupQuestionnaireResponseItem]
127
+ : null;
137
128
  }
138
129
 
139
- const initialQRItems: QuestionnaireResponseItem[] = [];
140
- for (const childQItem of childQItems) {
141
- const initialChildQRItemOrItems = readItemInitialValueRecursive(childQItem);
142
-
143
- if (Array.isArray(initialChildQRItemOrItems)) {
144
- if (initialChildQRItemOrItems.length > 0) {
145
- initialQRItems.push(...initialChildQRItemOrItems);
146
- }
147
- continue;
130
+ // Map qrItemOrItems into an array of qrItems
131
+ let childQrItems: QuestionnaireResponseItem[] = [];
132
+ if (qrItemOrItems) {
133
+ if (Array.isArray(qrItemOrItems)) {
134
+ childQrItems = qrItemOrItems;
135
+ } else {
136
+ childQrItems = qrItemOrItems.item ?? [];
148
137
  }
138
+ }
139
+
140
+ const indexMap = mapQItemsIndex(qItem);
141
+ const qrItemsByIndex = getQrItemsIndex(childQItems, childQrItems, indexMap);
142
+
143
+ for (const [index, childQItem] of childQItems.entries()) {
144
+ const childQRItemOrItems = qrItemsByIndex[index];
149
145
 
150
- if (initialChildQRItemOrItems) {
151
- initialQRItems.push(initialChildQRItemOrItems);
146
+ const childInitialValues = readInitialValuesRecursive(childQItem, childQRItemOrItems ?? null);
147
+
148
+ if (childInitialValues) {
149
+ initialValues.push(...childInitialValues);
152
150
  }
153
151
  }
152
+ }
154
153
 
155
- let qrItem = createNewQuestionnaireResponseItem(qItem, getInitialValueAnswers(qItem));
154
+ // Create new qrItem for items with initial values
155
+ let qrItem = createNewQuestionnaireResponseItem(qItem, getInitialValueAnswers(qItem));
156
156
 
157
- if (initialQRItems.length > 0) {
158
- if (!qrItem) {
159
- qrItem = {
160
- linkId: qItem.linkId,
161
- text: qItem.text
162
- };
163
- }
164
- qrItem.item = initialQRItems;
157
+ if (initialValues.length > 0) {
158
+ if (!qrItem) {
159
+ qrItem = {
160
+ linkId: qItem.linkId,
161
+ text: qItem.text
162
+ };
165
163
  }
164
+ qrItem.item = initialValues;
165
+ }
166
166
 
167
- return qrItem;
167
+ return qrItem ? [qrItem] : null;
168
+ }
169
+
170
+ function getInitialValueAnswersFromRepeatGroup(
171
+ qItem: QuestionnaireItem,
172
+ qrItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[] | null
173
+ ) {
174
+ if (!qItem.item) {
175
+ return [];
168
176
  }
169
177
 
170
- return createNewQuestionnaireResponseItem(qItem, getInitialValueAnswers(qItem));
178
+ return qItem.item
179
+ .flatMap((childQItem) => readInitialValuesRecursive(childQItem, qrItemOrItems))
180
+ .filter((childQRItem): childQRItem is QuestionnaireResponseItem => !!childQRItem);
171
181
  }
172
182
 
173
183
  function getInitialValueAnswers(qItem: QuestionnaireItem): QuestionnaireResponseItemAnswer[] {
@@ -210,22 +220,6 @@ function getInitialValueAnswers(qItem: QuestionnaireItem): QuestionnaireResponse
210
220
  .filter((item): item is QuestionnaireResponseItemAnswer => item !== null);
211
221
  }
212
222
 
213
- function getInitialValueAnswersFromRepeatGroup(qItem: QuestionnaireItem) {
214
- if (!qItem.item) {
215
- return [];
216
- }
217
-
218
- return qItem.item
219
- .map((childQItem): QuestionnaireResponseItem => {
220
- return {
221
- linkId: childQItem.linkId,
222
- ...(childQItem.text ? { text: childQItem.text } : {}),
223
- answer: getInitialValueAnswers(childQItem)
224
- };
225
- })
226
- .filter((childQRItem) => childQRItem.answer && childQRItem.answer.length > 0);
227
- }
228
-
229
223
  export function parseItemInitialToAnswer(
230
224
  initial: QuestionnaireItemInitial
231
225
  ): QuestionnaireResponseItemAnswer | null {
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import type { Coding, Extension, QuestionnaireItem } from 'fhir/r4';
18
+ import type { Coding, Extension, QuestionnaireItem, QuestionnaireItemAnswerOption } from 'fhir/r4';
19
19
  import type { RegexValidation } from '../interfaces/regex.interface';
20
20
  import { structuredDataCapture } from 'fhir-sdc-helpers';
21
21
 
@@ -234,6 +234,24 @@ export function getTextDisplayPrompt(qItem: QuestionnaireItem): string {
234
234
  return '';
235
235
  }
236
236
 
237
+ /**
238
+ * Get Quantity unit for items with itemControlCode unit and has a unit childItem
239
+ *
240
+ * @author Sean Fong
241
+ */
242
+ export function getQuantityUnit(qItem: QuestionnaireItem): QuestionnaireItemAnswerOption | null {
243
+ // Otherwise, check if the item has a unit extension
244
+ const itemControl = qItem.extension?.find(
245
+ (extension: Extension) =>
246
+ extension.url === 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit'
247
+ );
248
+ if (itemControl && itemControl.valueCoding) {
249
+ return itemControl;
250
+ }
251
+
252
+ return null;
253
+ }
254
+
237
255
  /**
238
256
  * Get decimal text display unit for items with itemControlCode unit and has a unit childItem
239
257
  *
@@ -0,0 +1,62 @@
1
+ /*
2
+ * Copyright 2024 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 {
19
+ Quantity,
20
+ QuestionnaireItemAnswerOption,
21
+ QuestionnaireResponseItemAnswer
22
+ } from 'fhir/r4';
23
+ import { parseDecimalStringToFloat } from './parseInputs';
24
+
25
+ export const quantityComparators: Quantity['comparator'][] = ['<', '<=', '>=', '>'];
26
+
27
+ export function stringIsComparator(str: string | undefined): str is Quantity['comparator'] {
28
+ return str === '<' || str === '<=' || str === '>=' || str === '>';
29
+ }
30
+
31
+ export function createQuantityItemAnswer(
32
+ precision: number | null,
33
+ parsedNewInput: string,
34
+ comparatorInput: Quantity['comparator'] | null,
35
+ unitInput: QuestionnaireItemAnswerOption | null
36
+ ): QuestionnaireResponseItemAnswer[] {
37
+ if (precision) {
38
+ return [
39
+ {
40
+ valueQuantity: {
41
+ value: parseDecimalStringToFloat(parsedNewInput, precision),
42
+ comparator: comparatorInput ?? undefined,
43
+ unit: unitInput?.valueCoding?.display,
44
+ system: unitInput?.valueCoding?.system,
45
+ code: unitInput?.valueCoding?.code
46
+ }
47
+ }
48
+ ];
49
+ }
50
+
51
+ return [
52
+ {
53
+ valueQuantity: {
54
+ value: parseFloat(parsedNewInput),
55
+ comparator: comparatorInput ?? undefined,
56
+ unit: unitInput?.valueCoding?.display,
57
+ system: unitInput?.valueCoding?.system,
58
+ code: unitInput?.valueCoding?.code
59
+ }
60
+ }
61
+ ];
62
+ }
@@ -3,7 +3,7 @@ import type { ItemToRepopulate } from './repopulateItems';
3
3
  import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
4
4
  import { isSpecificItemControl } from './itemControl';
5
5
  import { questionnaireResponseStore, questionnaireStore } from '../stores';
6
- import { updateQuestionnaireResponse } from './updateQr';
6
+ import { updateQuestionnaireResponse } from './genericRecursive';
7
7
 
8
8
  /**
9
9
  * Re-populate checked items in the re-population dialog into the current QuestionnaireResponse
@@ -29,7 +29,7 @@ import type {
29
29
  import * as FHIR from 'fhirclient';
30
30
  import type { FhirResourceString } from '../interfaces/populate.interface';
31
31
  import type { VariableXFhirQuery } from '../interfaces/variables.interface';
32
- import type { ValueSetPromise } from '../interfaces/valueSet.interface';
32
+ import type { ValidateCodeResponse, ValueSetPromise } from '../interfaces/valueSet.interface';
33
33
 
34
34
  const VALID_VALUE_SET_URL_REGEX =
35
35
  /https?:\/\/(www\.)?[-\w@:%.+~#=]{2,256}\.[a-z]{2,4}\b([-@\w:%+.~#?&/=]*ValueSet[-@\w:%+.~#?&/=]*)/;
@@ -65,6 +65,37 @@ export function getValueSetPromise(url: string, terminologyServerUrl: string): P
65
65
  });
66
66
  }
67
67
 
68
+ function validateCodeResponseIsValid(response: any): response is ValidateCodeResponse {
69
+ return (
70
+ response &&
71
+ response.resourceType === 'Parameters' &&
72
+ response.parameter &&
73
+ response.parameter.find((p: any) => p.name === 'code') &&
74
+ response.parameter.find((p: any) => p.name === 'code').valueCode &&
75
+ response.parameter.find((p: any) => p.name === 'system') &&
76
+ response.parameter.find((p: any) => p.name === 'system').valueUri &&
77
+ response.parameter.find((p: any) => p.name === 'display') &&
78
+ response.parameter.find((p: any) => p.name === 'display').valueString
79
+ );
80
+ }
81
+
82
+ export async function validateCodePromise(
83
+ url: string,
84
+ system: string,
85
+ code: string,
86
+ terminologyServerUrl: string
87
+ ): Promise<ValidateCodeResponse | null> {
88
+ const validateCodeResponse = await FHIR.client({ serverUrl: terminologyServerUrl }).request({
89
+ url: `ValueSet/$validate-code?url=${url}&system=${system}&code=${code}`
90
+ });
91
+
92
+ if (validateCodeResponse && validateCodeResponseIsValid(validateCodeResponse)) {
93
+ return validateCodeResponse;
94
+ }
95
+
96
+ return null;
97
+ }
98
+
68
99
  async function addTimeoutToPromise(promise: Promise<any>, timeoutMs: number) {
69
100
  const timeoutPromise = new Promise((_, reject) => {
70
101
  setTimeout(() => {