@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.
Files changed (45) hide show
  1. package/lib/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.js +0 -1
  2. package/lib/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.js.map +1 -1
  3. package/lib/components/FormComponents/DecimalItem/DecimalField.js +1 -1
  4. package/lib/components/FormComponents/DecimalItem/DecimalField.js.map +1 -1
  5. package/lib/components/FormComponents/DecimalItem/DecimalItem.js +2 -2
  6. package/lib/components/FormComponents/DecimalItem/DecimalItem.js.map +1 -1
  7. package/lib/components/FormComponents/IntegerItem/IntegerItem.js +2 -2
  8. package/lib/components/FormComponents/IntegerItem/IntegerItem.js.map +1 -1
  9. package/lib/components/FormComponents/StringItem/StringItem.js +2 -2
  10. package/lib/components/FormComponents/StringItem/StringItem.js.map +1 -1
  11. package/lib/components/FormComponents/TextItem/TextItem.js +2 -2
  12. package/lib/components/FormComponents/TextItem/TextItem.js.map +1 -1
  13. package/lib/components/FormComponents/UrlItem/UrlItem.js +2 -2
  14. package/lib/components/FormComponents/UrlItem/UrlItem.js.map +1 -1
  15. package/lib/hooks/useRenderingExtensions.d.ts +0 -4
  16. package/lib/hooks/useRenderingExtensions.js +3 -6
  17. package/lib/hooks/useRenderingExtensions.js.map +1 -1
  18. package/lib/hooks/useValidationFeedback.d.ts +2 -2
  19. package/lib/hooks/useValidationFeedback.js +37 -6
  20. package/lib/hooks/useValidationFeedback.js.map +1 -1
  21. package/lib/stores/questionnaireResponseStore.d.ts +3 -4
  22. package/lib/stores/questionnaireResponseStore.js +3 -5
  23. package/lib/stores/questionnaireResponseStore.js.map +1 -1
  24. package/lib/utils/debounce.d.ts +1 -1
  25. package/lib/utils/debounce.js +1 -1
  26. package/lib/utils/itemControl.d.ts +5 -3
  27. package/lib/utils/itemControl.js +49 -8
  28. package/lib/utils/itemControl.js.map +1 -1
  29. package/lib/utils/validateQuestionnaire.d.ts +54 -5
  30. package/lib/utils/validateQuestionnaire.js +418 -42
  31. package/lib/utils/validateQuestionnaire.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.tsx +0 -1
  34. package/src/components/FormComponents/DecimalItem/DecimalField.tsx +1 -0
  35. package/src/components/FormComponents/DecimalItem/DecimalItem.tsx +2 -3
  36. package/src/components/FormComponents/IntegerItem/IntegerItem.tsx +2 -3
  37. package/src/components/FormComponents/StringItem/StringItem.tsx +2 -3
  38. package/src/components/FormComponents/TextItem/TextItem.tsx +2 -3
  39. package/src/components/FormComponents/UrlItem/UrlItem.tsx +2 -3
  40. package/src/hooks/useRenderingExtensions.ts +1 -9
  41. package/src/hooks/useValidationFeedback.ts +46 -12
  42. package/src/stores/questionnaireResponseStore.ts +5 -9
  43. package/src/utils/debounce.ts +1 -1
  44. package/src/utils/itemControl.ts +64 -9
  45. 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 type InvalidType = 'regex' | 'minLength' | 'maxLength' | 'required';
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, InvalidType>;
20
- export declare function getInputInvalidType(input: string, regexValidation: RegexValidation | null, minLength: number | null, maxLength: number | null): InvalidType | null;
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, invalidItems, enableWhenIsActivated, enableWhenItems, enableWhenExpressions } = params;
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 invalidItems;
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
- invalidItems: invalidItems,
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
- // FIXME repeat groups not working
136
+ // Validate repeat groups
73
137
  if (qItem.type === 'group' && qItem.repeats) {
74
- return;
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] = 'required';
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
- let childQRItem = (_b = qrItemsByIndex[index]) !== null && _b !== void 0 ? _b : {
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
- validateSingleItem(qItem, qrItem, invalidItems);
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 validateSingleItem(qItem, qrItem, invalidItems) {
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] = 'required';
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(getInputInString(answer), getRegexValidation(qItem), (_a = structuredDataCapture.getMinLength(qItem)) !== null && _a !== void 0 ? _a : null, (_b = qItem.maxLength) !== null && _b !== void 0 ? _b : null);
124
- if (!invalidInputType) {
125
- continue;
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(input, regexValidation, minLength, maxLength) {
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 'regex';
275
+ return ValidationResult.regex;
169
276
  }
170
277
  if (minLength && input.length < minLength) {
171
- return 'minLength';
278
+ return ValidationResult.minLength;
172
279
  }
173
280
  if (maxLength && input.length > maxLength) {
174
- return 'maxLength';
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