@aehrc/smart-forms-renderer 0.21.2 → 0.22.1
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/lib/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.js +0 -1
- package/lib/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.js.map +1 -1
- package/lib/components/FormComponents/DecimalItem/DecimalField.js +1 -1
- package/lib/components/FormComponents/DecimalItem/DecimalField.js.map +1 -1
- package/lib/components/FormComponents/DecimalItem/DecimalItem.js +2 -2
- package/lib/components/FormComponents/DecimalItem/DecimalItem.js.map +1 -1
- package/lib/components/FormComponents/IntegerItem/IntegerItem.js +2 -2
- package/lib/components/FormComponents/IntegerItem/IntegerItem.js.map +1 -1
- package/lib/components/FormComponents/StringItem/StringItem.js +2 -2
- package/lib/components/FormComponents/StringItem/StringItem.js.map +1 -1
- package/lib/components/FormComponents/TextItem/TextItem.js +2 -2
- package/lib/components/FormComponents/TextItem/TextItem.js.map +1 -1
- package/lib/components/FormComponents/UrlItem/UrlItem.js +2 -2
- package/lib/components/FormComponents/UrlItem/UrlItem.js.map +1 -1
- package/lib/hooks/useRenderingExtensions.d.ts +0 -4
- package/lib/hooks/useRenderingExtensions.js +3 -6
- package/lib/hooks/useRenderingExtensions.js.map +1 -1
- package/lib/hooks/useValidationFeedback.d.ts +2 -2
- package/lib/hooks/useValidationFeedback.js +37 -6
- package/lib/hooks/useValidationFeedback.js.map +1 -1
- package/lib/stores/questionnaireResponseStore.d.ts +3 -4
- package/lib/stores/questionnaireResponseStore.js +3 -5
- package/lib/stores/questionnaireResponseStore.js.map +1 -1
- package/lib/utils/debounce.d.ts +1 -1
- package/lib/utils/debounce.js +1 -1
- package/lib/utils/itemControl.d.ts +5 -3
- package/lib/utils/itemControl.js +49 -8
- package/lib/utils/itemControl.js.map +1 -1
- package/lib/utils/validateQuestionnaire.d.ts +54 -5
- package/lib/utils/validateQuestionnaire.js +418 -42
- package/lib/utils/validateQuestionnaire.js.map +1 -1
- package/package.json +1 -1
- package/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.tsx +0 -1
- package/src/components/FormComponents/DecimalItem/DecimalField.tsx +1 -0
- package/src/components/FormComponents/DecimalItem/DecimalItem.tsx +2 -3
- package/src/components/FormComponents/IntegerItem/IntegerItem.tsx +2 -3
- package/src/components/FormComponents/StringItem/StringItem.tsx +2 -3
- package/src/components/FormComponents/TextItem/TextItem.tsx +2 -3
- package/src/components/FormComponents/UrlItem/UrlItem.tsx +2 -3
- package/src/hooks/useRenderingExtensions.ts +1 -9
- package/src/hooks/useValidationFeedback.ts +46 -12
- package/src/stores/questionnaireResponseStore.ts +5 -9
- package/src/utils/debounce.ts +1 -1
- package/src/utils/itemControl.ts +64 -9
- package/src/utils/validateQuestionnaire.ts +535 -58
|
@@ -1,11 +1,50 @@
|
|
|
1
|
-
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
1
|
+
import type { OperationOutcome, Questionnaire, QuestionnaireItem, QuestionnaireResponse } from 'fhir/r4';
|
|
2
2
|
import type { EnableWhenExpressions, EnableWhenItems } from '../interfaces';
|
|
3
3
|
import type { RegexValidation } from '../interfaces/regex.interface';
|
|
4
|
-
export
|
|
4
|
+
export declare enum ValidationResult {
|
|
5
|
+
unknown = "unknown",
|
|
6
|
+
questionnaireNotFound = "questionnaireNotFound",
|
|
7
|
+
questionnaireInactive = "questionnaireInactive",
|
|
8
|
+
questionnaireDraft = "questionnaireDraft",
|
|
9
|
+
questionnaireRetired = "questionnaireRetired",
|
|
10
|
+
invalidLinkId = "invalidLinkId",
|
|
11
|
+
invalidType = "invalidType",
|
|
12
|
+
invalidAnswerType = "invalidAnswerType",
|
|
13
|
+
invalidAnswerOption = "invalidAnswerOption",
|
|
14
|
+
exclusiveAnswerOption = "exclusiveAnswerOption",
|
|
15
|
+
invalidUrlValue = "invalidUrlValue",
|
|
16
|
+
groupShouldNotHaveAnswers = "groupShouldNotHaveAnswers",
|
|
17
|
+
required = "required",
|
|
18
|
+
invariant = "invariant",
|
|
19
|
+
invariantExecution = "invariantExecution",
|
|
20
|
+
repeats = "repeats",
|
|
21
|
+
minCount = "minCount",
|
|
22
|
+
maxCount = "maxCount",
|
|
23
|
+
minValue = "minValue",
|
|
24
|
+
maxValue = "maxValue",
|
|
25
|
+
maxDecimalPlaces = "maxDecimalPlaces",
|
|
26
|
+
minLength = "minLength",
|
|
27
|
+
maxLength = "maxLength",
|
|
28
|
+
invalidNewLine = "invalidNewLine",
|
|
29
|
+
invalidCoding = "invalidCoding",
|
|
30
|
+
tsError = "tsError",
|
|
31
|
+
maxAttachmentSize = "maxAttachmentSize",
|
|
32
|
+
attachmentSizeInconsistent = "attachmentSizeInconsistent",
|
|
33
|
+
invalidAttachmentType = "invalidAttachmentType",
|
|
34
|
+
displayAnswer = "displayAnswer",
|
|
35
|
+
regex = "regex",
|
|
36
|
+
regexTimeout = "regexTimeout",
|
|
37
|
+
invalidRefValue = "invalidRefValue",
|
|
38
|
+
invalidRefResourceType = "invalidRefResourceType",
|
|
39
|
+
invalidRefResourceTypeRestriction = "invalidRefResourceTypeRestriction",
|
|
40
|
+
minValueIncompatUnits = "minValueIncompatUnits",
|
|
41
|
+
maxValueIncompatUnits = "maxValueIncompatUnits",
|
|
42
|
+
invalidUnit = "invalidUnit",
|
|
43
|
+
invalidUnitValueSet = "invalidUnitValueSet"
|
|
44
|
+
}
|
|
5
45
|
interface ValidateQuestionnaireParams {
|
|
6
46
|
questionnaire: Questionnaire;
|
|
7
47
|
questionnaireResponse: QuestionnaireResponse;
|
|
8
|
-
invalidItems: Record<string, InvalidType>;
|
|
9
48
|
enableWhenIsActivated: boolean;
|
|
10
49
|
enableWhenItems: EnableWhenItems;
|
|
11
50
|
enableWhenExpressions: EnableWhenExpressions;
|
|
@@ -16,6 +55,16 @@ interface ValidateQuestionnaireParams {
|
|
|
16
55
|
*
|
|
17
56
|
* @author Sean Fong
|
|
18
57
|
*/
|
|
19
|
-
export declare function validateQuestionnaire(params: ValidateQuestionnaireParams): Record<string,
|
|
20
|
-
|
|
58
|
+
export declare function validateQuestionnaire(params: ValidateQuestionnaireParams): Record<string, OperationOutcome>;
|
|
59
|
+
interface GetInputInvalidTypeParams {
|
|
60
|
+
qItem: QuestionnaireItem;
|
|
61
|
+
input: string;
|
|
62
|
+
regexValidation?: RegexValidation;
|
|
63
|
+
minLength?: number;
|
|
64
|
+
maxLength?: number;
|
|
65
|
+
maxDecimalPlaces?: number;
|
|
66
|
+
minValue?: string | number;
|
|
67
|
+
maxValue?: string | number;
|
|
68
|
+
}
|
|
69
|
+
export declare function getInputInvalidType(getInputInvalidTypeParams: GetInputInvalidTypeParams): ValidationResult | null;
|
|
21
70
|
export {};
|
|
@@ -16,8 +16,52 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { getQrItemsIndex, mapQItemsIndex } from './mapItem';
|
|
18
18
|
import { isHiddenByEnableWhen } from './qItem';
|
|
19
|
-
import { getRegexValidation } from './itemControl';
|
|
19
|
+
import { getDecimalPrecision, getMaxValue, getMinValue, getRegexString, getRegexValidation, getShortText } from './itemControl';
|
|
20
20
|
import { structuredDataCapture } from 'fhir-sdc-helpers';
|
|
21
|
+
import { parseDecimalStringToFloat } from './parseInputs';
|
|
22
|
+
import dayjs from 'dayjs';
|
|
23
|
+
export var ValidationResult;
|
|
24
|
+
(function (ValidationResult) {
|
|
25
|
+
ValidationResult["unknown"] = "unknown";
|
|
26
|
+
ValidationResult["questionnaireNotFound"] = "questionnaireNotFound";
|
|
27
|
+
ValidationResult["questionnaireInactive"] = "questionnaireInactive";
|
|
28
|
+
ValidationResult["questionnaireDraft"] = "questionnaireDraft";
|
|
29
|
+
ValidationResult["questionnaireRetired"] = "questionnaireRetired";
|
|
30
|
+
ValidationResult["invalidLinkId"] = "invalidLinkId";
|
|
31
|
+
ValidationResult["invalidType"] = "invalidType";
|
|
32
|
+
ValidationResult["invalidAnswerType"] = "invalidAnswerType";
|
|
33
|
+
ValidationResult["invalidAnswerOption"] = "invalidAnswerOption";
|
|
34
|
+
ValidationResult["exclusiveAnswerOption"] = "exclusiveAnswerOption";
|
|
35
|
+
ValidationResult["invalidUrlValue"] = "invalidUrlValue";
|
|
36
|
+
ValidationResult["groupShouldNotHaveAnswers"] = "groupShouldNotHaveAnswers";
|
|
37
|
+
ValidationResult["required"] = "required";
|
|
38
|
+
ValidationResult["invariant"] = "invariant";
|
|
39
|
+
ValidationResult["invariantExecution"] = "invariantExecution";
|
|
40
|
+
ValidationResult["repeats"] = "repeats";
|
|
41
|
+
ValidationResult["minCount"] = "minCount";
|
|
42
|
+
ValidationResult["maxCount"] = "maxCount";
|
|
43
|
+
ValidationResult["minValue"] = "minValue";
|
|
44
|
+
ValidationResult["maxValue"] = "maxValue";
|
|
45
|
+
ValidationResult["maxDecimalPlaces"] = "maxDecimalPlaces";
|
|
46
|
+
ValidationResult["minLength"] = "minLength";
|
|
47
|
+
ValidationResult["maxLength"] = "maxLength";
|
|
48
|
+
ValidationResult["invalidNewLine"] = "invalidNewLine";
|
|
49
|
+
ValidationResult["invalidCoding"] = "invalidCoding";
|
|
50
|
+
ValidationResult["tsError"] = "tsError";
|
|
51
|
+
ValidationResult["maxAttachmentSize"] = "maxAttachmentSize";
|
|
52
|
+
ValidationResult["attachmentSizeInconsistent"] = "attachmentSizeInconsistent";
|
|
53
|
+
ValidationResult["invalidAttachmentType"] = "invalidAttachmentType";
|
|
54
|
+
ValidationResult["displayAnswer"] = "displayAnswer";
|
|
55
|
+
ValidationResult["regex"] = "regex";
|
|
56
|
+
ValidationResult["regexTimeout"] = "regexTimeout";
|
|
57
|
+
ValidationResult["invalidRefValue"] = "invalidRefValue";
|
|
58
|
+
ValidationResult["invalidRefResourceType"] = "invalidRefResourceType";
|
|
59
|
+
ValidationResult["invalidRefResourceTypeRestriction"] = "invalidRefResourceTypeRestriction";
|
|
60
|
+
ValidationResult["minValueIncompatUnits"] = "minValueIncompatUnits";
|
|
61
|
+
ValidationResult["maxValueIncompatUnits"] = "maxValueIncompatUnits";
|
|
62
|
+
ValidationResult["invalidUnit"] = "invalidUnit";
|
|
63
|
+
ValidationResult["invalidUnitValueSet"] = "invalidUnitValueSet"; // The unit provided was not in the provided valueset
|
|
64
|
+
})(ValidationResult || (ValidationResult = {}));
|
|
21
65
|
/**
|
|
22
66
|
* Recursively go through the questionnaireResponse and check for un-filled required qItems
|
|
23
67
|
* At the moment item.required for group items are not checked
|
|
@@ -26,41 +70,61 @@ import { structuredDataCapture } from 'fhir-sdc-helpers';
|
|
|
26
70
|
*/
|
|
27
71
|
export function validateQuestionnaire(params) {
|
|
28
72
|
var _a;
|
|
29
|
-
const { questionnaire, questionnaireResponse,
|
|
73
|
+
const { questionnaire, questionnaireResponse, enableWhenIsActivated, enableWhenItems, enableWhenExpressions } = params;
|
|
30
74
|
if (!questionnaire.item ||
|
|
31
75
|
questionnaire.item.length === 0 ||
|
|
32
76
|
!questionnaireResponse.item ||
|
|
33
77
|
questionnaireResponse.item.length === 0) {
|
|
34
|
-
return
|
|
78
|
+
return {};
|
|
35
79
|
}
|
|
36
80
|
const qItemsIndexMap = mapQItemsIndex(questionnaire);
|
|
37
81
|
const topLevelQRItemsByIndex = getQrItemsIndex(questionnaire.item, questionnaireResponse.item, qItemsIndexMap);
|
|
82
|
+
const invalidItems = {};
|
|
83
|
+
let qrItemIndex = 0;
|
|
38
84
|
for (const [index, topLevelQItem] of questionnaire.item.entries()) {
|
|
85
|
+
let repeatGroupInstances = null;
|
|
39
86
|
let topLevelQRItem = (_a = topLevelQRItemsByIndex[index]) !== null && _a !== void 0 ? _a : {
|
|
40
87
|
linkId: topLevelQItem.linkId,
|
|
41
88
|
text: topLevelQItem.text
|
|
42
89
|
};
|
|
90
|
+
// topLevelQRItem being an array means this item is a repeat group
|
|
91
|
+
const isRepeatGroup = Array.isArray(topLevelQRItem);
|
|
43
92
|
if (Array.isArray(topLevelQRItem)) {
|
|
93
|
+
repeatGroupInstances = topLevelQRItem.length;
|
|
44
94
|
topLevelQRItem = {
|
|
45
95
|
linkId: topLevelQItem.linkId,
|
|
46
96
|
text: topLevelQItem.text,
|
|
47
97
|
item: topLevelQRItem
|
|
48
98
|
};
|
|
49
99
|
}
|
|
100
|
+
const locationExpression = `QuestionnaireResponse.item`;
|
|
50
101
|
validateItemRecursive({
|
|
51
102
|
qItem: topLevelQItem,
|
|
52
103
|
qrItem: topLevelQRItem,
|
|
53
|
-
|
|
104
|
+
qrItemIndex,
|
|
105
|
+
locationExpression,
|
|
106
|
+
invalidItems,
|
|
54
107
|
enableWhenIsActivated,
|
|
55
108
|
enableWhenItems,
|
|
56
|
-
enableWhenExpressions
|
|
109
|
+
enableWhenExpressions,
|
|
110
|
+
isRepeatGroupInstance: false
|
|
57
111
|
});
|
|
112
|
+
// Increment qrItemIndex
|
|
113
|
+
// If it's a repeat group, increment by the number of instances so qrItemIndex is correct once we reach the next item
|
|
114
|
+
if (isRepeatGroup && typeof repeatGroupInstances === 'number') {
|
|
115
|
+
qrItemIndex += repeatGroupInstances;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
qrItemIndex++;
|
|
119
|
+
}
|
|
58
120
|
}
|
|
59
121
|
return invalidItems;
|
|
60
122
|
}
|
|
61
123
|
function validateItemRecursive(params) {
|
|
62
|
-
var _a, _b;
|
|
63
|
-
const { qItem, qrItem, invalidItems, enableWhenIsActivated, enableWhenItems, enableWhenExpressions } = params;
|
|
124
|
+
var _a, _b, _c;
|
|
125
|
+
const { qItem, qrItem, qrItemIndex, invalidItems, enableWhenIsActivated, enableWhenItems, enableWhenExpressions, isRepeatGroupInstance } = params;
|
|
126
|
+
let { locationExpression } = params;
|
|
127
|
+
// If item is hidden by enableWhen, skip validation
|
|
64
128
|
if (isHiddenByEnableWhen({
|
|
65
129
|
linkId: qItem.linkId,
|
|
66
130
|
enableWhenIsActivated,
|
|
@@ -69,22 +133,40 @@ function validateItemRecursive(params) {
|
|
|
69
133
|
})) {
|
|
70
134
|
return;
|
|
71
135
|
}
|
|
72
|
-
//
|
|
136
|
+
// Validate repeat groups
|
|
73
137
|
if (qItem.type === 'group' && qItem.repeats) {
|
|
74
|
-
|
|
138
|
+
if (!isRepeatGroupInstance) {
|
|
139
|
+
validateRepeatGroupRecursive({
|
|
140
|
+
qItem,
|
|
141
|
+
qrItem,
|
|
142
|
+
qrItemIndex,
|
|
143
|
+
locationExpression,
|
|
144
|
+
invalidItems,
|
|
145
|
+
enableWhenIsActivated,
|
|
146
|
+
enableWhenItems,
|
|
147
|
+
enableWhenExpressions,
|
|
148
|
+
isRepeatGroupInstance: false
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
75
152
|
}
|
|
153
|
+
locationExpression = `${locationExpression}[${qrItemIndex}]`;
|
|
154
|
+
// Recursively validate groups with child items
|
|
76
155
|
const childQItems = qItem.item;
|
|
77
156
|
if (childQItems && childQItems.length > 0) {
|
|
78
157
|
const childQrItems = (_a = qrItem === null || qrItem === void 0 ? void 0 : qrItem.item) !== null && _a !== void 0 ? _a : [];
|
|
79
158
|
const indexMap = mapQItemsIndex(qItem);
|
|
80
159
|
const qrItemsByIndex = getQrItemsIndex(childQItems, childQrItems, indexMap);
|
|
160
|
+
// Check if group is required and has no answers
|
|
81
161
|
if (qItem.type === 'group' && qItem.required) {
|
|
82
162
|
if (!qrItem || qrItemsByIndex.length === 0) {
|
|
83
|
-
invalidItems[qItem.linkId] =
|
|
163
|
+
invalidItems[qItem.linkId] = createValidationOperationOutcome(ValidationResult.required, qItem, qrItem, null, locationExpression, (_b = invalidItems[qItem.linkId]) === null || _b === void 0 ? void 0 : _b.issue);
|
|
84
164
|
}
|
|
85
165
|
}
|
|
166
|
+
// Loop through child items
|
|
86
167
|
for (const [index, childQItem] of childQItems.entries()) {
|
|
87
|
-
|
|
168
|
+
const childLocationExpression = `${locationExpression}.item`;
|
|
169
|
+
let childQRItem = (_c = qrItemsByIndex[index]) !== null && _c !== void 0 ? _c : {
|
|
88
170
|
linkId: childQItem.linkId,
|
|
89
171
|
text: childQItem.text
|
|
90
172
|
};
|
|
@@ -98,56 +180,79 @@ function validateItemRecursive(params) {
|
|
|
98
180
|
validateItemRecursive({
|
|
99
181
|
qItem: childQItem,
|
|
100
182
|
qrItem: childQRItem,
|
|
183
|
+
qrItemIndex: index,
|
|
184
|
+
locationExpression: childLocationExpression,
|
|
101
185
|
invalidItems: invalidItems,
|
|
102
186
|
enableWhenIsActivated,
|
|
103
187
|
enableWhenItems,
|
|
104
|
-
enableWhenExpressions
|
|
188
|
+
enableWhenExpressions,
|
|
189
|
+
isRepeatGroupInstance: false
|
|
105
190
|
});
|
|
106
191
|
}
|
|
107
192
|
}
|
|
108
|
-
|
|
193
|
+
// Validate the item, note that this can be either a group or a non-group
|
|
194
|
+
validateSingleItem(qItem, qrItem, invalidItems, locationExpression);
|
|
109
195
|
}
|
|
110
|
-
function
|
|
196
|
+
function validateRepeatGroupRecursive(params) {
|
|
197
|
+
var _a;
|
|
198
|
+
const { qItem, qrItem, qrItemIndex, locationExpression, invalidItems, enableWhenIsActivated, enableWhenItems, enableWhenExpressions } = params;
|
|
199
|
+
if (!qItem.item || qItem.item.length === 0 || !qrItem.item || qrItem.item.length === 0) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
// Get repeat group answers
|
|
203
|
+
const repeatGroupAnswers = (_a = qrItem.item) !== null && _a !== void 0 ? _a : [];
|
|
204
|
+
for (const [index, repeatGroupAnswer] of repeatGroupAnswers.entries()) {
|
|
205
|
+
// Because the item is a repeat group and might have multiple answer instances, we need to increment the qItemIndex by the instanceIndex
|
|
206
|
+
const updatedQrItemIndex = qrItemIndex + index;
|
|
207
|
+
validateItemRecursive({
|
|
208
|
+
qItem: qItem,
|
|
209
|
+
qrItem: repeatGroupAnswer,
|
|
210
|
+
qrItemIndex: updatedQrItemIndex,
|
|
211
|
+
locationExpression: locationExpression,
|
|
212
|
+
invalidItems: invalidItems,
|
|
213
|
+
enableWhenIsActivated,
|
|
214
|
+
enableWhenItems,
|
|
215
|
+
enableWhenExpressions,
|
|
216
|
+
isRepeatGroupInstance: true
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function validateSingleItem(qItem, qrItem, invalidItems, locationExpression) {
|
|
111
221
|
var _a, _b;
|
|
112
|
-
// Validate item.required
|
|
222
|
+
// Validate item.required first before every other validation check
|
|
113
223
|
if (qItem.type !== 'display') {
|
|
114
224
|
if (qItem.required && !qrItem.answer) {
|
|
115
|
-
invalidItems[qItem.linkId] =
|
|
225
|
+
invalidItems[qItem.linkId] = createValidationOperationOutcome(ValidationResult.required, qItem, qrItem, null, locationExpression, (_a = invalidItems[qItem.linkId]) === null || _a === void 0 ? void 0 : _a.issue);
|
|
116
226
|
return invalidItems;
|
|
117
227
|
}
|
|
118
228
|
}
|
|
119
229
|
// Validate regex, maxLength and minLength
|
|
120
230
|
if (qrItem.answer) {
|
|
121
|
-
for (const answer of qrItem.answer) {
|
|
231
|
+
for (const [i, answer] of qrItem.answer.entries()) {
|
|
232
|
+
// Your code here, you can use 'index' and 'answer' as needed
|
|
122
233
|
if (answer.valueString || answer.valueInteger || answer.valueDecimal || answer.valueUri) {
|
|
123
|
-
const invalidInputType = getInputInvalidType(
|
|
124
|
-
|
|
125
|
-
|
|
234
|
+
const invalidInputType = getInputInvalidType({
|
|
235
|
+
qItem,
|
|
236
|
+
input: getInputInString(answer),
|
|
237
|
+
regexValidation: getRegexValidation(qItem),
|
|
238
|
+
minLength: structuredDataCapture.getMinLength(qItem),
|
|
239
|
+
maxLength: qItem.maxLength,
|
|
240
|
+
maxDecimalPlaces: structuredDataCapture.getMaxDecimalPlaces(qItem),
|
|
241
|
+
minValue: getMinValue(qItem),
|
|
242
|
+
maxValue: getMaxValue(qItem)
|
|
243
|
+
});
|
|
244
|
+
if (invalidInputType) {
|
|
245
|
+
invalidItems[qItem.linkId] = createValidationOperationOutcome(invalidInputType, qItem, qrItem, i, locationExpression, (_b = invalidItems[qItem.linkId]) === null || _b === void 0 ? void 0 : _b.issue);
|
|
126
246
|
}
|
|
127
|
-
// Assign invalid type and break - stop checking other answers if is a repeat item
|
|
128
|
-
switch (invalidInputType) {
|
|
129
|
-
case 'regex':
|
|
130
|
-
invalidItems[qItem.linkId] = 'regex';
|
|
131
|
-
break;
|
|
132
|
-
case 'minLength':
|
|
133
|
-
invalidItems[qItem.linkId] = 'minLength';
|
|
134
|
-
break;
|
|
135
|
-
case 'maxLength':
|
|
136
|
-
invalidItems[qItem.linkId] = 'maxLength';
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
247
|
}
|
|
141
248
|
}
|
|
142
|
-
// Reached the end of the loop and no invalid input type found
|
|
143
|
-
// If a required item is filled, remove the required invalid type
|
|
144
|
-
if (qItem.required && invalidItems[qItem.linkId] && invalidItems[qItem.linkId] === 'required') {
|
|
145
|
-
delete invalidItems[qItem.linkId];
|
|
146
|
-
}
|
|
147
249
|
}
|
|
148
250
|
return invalidItems;
|
|
149
251
|
}
|
|
150
252
|
function getInputInString(answer) {
|
|
253
|
+
if (!answer) {
|
|
254
|
+
return '';
|
|
255
|
+
}
|
|
151
256
|
if (answer.valueString) {
|
|
152
257
|
return answer.valueString;
|
|
153
258
|
}
|
|
@@ -162,18 +267,289 @@ function getInputInString(answer) {
|
|
|
162
267
|
}
|
|
163
268
|
return '';
|
|
164
269
|
}
|
|
165
|
-
export function getInputInvalidType(
|
|
270
|
+
export function getInputInvalidType(getInputInvalidTypeParams) {
|
|
271
|
+
var _a, _b;
|
|
272
|
+
const { qItem, input, regexValidation, minLength, maxLength, maxDecimalPlaces, minValue, maxValue } = getInputInvalidTypeParams;
|
|
166
273
|
if (input) {
|
|
167
274
|
if (regexValidation && !regexValidation.expression.test(input)) {
|
|
168
|
-
return
|
|
275
|
+
return ValidationResult.regex;
|
|
169
276
|
}
|
|
170
277
|
if (minLength && input.length < minLength) {
|
|
171
|
-
return
|
|
278
|
+
return ValidationResult.minLength;
|
|
172
279
|
}
|
|
173
280
|
if (maxLength && input.length > maxLength) {
|
|
174
|
-
return
|
|
281
|
+
return ValidationResult.maxLength;
|
|
282
|
+
}
|
|
283
|
+
if (maxDecimalPlaces) {
|
|
284
|
+
const decimalPlaces = (_b = (_a = input.split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
|
|
285
|
+
if (decimalPlaces > maxDecimalPlaces) {
|
|
286
|
+
return ValidationResult.maxDecimalPlaces;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (minValue) {
|
|
290
|
+
const minValueError = checkMinValue(qItem, input, minValue);
|
|
291
|
+
if (minValueError !== null) {
|
|
292
|
+
return ValidationResult.minValue;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (maxValue) {
|
|
296
|
+
const maxValueError = checkMaxValue(qItem, input, maxValue);
|
|
297
|
+
if (maxValueError !== null) {
|
|
298
|
+
return ValidationResult.maxValue;
|
|
299
|
+
}
|
|
175
300
|
}
|
|
176
301
|
}
|
|
177
302
|
return null;
|
|
178
303
|
}
|
|
304
|
+
function checkMinValue(qItem, input, minValue) {
|
|
305
|
+
switch (qItem.type) {
|
|
306
|
+
case 'integer':
|
|
307
|
+
if (typeof minValue === 'number') {
|
|
308
|
+
if (parseInt(input) < minValue) {
|
|
309
|
+
return ValidationResult.minValue;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
break;
|
|
313
|
+
case 'decimal': {
|
|
314
|
+
const precision = getDecimalPrecision(qItem);
|
|
315
|
+
const decimalValue = precision
|
|
316
|
+
? parseDecimalStringToFloat(input, precision)
|
|
317
|
+
: parseFloat(input);
|
|
318
|
+
if (typeof minValue === 'number') {
|
|
319
|
+
if (decimalValue < minValue) {
|
|
320
|
+
return ValidationResult.minValue;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case 'date':
|
|
326
|
+
if (typeof minValue === 'string') {
|
|
327
|
+
if (new Date(input) < new Date(minValue)) {
|
|
328
|
+
return ValidationResult.minValue;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
case 'dateTime':
|
|
333
|
+
if (typeof minValue === 'string') {
|
|
334
|
+
if (dayjs(input).isBefore(dayjs(minValue))) {
|
|
335
|
+
return ValidationResult.minValue;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
default:
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
function checkMaxValue(qItem, input, maxValue) {
|
|
345
|
+
switch (qItem.type) {
|
|
346
|
+
case 'integer':
|
|
347
|
+
if (typeof maxValue === 'number') {
|
|
348
|
+
if (parseInt(input) > maxValue) {
|
|
349
|
+
return ValidationResult.maxValue;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
case 'decimal': {
|
|
354
|
+
const precision = getDecimalPrecision(qItem);
|
|
355
|
+
const decimalValue = precision
|
|
356
|
+
? parseDecimalStringToFloat(input, precision)
|
|
357
|
+
: parseFloat(input);
|
|
358
|
+
if (typeof maxValue === 'number') {
|
|
359
|
+
if (decimalValue > maxValue) {
|
|
360
|
+
return ValidationResult.maxValue;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
case 'date':
|
|
366
|
+
if (typeof maxValue === 'string') {
|
|
367
|
+
if (new Date(input) > new Date(maxValue)) {
|
|
368
|
+
return ValidationResult.maxValue;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
case 'dateTime':
|
|
373
|
+
if (typeof maxValue === 'string') {
|
|
374
|
+
if (dayjs(input).isAfter(dayjs(maxValue))) {
|
|
375
|
+
return ValidationResult.maxValue;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
default:
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
function createValidationOperationOutcome(error, qItem, qrItem, answerIndex, locationExpression, existingOperationOutcomeIssues = []) {
|
|
385
|
+
return {
|
|
386
|
+
resourceType: 'OperationOutcome',
|
|
387
|
+
issue: existingOperationOutcomeIssues.concat(createValidationOperationOutcomeIssue(error, qItem, qrItem, answerIndex, locationExpression))
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function createValidationOperationOutcomeIssue(error, qItem, qrItem, answerIndex, locationExpression) {
|
|
391
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
392
|
+
const errorCodeSystem = 'http://fhir.forms-lab.com/CodeSystem/errors';
|
|
393
|
+
let detailsText = '';
|
|
394
|
+
let fieldDisplayText = (_d = (_c = (_b = (_a = qrItem === null || qrItem === void 0 ? void 0 : qrItem.text) !== null && _a !== void 0 ? _a : getShortText(qItem)) !== null && _b !== void 0 ? _b : qItem === null || qItem === void 0 ? void 0 : qItem.text) !== null && _c !== void 0 ? _c : qItem.linkId) !== null && _d !== void 0 ? _d : qrItem.linkId;
|
|
395
|
+
if (!fieldDisplayText && fieldDisplayText.endsWith(':')) {
|
|
396
|
+
fieldDisplayText = fieldDisplayText.substring(0, fieldDisplayText.length - 1);
|
|
397
|
+
}
|
|
398
|
+
answerIndex = answerIndex !== null && answerIndex !== void 0 ? answerIndex : 0;
|
|
399
|
+
// create operationOutcomeIssue based on error
|
|
400
|
+
switch (error) {
|
|
401
|
+
case ValidationResult.required: {
|
|
402
|
+
if (qItem.type === 'group') {
|
|
403
|
+
detailsText = `${fieldDisplayText}: Mandatory group does not have answer(s)`;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
detailsText = `${fieldDisplayText}: Mandatory field does not have an answer`;
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
severity: 'error',
|
|
410
|
+
code: 'required',
|
|
411
|
+
expression: [locationExpression],
|
|
412
|
+
details: {
|
|
413
|
+
coding: [
|
|
414
|
+
{
|
|
415
|
+
system: errorCodeSystem,
|
|
416
|
+
code: error,
|
|
417
|
+
display: 'Required'
|
|
418
|
+
}
|
|
419
|
+
],
|
|
420
|
+
text: detailsText
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
case ValidationResult.regex: {
|
|
425
|
+
detailsText = `${fieldDisplayText}: The value '${getInputInString((_e = qrItem.answer) === null || _e === void 0 ? void 0 : _e[answerIndex])}' does not match the defined format.`;
|
|
426
|
+
if (structuredDataCapture.getEntryFormat(qItem)) {
|
|
427
|
+
detailsText += ` ${structuredDataCapture.getEntryFormat(qItem)}`;
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
severity: 'error',
|
|
431
|
+
code: 'invalid',
|
|
432
|
+
expression: [locationExpression],
|
|
433
|
+
details: {
|
|
434
|
+
coding: [
|
|
435
|
+
{
|
|
436
|
+
system: errorCodeSystem,
|
|
437
|
+
code: error,
|
|
438
|
+
display: 'Invalid format'
|
|
439
|
+
}
|
|
440
|
+
],
|
|
441
|
+
text: detailsText
|
|
442
|
+
},
|
|
443
|
+
diagnostics: (_f = getRegexString(qItem)) !== null && _f !== void 0 ? _f : undefined
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
case ValidationResult.minLength: {
|
|
447
|
+
detailsText = `${fieldDisplayText}: Expected the minimum value ${structuredDataCapture.getMinLength(qItem)} characters, received '${getInputInString((_g = qrItem.answer) === null || _g === void 0 ? void 0 : _g[answerIndex])}'`;
|
|
448
|
+
return {
|
|
449
|
+
severity: 'error',
|
|
450
|
+
code: 'business-rule',
|
|
451
|
+
expression: [locationExpression],
|
|
452
|
+
details: {
|
|
453
|
+
coding: [
|
|
454
|
+
{
|
|
455
|
+
system: errorCodeSystem,
|
|
456
|
+
code: error,
|
|
457
|
+
display: 'Too short'
|
|
458
|
+
}
|
|
459
|
+
],
|
|
460
|
+
text: detailsText
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
case ValidationResult.maxLength: {
|
|
465
|
+
detailsText = `${fieldDisplayText}: Exceeded maximum of ${qItem.maxLength} characters, received '${getInputInString((_h = qrItem.answer) === null || _h === void 0 ? void 0 : _h[answerIndex])}'`;
|
|
466
|
+
return {
|
|
467
|
+
severity: 'error',
|
|
468
|
+
code: 'business-rule',
|
|
469
|
+
expression: [locationExpression],
|
|
470
|
+
details: {
|
|
471
|
+
coding: [
|
|
472
|
+
{
|
|
473
|
+
system: errorCodeSystem,
|
|
474
|
+
code: error,
|
|
475
|
+
display: 'Too long'
|
|
476
|
+
}
|
|
477
|
+
],
|
|
478
|
+
text: detailsText
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
case ValidationResult.maxDecimalPlaces: {
|
|
483
|
+
detailsText = `${fieldDisplayText}: Exceeded maximum decimal places ${structuredDataCapture.getMaxDecimalPlaces(qItem)}, received '${getInputInString((_j = qrItem.answer) === null || _j === void 0 ? void 0 : _j[answerIndex])}'`;
|
|
484
|
+
return {
|
|
485
|
+
severity: 'error',
|
|
486
|
+
code: 'business-rule',
|
|
487
|
+
expression: [locationExpression],
|
|
488
|
+
details: {
|
|
489
|
+
coding: [
|
|
490
|
+
{
|
|
491
|
+
system: errorCodeSystem,
|
|
492
|
+
code: error,
|
|
493
|
+
display: 'Too precise'
|
|
494
|
+
}
|
|
495
|
+
],
|
|
496
|
+
text: detailsText
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
case ValidationResult.minValue: {
|
|
501
|
+
detailsText = `${fieldDisplayText}: Expected the minimum value ${getMinValue(qItem)}, received '${getInputInString((_k = qrItem.answer) === null || _k === void 0 ? void 0 : _k[answerIndex])}'`;
|
|
502
|
+
return {
|
|
503
|
+
severity: 'error',
|
|
504
|
+
code: 'business-rule',
|
|
505
|
+
expression: [locationExpression],
|
|
506
|
+
details: {
|
|
507
|
+
coding: [
|
|
508
|
+
{
|
|
509
|
+
system: errorCodeSystem,
|
|
510
|
+
code: error,
|
|
511
|
+
display: 'Too small'
|
|
512
|
+
}
|
|
513
|
+
],
|
|
514
|
+
text: detailsText
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
case ValidationResult.maxValue: {
|
|
519
|
+
detailsText = `${fieldDisplayText}: Exceeded the maximum value ${getMaxValue(qItem)}, received '${getInputInString((_l = qrItem.answer) === null || _l === void 0 ? void 0 : _l[answerIndex])}'`;
|
|
520
|
+
return {
|
|
521
|
+
severity: 'error',
|
|
522
|
+
code: 'business-rule',
|
|
523
|
+
expression: [locationExpression],
|
|
524
|
+
details: {
|
|
525
|
+
coding: [
|
|
526
|
+
{
|
|
527
|
+
system: errorCodeSystem,
|
|
528
|
+
code: error,
|
|
529
|
+
display: 'Too big'
|
|
530
|
+
}
|
|
531
|
+
],
|
|
532
|
+
text: detailsText
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
// mark unknown issues as fatal
|
|
537
|
+
default: {
|
|
538
|
+
return {
|
|
539
|
+
severity: 'error',
|
|
540
|
+
code: 'unknown',
|
|
541
|
+
expression: [locationExpression],
|
|
542
|
+
details: {
|
|
543
|
+
coding: [
|
|
544
|
+
{
|
|
545
|
+
system: errorCodeSystem,
|
|
546
|
+
code: 'unknown',
|
|
547
|
+
display: 'Unknown'
|
|
548
|
+
}
|
|
549
|
+
]
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
179
555
|
//# sourceMappingURL=validateQuestionnaire.js.map
|