@openmrs/esm-form-engine-lib 2.1.0-pre.1362
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/.editorconfig +12 -0
- package/.eslintignore +2 -0
- package/.eslintrc +58 -0
- package/.husky/pre-commit +6 -0
- package/.husky/pre-push +6 -0
- package/.prettierignore +4 -0
- package/LICENSE.txt +401 -0
- package/README.md +136 -0
- package/__mocks__/concepts.mock.json +140 -0
- package/__mocks__/forms/afe-forms/component_art.json +38 -0
- package/__mocks__/forms/afe-forms/component_preclinic-review.json +38 -0
- package/__mocks__/forms/afe-forms/demo_hts-form.json +62 -0
- package/__mocks__/forms/afe-forms/form-component.json +38 -0
- package/__mocks__/forms/afe-forms/mini-form.json +31 -0
- package/__mocks__/forms/afe-forms/nested-form1.json +38 -0
- package/__mocks__/forms/afe-forms/nested-form2.json +38 -0
- package/__mocks__/forms/afe-forms/test-orders.json +72 -0
- package/__mocks__/forms/afe-forms/test-schema-transformer-form.json +88 -0
- package/__mocks__/forms/rfe-forms/age-validation-form.json +58 -0
- package/__mocks__/forms/rfe-forms/bmi-test-form.json +69 -0
- package/__mocks__/forms/rfe-forms/bsa-test-form.json +69 -0
- package/__mocks__/forms/rfe-forms/component_art.json +1705 -0
- package/__mocks__/forms/rfe-forms/component_preclinic-review.json +480 -0
- package/__mocks__/forms/rfe-forms/conditional-answered-form.json +97 -0
- package/__mocks__/forms/rfe-forms/conditional-required-form.json +281 -0
- package/__mocks__/forms/rfe-forms/demo_hts-form.json +346 -0
- package/__mocks__/forms/rfe-forms/edd-test-form.json +88 -0
- package/__mocks__/forms/rfe-forms/external_data_source_form.json +43 -0
- package/__mocks__/forms/rfe-forms/filter-answer-options-test-form.json +87 -0
- package/__mocks__/forms/rfe-forms/form-component.json +43 -0
- package/__mocks__/forms/rfe-forms/forms-loader.test.schema.ts +209 -0
- package/__mocks__/forms/rfe-forms/historical-expressions-form.json +170 -0
- package/__mocks__/forms/rfe-forms/labour_and_delivery_test_form.json +374 -0
- package/__mocks__/forms/rfe-forms/mini-form.json +29 -0
- package/__mocks__/forms/rfe-forms/mockHistoricalvisitsEncounter.json +89 -0
- package/__mocks__/forms/rfe-forms/months-on-art-form.json +90 -0
- package/__mocks__/forms/rfe-forms/multi-select-form.json +86 -0
- package/__mocks__/forms/rfe-forms/nested-form1.json +43 -0
- package/__mocks__/forms/rfe-forms/nested-form2.json +43 -0
- package/__mocks__/forms/rfe-forms/next-visit-test-form.json +78 -0
- package/__mocks__/forms/rfe-forms/obs-group-test_form.json +137 -0
- package/__mocks__/forms/rfe-forms/obs-list-data.ts +37 -0
- package/__mocks__/forms/rfe-forms/post-submission-test-form.json +116 -0
- package/__mocks__/forms/rfe-forms/reference-by-mapping-form.json +54 -0
- package/__mocks__/forms/rfe-forms/required-form.json +50 -0
- package/__mocks__/forms/rfe-forms/sample_fields.json +36 -0
- package/__mocks__/forms/rfe-forms/test-enrolment-form.json +241 -0
- package/__mocks__/forms/rfe-forms/treatment-end-date-test-form.json +121 -0
- package/__mocks__/forms/rfe-forms/viral-load-status-form.json +75 -0
- package/__mocks__/forms/rfe-forms/zscore-bmi-for-age-form.json +79 -0
- package/__mocks__/forms/rfe-forms/zscore-height-for-age-form.json +79 -0
- package/__mocks__/forms/rfe-forms/zscore-weight-height-form.json +77 -0
- package/__mocks__/packages/hiv/forms/hts_poc/1.0.json +8 -0
- package/__mocks__/packages/hiv/forms/hts_poc/1.1.json +91 -0
- package/__mocks__/packages/test-forms-registry.ts +12 -0
- package/__mocks__/patient.mock.ts +173 -0
- package/__mocks__/react-i18next.js +49 -0
- package/__mocks__/react-markdown.tsx +5 -0
- package/__mocks__/session.mock.ts +117 -0
- package/__mocks__/single-spa-react.js +11 -0
- package/__mocks__/use-initial-values/encounter.mock.json +963 -0
- package/__mocks__/use-initial-values/patient.mock.json +73 -0
- package/__mocks__/visit.mock.ts +19 -0
- package/dist/openmrs-esm-form-engine-lib.js +1 -0
- package/jest.config.js +30 -0
- package/package.json +104 -0
- package/prettier.config.js +8 -0
- package/readme/form-engine.jpeg +0 -0
- package/src/adapters/control-adapter.ts +29 -0
- package/src/adapters/encounter-datetime-adapter.ts +38 -0
- package/src/adapters/encounter-location-adapter.ts +39 -0
- package/src/adapters/encounter-provider-adapter.ts +48 -0
- package/src/adapters/encounter-role-adapter.ts +54 -0
- package/src/adapters/inline-date-adapter.ts +58 -0
- package/src/adapters/obs-adapter.ts +280 -0
- package/src/adapters/obs-comment-adapter.ts +60 -0
- package/src/adapters/orders-adapter.ts +75 -0
- package/src/adapters/patient-identifier-adapter.ts +40 -0
- package/src/adapters/program-state-adapter.ts +52 -0
- package/src/api/index.ts +178 -0
- package/src/components/error/error-modal.component.tsx +37 -0
- package/src/components/error/error.scss +4 -0
- package/src/components/extension/extension-parcel.component.tsx +32 -0
- package/src/components/field-label/field-label.component.tsx +32 -0
- package/src/components/field-label/field-label.scss +11 -0
- package/src/components/group/obs-group.component.tsx +29 -0
- package/src/components/group/obs-group.scss +12 -0
- package/src/components/inputs/content-switcher/content-switcher.component.tsx +71 -0
- package/src/components/inputs/content-switcher/content-switcher.scss +55 -0
- package/src/components/inputs/date/date.component.tsx +149 -0
- package/src/components/inputs/date/date.scss +36 -0
- package/src/components/inputs/file/camera/camera.component.tsx +34 -0
- package/src/components/inputs/file/camera/camera.scss +3 -0
- package/src/components/inputs/file/file.component.tsx +159 -0
- package/src/components/inputs/file/file.scss +101 -0
- package/src/components/inputs/fixed-value/fixed-value.component.tsx +19 -0
- package/src/components/inputs/markdown/markdown-wrapper.component.tsx +14 -0
- package/src/components/inputs/markdown/markdown.component.tsx +8 -0
- package/src/components/inputs/multi-select/multi-select.component.tsx +151 -0
- package/src/components/inputs/multi-select/multi-select.scss +25 -0
- package/src/components/inputs/multi-select/multi-select.test.tsx +90 -0
- package/src/components/inputs/number/number.component.tsx +69 -0
- package/src/components/inputs/number/number.scss +15 -0
- package/src/components/inputs/radio/radio.component.tsx +79 -0
- package/src/components/inputs/radio/radio.scss +36 -0
- package/src/components/inputs/select/dropdown.component.tsx +73 -0
- package/src/components/inputs/select/dropdown.scss +11 -0
- package/src/components/inputs/select/dropdown.test.tsx +120 -0
- package/src/components/inputs/text/text.component.tsx +65 -0
- package/src/components/inputs/text/text.scss +15 -0
- package/src/components/inputs/text/text.test.tsx +104 -0
- package/src/components/inputs/text-area/text-area.component.tsx +63 -0
- package/src/components/inputs/text-area/text-area.scss +11 -0
- package/src/components/inputs/toggle/toggle.component.tsx +66 -0
- package/src/components/inputs/toggle/toggle.scss +12 -0
- package/src/components/inputs/tooltip/tooltip.component.tsx +23 -0
- package/src/components/inputs/tooltip/tooltip.scss +8 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.component.tsx +187 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.scss +15 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.test.tsx +211 -0
- package/src/components/inputs/unspecified/unspecified.component.tsx +74 -0
- package/src/components/inputs/unspecified/unspecified.scss +7 -0
- package/src/components/inputs/unspecified/unspecified.test.tsx +95 -0
- package/src/components/inputs/workspace-launcher/workspace-launcher.component.tsx +35 -0
- package/src/components/inputs/workspace-launcher/workspace-launcher.scss +15 -0
- package/src/components/label/label.component.tsx +20 -0
- package/src/components/label/label.scss +11 -0
- package/src/components/loaders/loader.component.tsx +16 -0
- package/src/components/loaders/loader.scss +20 -0
- package/src/components/patient-banner/patient-banner.component.tsx +20 -0
- package/src/components/patient-banner/patient-banner.scss +12 -0
- package/src/components/previous-value-review/previous-value-review.component.tsx +49 -0
- package/src/components/previous-value-review/previous-value-review.scss +36 -0
- package/src/components/processor-factory/form-processor-factory.component.tsx +127 -0
- package/src/components/renderer/custom-hooks-renderer.component.tsx +30 -0
- package/src/components/renderer/field/fieldLogic.ts +214 -0
- package/src/components/renderer/field/form-field-renderer.component.tsx +281 -0
- package/src/components/renderer/field/form-field-renderer.scss +5 -0
- package/src/components/renderer/form/form-renderer.component.tsx +89 -0
- package/src/components/renderer/form/state.ts +54 -0
- package/src/components/renderer/page/page.renderer.component.tsx +50 -0
- package/src/components/renderer/page/page.renderer.scss +36 -0
- package/src/components/renderer/section/section-renderer.component.tsx +21 -0
- package/src/components/renderer/section/section-renderer.scss +19 -0
- package/src/components/repeat/helpers.test.ts +29 -0
- package/src/components/repeat/helpers.ts +68 -0
- package/src/components/repeat/repeat-controls.component.tsx +38 -0
- package/src/components/repeat/repeat-controls.scss +7 -0
- package/src/components/repeat/repeat.component.tsx +201 -0
- package/src/components/repeat/repeat.scss +30 -0
- package/src/components/repeat/repeat.test.ts +29 -0
- package/src/components/sidebar/sidebar.component.tsx +134 -0
- package/src/components/sidebar/sidebar.scss +121 -0
- package/src/components/value/value.component.tsx +27 -0
- package/src/components/value/value.scss +17 -0
- package/src/components/value/view/field-value-view.component.tsx +33 -0
- package/src/components/value/view/field-value-view.scss +31 -0
- package/src/constants.ts +12 -0
- package/src/datasources/concept-data-source.ts +42 -0
- package/src/datasources/data-source.ts +23 -0
- package/src/datasources/encounter-role-datasource.ts +15 -0
- package/src/datasources/historical-data-source.ts +11 -0
- package/src/datasources/location-data-source.ts +27 -0
- package/src/datasources/provider-datasource.ts +15 -0
- package/src/datasources/select-concept-answers-datasource.ts +15 -0
- package/src/declarations.d.ts +4 -0
- package/src/external-function-context.tsx +8 -0
- package/src/form-context.tsx +42 -0
- package/src/form-engine.component.tsx +178 -0
- package/src/form-engine.scss +140 -0
- package/src/form-engine.test.tsx +817 -0
- package/src/globals.ts +1 -0
- package/src/hooks/useClobData.tsx +21 -0
- package/src/hooks/useConcepts.tsx +55 -0
- package/src/hooks/useDatasourceDependentValue.ts +16 -0
- package/src/hooks/useEncounter.tsx +32 -0
- package/src/hooks/useEncounterRole.tsx +15 -0
- package/src/hooks/useEvaluateFormFieldExpressions.ts +138 -0
- package/src/hooks/useFieldValidationResults.ts +18 -0
- package/src/hooks/useFormCollapse.tsx +36 -0
- package/src/hooks/useFormFieldValidators.ts +22 -0
- package/src/hooks/useFormFieldValueAdapters.ts +24 -0
- package/src/hooks/useFormFields.ts +37 -0
- package/src/hooks/useFormFieldsMeta.ts +48 -0
- package/src/hooks/useFormJson.test.tsx +173 -0
- package/src/hooks/useFormJson.tsx +237 -0
- package/src/hooks/useFormStateHelpers.ts +50 -0
- package/src/hooks/useFormsConfig.tsx +27 -0
- package/src/hooks/useInitialValues.ts +38 -0
- package/src/hooks/usePatientData.tsx +32 -0
- package/src/hooks/usePatientPrograms.ts +32 -0
- package/src/hooks/usePostSubmissionActions.test.tsx +42 -0
- package/src/hooks/usePostSubmissionActions.ts +31 -0
- package/src/hooks/useProcessorDependencies.ts +30 -0
- package/src/hooks/useRestMaxResultsCount.ts +5 -0
- package/src/hooks/useSystemSetting.ts +36 -0
- package/src/hooks/useWorkspaceLayout.ts +29 -0
- package/src/index.ts +12 -0
- package/src/lifecycle.ts +33 -0
- package/src/post-submission-actions/program-enrollment-action.ts +138 -0
- package/src/processors/encounter/encounter-form-processor.ts +337 -0
- package/src/processors/encounter/encounter-processor-helper.ts +320 -0
- package/src/processors/form-processor.ts +41 -0
- package/src/provider/form-factory-helper.ts +100 -0
- package/src/provider/form-factory-provider.tsx +169 -0
- package/src/provider/form-provider.tsx +37 -0
- package/src/registry/inbuilt-components/InbuiltPostSubmissionActions.ts +9 -0
- package/src/registry/inbuilt-components/control-templates.ts +57 -0
- package/src/registry/inbuilt-components/inbuiltControls.ts +99 -0
- package/src/registry/inbuilt-components/inbuiltDataSources.ts +41 -0
- package/src/registry/inbuilt-components/inbuiltFieldValueAdapters.ts +64 -0
- package/src/registry/inbuilt-components/inbuiltTransformers.ts +10 -0
- package/src/registry/inbuilt-components/inbuiltValidators.ts +33 -0
- package/src/registry/inbuilt-components/template-component-map.ts +28 -0
- package/src/registry/registry.test.ts +20 -0
- package/src/registry/registry.ts +261 -0
- package/src/routes.json +1 -0
- package/src/setupI18n.ts +16 -0
- package/src/setupTests.ts +5 -0
- package/src/transformers/default-schema-transformer.test.ts +155 -0
- package/src/transformers/default-schema-transformer.ts +239 -0
- package/src/types/domain.ts +129 -0
- package/src/types/index.ts +130 -0
- package/src/types/schema.ts +238 -0
- package/src/typings.d.ts +9 -0
- package/src/utils/boolean-utils.ts +25 -0
- package/src/utils/common-expression-helpers.ts +503 -0
- package/src/utils/common-utils.test.ts +136 -0
- package/src/utils/common-utils.ts +55 -0
- package/src/utils/error-utils.ts +37 -0
- package/src/utils/expression-parser.test.ts +308 -0
- package/src/utils/expression-parser.ts +158 -0
- package/src/utils/expression-runner.test.ts +387 -0
- package/src/utils/expression-runner.ts +219 -0
- package/src/utils/form-helper.test.ts +482 -0
- package/src/utils/form-helper.ts +210 -0
- package/src/utils/form-page-utils.ts +13 -0
- package/src/utils/forms-loader.test.ts +323 -0
- package/src/utils/forms-loader.ts +306 -0
- package/src/utils/post-submission-action-helper.ts +71 -0
- package/src/utils/test-utils.ts +54 -0
- package/src/utils/zscore-service.ts +59 -0
- package/src/validators/conditional-answered-validator.test.ts +61 -0
- package/src/validators/conditional-answered-validator.ts +17 -0
- package/src/validators/date-validator.test.ts +46 -0
- package/src/validators/date-validator.ts +19 -0
- package/src/validators/default-value-validator.test.ts +90 -0
- package/src/validators/default-value-validator.ts +36 -0
- package/src/validators/form-validator.test.ts +188 -0
- package/src/validators/form-validator.ts +95 -0
- package/src/validators/js-expression-validator.test.ts +118 -0
- package/src/validators/js-expression-validator.ts +44 -0
- package/src/validators/schema.ts +34 -0
- package/src/zscore/bfa_boys_5_above.json +2522 -0
- package/src/zscore/bfa_girls_5_above.json +2522 -0
- package/src/zscore/hfa_boys_5_above.json +2186 -0
- package/src/zscore/hfa_boys_below5.json +22286 -0
- package/src/zscore/hfa_girls_5_above.json +2186 -0
- package/src/zscore/hfa_girls_below5.json +22286 -0
- package/src/zscore/wfl_boys_below5.json +7814 -0
- package/src/zscore/wfl_girls_below5.json +7814 -0
- package/src/zscore-tests/bmi-age.test.tsx +88 -0
- package/src/zscore-tests/height-age.test.tsx +96 -0
- package/src/zscore-tests/weight-height.test.tsx +87 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/en.json +47 -0
- package/translations/es.json +38 -0
- package/translations/fr.json +38 -0
- package/translations/km.json +38 -0
- package/tsconfig.json +19 -0
- package/turbo.json +15 -0
- package/webpack.config.js +1 -0
@@ -0,0 +1,503 @@
|
|
1
|
+
import dayjs from 'dayjs';
|
2
|
+
import duration from 'dayjs/plugin/duration';
|
3
|
+
dayjs.extend(duration);
|
4
|
+
import findIndex from 'lodash/findIndex';
|
5
|
+
import filter from 'lodash/filter';
|
6
|
+
import first from 'lodash/first';
|
7
|
+
import forEach from 'lodash/forEach';
|
8
|
+
import last from 'lodash/last';
|
9
|
+
import { type FormField } from '../types';
|
10
|
+
import { type FormNode } from './expression-runner';
|
11
|
+
import { isEmpty as isValueEmpty } from '../validators/form-validator';
|
12
|
+
import * as apiFunctions from '../api';
|
13
|
+
import { getZRefByGenderAndAge } from './zscore-service';
|
14
|
+
import { ConceptFalse, ConceptTrue } from '../constants';
|
15
|
+
|
16
|
+
export class CommonExpressionHelpers {
|
17
|
+
node: FormNode = null;
|
18
|
+
patient: any = null;
|
19
|
+
allFields: FormField[] = [];
|
20
|
+
allFieldValues: Record<string, any> = {};
|
21
|
+
allFieldsKeys: string[] = [];
|
22
|
+
api = apiFunctions;
|
23
|
+
isEmpty = isValueEmpty;
|
24
|
+
|
25
|
+
constructor(
|
26
|
+
node: FormNode,
|
27
|
+
patient: any,
|
28
|
+
allFields: FormField[],
|
29
|
+
allFieldValues: Record<string, any>,
|
30
|
+
allFieldsKeys: string[],
|
31
|
+
) {
|
32
|
+
this.allFields = allFields;
|
33
|
+
this.allFieldValues = allFieldValues;
|
34
|
+
this.allFieldsKeys = allFieldsKeys;
|
35
|
+
this.node = node;
|
36
|
+
this.patient = patient;
|
37
|
+
}
|
38
|
+
|
39
|
+
today = () => {
|
40
|
+
return new Date();
|
41
|
+
};
|
42
|
+
|
43
|
+
includes = <T = any>(collection: T[], value: T) => {
|
44
|
+
return collection?.includes(value);
|
45
|
+
};
|
46
|
+
|
47
|
+
isDateBefore = (left: Date, right: string | Date, format?: string) => {
|
48
|
+
let otherDate: any = right;
|
49
|
+
if (typeof right == 'string') {
|
50
|
+
otherDate = format ? dayjs(right, format, true).toDate() : dayjs(right, 'YYYY-MM-DD', true).toDate();
|
51
|
+
}
|
52
|
+
return left?.getTime() < otherDate.getTime();
|
53
|
+
};
|
54
|
+
|
55
|
+
isDateAfter = (selectedDate: Date, baseDate: Date, duration: number, timePeriod: string) => {
|
56
|
+
let calculatedDate = new Date(0);
|
57
|
+
selectedDate = dayjs(selectedDate, 'YYYY-MM-DD', true).toDate();
|
58
|
+
baseDate = dayjs(baseDate, 'YYYY-MM-DD', true).toDate();
|
59
|
+
|
60
|
+
switch (timePeriod) {
|
61
|
+
case 'months':
|
62
|
+
calculatedDate = new Date(baseDate.setMonth(baseDate.getMonth() + duration));
|
63
|
+
break;
|
64
|
+
case 'weeks':
|
65
|
+
calculatedDate = this.addWeeksToDate(baseDate, duration);
|
66
|
+
break;
|
67
|
+
case 'days':
|
68
|
+
calculatedDate = this.addDaysToDate(baseDate, duration);
|
69
|
+
break;
|
70
|
+
case 'years':
|
71
|
+
calculatedDate = new Date(baseDate.setFullYear(baseDate.getFullYear() + duration));
|
72
|
+
break;
|
73
|
+
default:
|
74
|
+
break;
|
75
|
+
}
|
76
|
+
|
77
|
+
return selectedDate.getTime() > calculatedDate.getTime();
|
78
|
+
};
|
79
|
+
|
80
|
+
addWeeksToDate = (date: Date, weeks: number) => {
|
81
|
+
date.setDate(date.getDate() + 7 * weeks);
|
82
|
+
|
83
|
+
return date;
|
84
|
+
};
|
85
|
+
|
86
|
+
addDaysToDate = (date: Date, days: number): Date => {
|
87
|
+
return dayjs(date).add(days, 'day').toDate();
|
88
|
+
};
|
89
|
+
|
90
|
+
useFieldValue = (questionId: string) => {
|
91
|
+
if (this.allFieldsKeys.includes(questionId)) {
|
92
|
+
return this.allFieldValues[questionId];
|
93
|
+
}
|
94
|
+
return null;
|
95
|
+
};
|
96
|
+
|
97
|
+
doesNotMatchExpression = (regexString: string, val: string | null | undefined): boolean => {
|
98
|
+
if (!val || ['undefined', 'null', ''].includes(val.toString())) {
|
99
|
+
return true;
|
100
|
+
}
|
101
|
+
const pattern = new RegExp(regexString);
|
102
|
+
|
103
|
+
return !pattern.test(val);
|
104
|
+
};
|
105
|
+
|
106
|
+
calcBMI = (height: number, weight: number) => {
|
107
|
+
let r: string;
|
108
|
+
if (height && weight) {
|
109
|
+
r = (weight / (((height / 100) * height) / 100)).toFixed(1);
|
110
|
+
}
|
111
|
+
return r ? parseFloat(r) : null;
|
112
|
+
};
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Expected date of delivery
|
116
|
+
* @param lmpQuestionId
|
117
|
+
* @returns
|
118
|
+
*/
|
119
|
+
calcEDD = (lmp: Date) => {
|
120
|
+
let resultEdd = {};
|
121
|
+
if (lmp) {
|
122
|
+
resultEdd = new Date(lmp.getTime() + 280 * 24 * 60 * 60 * 1000);
|
123
|
+
}
|
124
|
+
return lmp ? resultEdd : null;
|
125
|
+
};
|
126
|
+
|
127
|
+
calcMonthsOnART = (artStartDate: Date) => {
|
128
|
+
let today = new Date();
|
129
|
+
let resultMonthsOnART: number;
|
130
|
+
let artInDays = Math.round((today.getTime() - artStartDate.getTime?.()) / 86400000);
|
131
|
+
if (artStartDate && artInDays >= 30) {
|
132
|
+
resultMonthsOnART = Math.floor(artInDays / 30);
|
133
|
+
}
|
134
|
+
return artStartDate ? resultMonthsOnART : null;
|
135
|
+
};
|
136
|
+
|
137
|
+
calcViralLoadStatus = (viralLoadCount: number) => {
|
138
|
+
let resultViralLoadStatus: string;
|
139
|
+
if (viralLoadCount) {
|
140
|
+
if (viralLoadCount > 50) {
|
141
|
+
resultViralLoadStatus = 'a6768be6-c08e-464d-8f53-5f4229508e54';
|
142
|
+
} else {
|
143
|
+
resultViralLoadStatus = '5d5e42cc-acc4-4069-b3a8-7163e0db5d96';
|
144
|
+
}
|
145
|
+
}
|
146
|
+
return resultViralLoadStatus ?? null;
|
147
|
+
};
|
148
|
+
|
149
|
+
calcNextVisitDate = (followupDate, arvDispensedInDays) => {
|
150
|
+
let resultNextVisitDate: Date;
|
151
|
+
if (followupDate && arvDispensedInDays) {
|
152
|
+
resultNextVisitDate = new Date(followupDate.getTime() + arvDispensedInDays * 24 * 60 * 60 * 1000);
|
153
|
+
}
|
154
|
+
return resultNextVisitDate ?? null;
|
155
|
+
};
|
156
|
+
|
157
|
+
calcTreatmentEndDate = (followupDate: Date, arvDispensedInDays: number, patientStatus: string) => {
|
158
|
+
let resultTreatmentEndDate = {};
|
159
|
+
let extraDaysAdded = 30 + arvDispensedInDays;
|
160
|
+
if (followupDate && arvDispensedInDays && patientStatus == '160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') {
|
161
|
+
resultTreatmentEndDate = new Date(followupDate.getTime() + extraDaysAdded * 24 * 60 * 60 * 1000);
|
162
|
+
}
|
163
|
+
return followupDate && arvDispensedInDays && patientStatus == '160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
|
164
|
+
? resultTreatmentEndDate
|
165
|
+
: null;
|
166
|
+
};
|
167
|
+
|
168
|
+
calcAgeBasedOnDate = (dateValue?: ConstructorParameters<typeof Date>[0] | null) => {
|
169
|
+
let targetYear = null;
|
170
|
+
if (dateValue) {
|
171
|
+
targetYear = new Date(dateValue).getFullYear();
|
172
|
+
} else {
|
173
|
+
targetYear = new Date().getFullYear();
|
174
|
+
}
|
175
|
+
let birthDate = new Date(this.patient.birthDate).getFullYear();
|
176
|
+
let calculatedYear = targetYear - birthDate;
|
177
|
+
return calculatedYear;
|
178
|
+
};
|
179
|
+
|
180
|
+
//Ampath Helper Functions
|
181
|
+
calcBSA = (height: number, weight: number) => {
|
182
|
+
let result: string;
|
183
|
+
if (height && weight) {
|
184
|
+
result = Math.sqrt((height * weight) / 3600).toFixed(2);
|
185
|
+
}
|
186
|
+
return result ? parseFloat(result) : null;
|
187
|
+
};
|
188
|
+
|
189
|
+
arrayContains = <T = any>(array: T[], members: T[] | T) => {
|
190
|
+
if (!array || !Array.isArray(array)) {
|
191
|
+
return false;
|
192
|
+
}
|
193
|
+
|
194
|
+
if (array.length === 0) {
|
195
|
+
return members === undefined || members === null || (Array.isArray(members) && members.length === 0);
|
196
|
+
}
|
197
|
+
|
198
|
+
if (!Array.isArray(members)) {
|
199
|
+
members = [members];
|
200
|
+
}
|
201
|
+
|
202
|
+
if (members.length === 0) {
|
203
|
+
return true;
|
204
|
+
}
|
205
|
+
|
206
|
+
for (let val of members) {
|
207
|
+
if (array.indexOf(val) === -1) {
|
208
|
+
return false;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
return true;
|
213
|
+
};
|
214
|
+
|
215
|
+
arrayContainsAny = <T = any>(array: T[], members: T[]) => {
|
216
|
+
if (!array || !Array.isArray(array)) {
|
217
|
+
return false;
|
218
|
+
}
|
219
|
+
|
220
|
+
if (array.length === 0) {
|
221
|
+
return members === undefined || members === null || (Array.isArray(members) && members.length === 0);
|
222
|
+
}
|
223
|
+
|
224
|
+
if (!Array.isArray(members)) {
|
225
|
+
members = [members];
|
226
|
+
}
|
227
|
+
|
228
|
+
if (members.length === 0) {
|
229
|
+
return true;
|
230
|
+
}
|
231
|
+
|
232
|
+
for (let val of members) {
|
233
|
+
if (array.indexOf(val) !== -1) {
|
234
|
+
return true;
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
return false;
|
239
|
+
};
|
240
|
+
|
241
|
+
formatDate = (value: ConstructorParameters<typeof Date>[0], format?: string | null, offset?: string | null) => {
|
242
|
+
format = format ?? 'yyyy-MM-dd';
|
243
|
+
offset = offset ?? '+0300';
|
244
|
+
|
245
|
+
if (!(value instanceof Date)) {
|
246
|
+
value = new Date(value);
|
247
|
+
if (value === null || value === undefined) {
|
248
|
+
throw new Error('DateFormatException: value passed ' + 'is not a valid date');
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
return value;
|
253
|
+
};
|
254
|
+
|
255
|
+
extractRepeatingGroupValues = (key: string | number | symbol, array: Record<string | number | symbol, unknown>[]) => {
|
256
|
+
const values = array.map(function (item) {
|
257
|
+
return item[key];
|
258
|
+
});
|
259
|
+
return values;
|
260
|
+
};
|
261
|
+
|
262
|
+
/**
|
263
|
+
* Calculates the gravida (total number of pregnancies) based on term pregnancies and abortions/miscarriages.
|
264
|
+
*
|
265
|
+
* @param {number|string} parityTerm - The number of term pregnancies.
|
266
|
+
* @param {number|string} parityAbortion - The number of abortions (including miscarriages).
|
267
|
+
* @returns {number} The total number of pregnancies (gravida).
|
268
|
+
* @throws {Error} If either input is not a valid number.
|
269
|
+
*
|
270
|
+
* @example
|
271
|
+
* const gravida = calcGravida(2, 1);
|
272
|
+
* console.log(gravida); // Output: 3
|
273
|
+
*/
|
274
|
+
|
275
|
+
calcGravida = (parityTerm, parityAbortion) => {
|
276
|
+
const term = parseInt(parityTerm, 10);
|
277
|
+
const abortion = parseInt(parityAbortion, 10);
|
278
|
+
|
279
|
+
if (!Number.isInteger(term) || !Number.isInteger(abortion)) {
|
280
|
+
throw new Error('Both inputs must be valid numbers.');
|
281
|
+
}
|
282
|
+
|
283
|
+
return term + abortion;
|
284
|
+
};
|
285
|
+
|
286
|
+
calcWeightForHeightZscore = (height, weight) => {
|
287
|
+
const birthDate = new Date(this.patient.birthDate);
|
288
|
+
const weightForHeightRef = getZRefByGenderAndAge(this.patient.sex, birthDate, new Date()).weightForHeightRef;
|
289
|
+
let refSection;
|
290
|
+
let formattedSDValue;
|
291
|
+
if (height && weight) {
|
292
|
+
height = parseFloat(height).toFixed(1);
|
293
|
+
}
|
294
|
+
const standardHeightMin = 45;
|
295
|
+
const standardMaxHeight = 110;
|
296
|
+
if (height < standardHeightMin || height > standardMaxHeight) {
|
297
|
+
formattedSDValue = -4;
|
298
|
+
} else {
|
299
|
+
refSection = filter(weightForHeightRef, (refObject) => {
|
300
|
+
return parseFloat(refObject['Length']).toFixed(1) === height;
|
301
|
+
});
|
302
|
+
}
|
303
|
+
|
304
|
+
const refSectionObject = first(refSection);
|
305
|
+
if (refSectionObject) {
|
306
|
+
const refObjectValues = Object.keys(refSectionObject)
|
307
|
+
.map((key) => refSectionObject[key])
|
308
|
+
.map((x) => x);
|
309
|
+
const refObjectKeys = Object.keys(refSectionObject);
|
310
|
+
const minimumValue = refObjectValues[1];
|
311
|
+
const minReferencePoint = [];
|
312
|
+
if (weight < minimumValue) {
|
313
|
+
minReferencePoint.push(minimumValue);
|
314
|
+
} else {
|
315
|
+
forEach(refObjectValues, (value) => {
|
316
|
+
if (value <= weight) {
|
317
|
+
minReferencePoint.push(value);
|
318
|
+
}
|
319
|
+
});
|
320
|
+
}
|
321
|
+
const lastReferenceValue = last(minReferencePoint);
|
322
|
+
const lastValueIndex = findIndex(refObjectValues, (o) => {
|
323
|
+
return o === lastReferenceValue;
|
324
|
+
});
|
325
|
+
const SDValue = refObjectKeys[lastValueIndex];
|
326
|
+
formattedSDValue = SDValue?.replace('SD', '');
|
327
|
+
if (formattedSDValue.includes('neg')) {
|
328
|
+
formattedSDValue = formattedSDValue.substring(1, 0);
|
329
|
+
formattedSDValue = '-' + formattedSDValue;
|
330
|
+
}
|
331
|
+
if (
|
332
|
+
formattedSDValue === 'S' ||
|
333
|
+
formattedSDValue === 'L' ||
|
334
|
+
formattedSDValue === 'M' ||
|
335
|
+
formattedSDValue === '-5'
|
336
|
+
) {
|
337
|
+
formattedSDValue = '-4';
|
338
|
+
}
|
339
|
+
}
|
340
|
+
|
341
|
+
return height && weight ? formattedSDValue : null;
|
342
|
+
};
|
343
|
+
|
344
|
+
calcBMIForAgeZscore = (height, weight) => {
|
345
|
+
const birthDate = new Date(this.patient.birthDate);
|
346
|
+
const bmiForAgeRef = getZRefByGenderAndAge(this.patient.sex, birthDate, new Date()).bmiForAgeRef;
|
347
|
+
let bmi;
|
348
|
+
const maxAgeInDays = 1856;
|
349
|
+
if (height && weight) {
|
350
|
+
bmi = (weight / (((height / 100) * height) / 100)).toFixed(1);
|
351
|
+
}
|
352
|
+
const refSectionObject = first(bmiForAgeRef);
|
353
|
+
let formattedSDValue;
|
354
|
+
if (refSectionObject) {
|
355
|
+
const refObjectValues = Object.keys(refSectionObject)
|
356
|
+
.map((key) => refSectionObject[key])
|
357
|
+
.map((x) => x);
|
358
|
+
const refObjectKeys = Object.keys(refSectionObject);
|
359
|
+
const minimumValue = refObjectValues[1];
|
360
|
+
const minReferencePoint = [];
|
361
|
+
if (bmi < minimumValue) {
|
362
|
+
minReferencePoint.push(minimumValue);
|
363
|
+
} else {
|
364
|
+
forEach(refObjectValues, (value) => {
|
365
|
+
if (value <= bmi) {
|
366
|
+
minReferencePoint.push(value);
|
367
|
+
}
|
368
|
+
});
|
369
|
+
}
|
370
|
+
const lastReferenceValue = last(minReferencePoint);
|
371
|
+
const lastValueIndex = findIndex(refObjectValues, (o) => {
|
372
|
+
return o === lastReferenceValue;
|
373
|
+
});
|
374
|
+
const SDValue = refObjectKeys[lastValueIndex];
|
375
|
+
formattedSDValue = SDValue?.replace('SD', '');
|
376
|
+
if (formattedSDValue.includes('neg')) {
|
377
|
+
formattedSDValue = formattedSDValue.substring(1, 0);
|
378
|
+
formattedSDValue = '-' + formattedSDValue;
|
379
|
+
}
|
380
|
+
|
381
|
+
if (
|
382
|
+
formattedSDValue === 'S' ||
|
383
|
+
formattedSDValue === 'L' ||
|
384
|
+
formattedSDValue === 'M' ||
|
385
|
+
formattedSDValue === '-5'
|
386
|
+
) {
|
387
|
+
formattedSDValue = '-4';
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
return bmi && refSectionObject ? formattedSDValue : null;
|
392
|
+
};
|
393
|
+
|
394
|
+
calcHeightForAgeZscore = (height, weight) => {
|
395
|
+
const birthDate = new Date(this.patient.birthDate);
|
396
|
+
const heightForAgeRef = getZRefByGenderAndAge(this.patient.sex, birthDate, new Date()).heightForAgeRef;
|
397
|
+
const refSectionObject = first(heightForAgeRef);
|
398
|
+
let formattedSDValue;
|
399
|
+
if (refSectionObject) {
|
400
|
+
const refObjectValues = Object.keys(refSectionObject)
|
401
|
+
.map((key) => refSectionObject[key])
|
402
|
+
.map((x) => x);
|
403
|
+
const refObjectKeys = Object.keys(refSectionObject);
|
404
|
+
const minimumValue = refObjectValues[1];
|
405
|
+
const minReferencePoint = [];
|
406
|
+
if (height < minimumValue) {
|
407
|
+
minReferencePoint.push(minimumValue);
|
408
|
+
} else {
|
409
|
+
forEach(refObjectValues, (value) => {
|
410
|
+
if (value <= height) {
|
411
|
+
minReferencePoint.push(value);
|
412
|
+
}
|
413
|
+
});
|
414
|
+
}
|
415
|
+
const lastReferenceValue = last(minReferencePoint);
|
416
|
+
const lastValueIndex = findIndex(refObjectValues, (o) => {
|
417
|
+
return o === lastReferenceValue;
|
418
|
+
});
|
419
|
+
const SDValue = refObjectKeys[lastValueIndex];
|
420
|
+
formattedSDValue = SDValue?.replace('SD', '');
|
421
|
+
if (formattedSDValue.includes('neg')) {
|
422
|
+
formattedSDValue = formattedSDValue.substring(1, 0);
|
423
|
+
formattedSDValue = '-' + formattedSDValue;
|
424
|
+
}
|
425
|
+
|
426
|
+
if (
|
427
|
+
formattedSDValue === 'S' ||
|
428
|
+
formattedSDValue === 'L' ||
|
429
|
+
formattedSDValue === 'M' ||
|
430
|
+
formattedSDValue === '-5'
|
431
|
+
) {
|
432
|
+
formattedSDValue = '-4';
|
433
|
+
}
|
434
|
+
}
|
435
|
+
|
436
|
+
return height && weight && refSectionObject ? formattedSDValue : null;
|
437
|
+
};
|
438
|
+
|
439
|
+
calcTimeDifference = (obsDate: Date | dayjs.Dayjs, timeFrame: 'd' | 'w' | 'm' | 'y') => {
|
440
|
+
let daySinceLastObs: number | string = '';
|
441
|
+
const endDate = dayjs();
|
442
|
+
if (obsDate) {
|
443
|
+
if (timeFrame == 'd') {
|
444
|
+
daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'day', true)));
|
445
|
+
}
|
446
|
+
if (timeFrame == 'w') {
|
447
|
+
daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'week', true)));
|
448
|
+
}
|
449
|
+
if (timeFrame == 'm') {
|
450
|
+
daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'month', true)));
|
451
|
+
}
|
452
|
+
if (timeFrame == 'y') {
|
453
|
+
daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'year', true)));
|
454
|
+
}
|
455
|
+
}
|
456
|
+
return daySinceLastObs === '' ? '0' : daySinceLastObs;
|
457
|
+
};
|
458
|
+
|
459
|
+
/**
|
460
|
+
* Used as wrapper around async functions. It basically evaluates the promised value.
|
461
|
+
*/
|
462
|
+
resolve = (lazy: Promise<unknown>) => {
|
463
|
+
return Promise.resolve(lazy);
|
464
|
+
};
|
465
|
+
}
|
466
|
+
|
467
|
+
export function registerDependency(node: FormNode, determinant: FormField) {
|
468
|
+
if (!node || !determinant) {
|
469
|
+
return;
|
470
|
+
}
|
471
|
+
switch (node.type) {
|
472
|
+
case 'page':
|
473
|
+
if (!determinant.pageDependents) {
|
474
|
+
determinant.pageDependents = new Set();
|
475
|
+
}
|
476
|
+
determinant.pageDependents.add(node.value.label);
|
477
|
+
break;
|
478
|
+
case 'section':
|
479
|
+
if (!determinant.sectionDependents) {
|
480
|
+
determinant.sectionDependents = new Set();
|
481
|
+
}
|
482
|
+
determinant.sectionDependents.add(node.value.label);
|
483
|
+
break;
|
484
|
+
default:
|
485
|
+
if (!determinant.fieldDependents) {
|
486
|
+
determinant.fieldDependents = new Set();
|
487
|
+
}
|
488
|
+
determinant.fieldDependents.add(node.value['id']);
|
489
|
+
}
|
490
|
+
}
|
491
|
+
|
492
|
+
export const booleanConceptToBoolean = (booleanConceptRepresentation): boolean => {
|
493
|
+
const { value } = booleanConceptRepresentation;
|
494
|
+
if (!booleanConceptRepresentation) {
|
495
|
+
throw new Error('booleanConceptRepresentation cannot be a null value');
|
496
|
+
}
|
497
|
+
if (value == ConceptTrue) {
|
498
|
+
return true;
|
499
|
+
}
|
500
|
+
if (value == ConceptFalse) {
|
501
|
+
return false;
|
502
|
+
}
|
503
|
+
};
|
@@ -0,0 +1,136 @@
|
|
1
|
+
import { flattenObsList, hasRendering, clearSubmission, gracefullySetSubmission, hasSubmission } from './common-utils';
|
2
|
+
import { isEmpty } from '../validators/form-validator';
|
3
|
+
import { type FormField, type OpenmrsObs } from '../types';
|
4
|
+
import { obsList } from '__mocks__/forms/rfe-forms/obs-list-data';
|
5
|
+
|
6
|
+
jest.mock('@openmrs/esm-framework', () => ({
|
7
|
+
formatDate: jest.fn(),
|
8
|
+
restBaseUrl: 'http://openmrs.com/rest',
|
9
|
+
}));
|
10
|
+
|
11
|
+
jest.mock('../validators/form-validator', () => ({
|
12
|
+
isEmpty: jest.fn(),
|
13
|
+
}));
|
14
|
+
|
15
|
+
describe('utils functions', () => {
|
16
|
+
describe('flattenObsList', () => {
|
17
|
+
it('should flatten a nested obs list', () => {
|
18
|
+
const result = flattenObsList(obsList);
|
19
|
+
expect(result).toHaveLength(2);
|
20
|
+
});
|
21
|
+
});
|
22
|
+
|
23
|
+
describe('hasRendering', () => {
|
24
|
+
it('should return true if the field has the specified rendering', () => {
|
25
|
+
const field: FormField = {
|
26
|
+
label: 'Test Field',
|
27
|
+
type: 'obs',
|
28
|
+
questionOptions: { rendering: 'text' },
|
29
|
+
id: 'testFieldId',
|
30
|
+
} as FormField;
|
31
|
+
|
32
|
+
expect(hasRendering(field, 'text')).toBe(true);
|
33
|
+
});
|
34
|
+
|
35
|
+
it('should return false if the field does not have the specified rendering', () => {
|
36
|
+
const field: FormField = {
|
37
|
+
label: 'Test Field',
|
38
|
+
type: 'obs',
|
39
|
+
questionOptions: { rendering: 'textarea' },
|
40
|
+
id: 'testFieldId',
|
41
|
+
} as FormField;
|
42
|
+
|
43
|
+
expect(hasRendering(field, 'text')).toBe(false);
|
44
|
+
});
|
45
|
+
});
|
46
|
+
|
47
|
+
describe('clearSubmission', () => {
|
48
|
+
it('should initialize the submission object if not present and clear values', () => {
|
49
|
+
const field: FormField = {
|
50
|
+
label: 'Test Field',
|
51
|
+
type: 'obs',
|
52
|
+
questionOptions: {},
|
53
|
+
id: 'testFieldId',
|
54
|
+
meta: {},
|
55
|
+
} as FormField;
|
56
|
+
|
57
|
+
clearSubmission(field);
|
58
|
+
|
59
|
+
expect(field.meta.submission).toEqual({
|
60
|
+
voidedValue: null,
|
61
|
+
newValue: null,
|
62
|
+
});
|
63
|
+
});
|
64
|
+
});
|
65
|
+
|
66
|
+
describe('gracefullySetSubmission', () => {
|
67
|
+
it('should set the newValue and voidedValue correctly', () => {
|
68
|
+
const field: FormField = {
|
69
|
+
label: 'Test Field',
|
70
|
+
type: 'obs',
|
71
|
+
questionOptions: {},
|
72
|
+
id: 'testFieldId',
|
73
|
+
meta: {},
|
74
|
+
} as FormField;
|
75
|
+
|
76
|
+
(isEmpty as jest.Mock).mockReturnValueOnce(false).mockReturnValueOnce(false);
|
77
|
+
|
78
|
+
const newValue = 'new value';
|
79
|
+
const voidedValue = 'voided value';
|
80
|
+
|
81
|
+
gracefullySetSubmission(field, newValue, voidedValue);
|
82
|
+
|
83
|
+
expect(field.meta.submission).toEqual({
|
84
|
+
voidedValue: 'voided value',
|
85
|
+
newValue: 'new value',
|
86
|
+
});
|
87
|
+
});
|
88
|
+
|
89
|
+
it('should not set values if they are empty', () => {
|
90
|
+
const field: FormField = {
|
91
|
+
label: 'Test Field',
|
92
|
+
type: 'obs',
|
93
|
+
questionOptions: {},
|
94
|
+
id: 'testFieldId',
|
95
|
+
meta: {},
|
96
|
+
} as FormField;
|
97
|
+
|
98
|
+
(isEmpty as jest.Mock).mockReturnValueOnce(true).mockReturnValueOnce(true);
|
99
|
+
|
100
|
+
gracefullySetSubmission(field, '', '');
|
101
|
+
|
102
|
+
expect(field.meta.submission).toEqual({});
|
103
|
+
});
|
104
|
+
});
|
105
|
+
|
106
|
+
describe('hasSubmission', () => {
|
107
|
+
it('should return true if there is a newValue or voidedValue', () => {
|
108
|
+
const field: FormField = {
|
109
|
+
label: 'Test Field',
|
110
|
+
type: 'obs',
|
111
|
+
questionOptions: {},
|
112
|
+
id: 'testFieldId',
|
113
|
+
meta: {
|
114
|
+
submission: {
|
115
|
+
newValue: 'new value',
|
116
|
+
voidedValue: 'voided value',
|
117
|
+
},
|
118
|
+
},
|
119
|
+
} as FormField;
|
120
|
+
|
121
|
+
expect(hasSubmission(field)).toBe(true);
|
122
|
+
});
|
123
|
+
|
124
|
+
it('should return false if there is no newValue or voidedValue', () => {
|
125
|
+
const field: FormField = {
|
126
|
+
label: 'Test Field',
|
127
|
+
type: 'obs',
|
128
|
+
questionOptions: {},
|
129
|
+
id: 'testFieldId',
|
130
|
+
meta: {},
|
131
|
+
} as FormField;
|
132
|
+
|
133
|
+
expect(hasSubmission(field)).toBe(false);
|
134
|
+
});
|
135
|
+
});
|
136
|
+
});
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import { type FormField, type OpenmrsObs, type RenderType } from '../types';
|
2
|
+
import { isEmpty } from '../validators/form-validator';
|
3
|
+
|
4
|
+
export function flattenObsList(obsList: OpenmrsObs[]): OpenmrsObs[] {
|
5
|
+
const flattenedList: OpenmrsObs[] = [];
|
6
|
+
|
7
|
+
function flatten(obs: OpenmrsObs): void {
|
8
|
+
flattenedList.push(obs);
|
9
|
+
if (obs.groupMembers?.length) {
|
10
|
+
obs.groupMembers.forEach((groupMember) => {
|
11
|
+
flatten(groupMember);
|
12
|
+
});
|
13
|
+
}
|
14
|
+
}
|
15
|
+
obsList.forEach((obs) => {
|
16
|
+
flatten(obs);
|
17
|
+
});
|
18
|
+
|
19
|
+
return flattenedList;
|
20
|
+
}
|
21
|
+
|
22
|
+
export function hasRendering(field: FormField, rendering: RenderType) {
|
23
|
+
return field.questionOptions.rendering === rendering;
|
24
|
+
}
|
25
|
+
|
26
|
+
export function clearSubmission(field: FormField) {
|
27
|
+
if (!field.meta?.submission) {
|
28
|
+
field.meta = { ...(field.meta || {}), submission: {} };
|
29
|
+
}
|
30
|
+
field.meta.submission = {
|
31
|
+
voidedValue: null,
|
32
|
+
newValue: null,
|
33
|
+
};
|
34
|
+
}
|
35
|
+
|
36
|
+
export function gracefullySetSubmission(field: FormField, newValue: any, voidedValue: any) {
|
37
|
+
if (!field.meta?.submission) {
|
38
|
+
field.meta = { ...(field.meta || {}), submission: {} };
|
39
|
+
}
|
40
|
+
if (!isEmpty(newValue)) {
|
41
|
+
field.meta.submission.newValue = newValue;
|
42
|
+
}
|
43
|
+
if (!isEmpty(voidedValue)) {
|
44
|
+
field.meta.submission.voidedValue = voidedValue;
|
45
|
+
}
|
46
|
+
return field.meta.submission.newValue;
|
47
|
+
}
|
48
|
+
|
49
|
+
export function hasSubmission(field: FormField) {
|
50
|
+
return !!field.meta.submission?.newValue || !!field.meta.submission?.voidedValue;
|
51
|
+
}
|
52
|
+
|
53
|
+
export function isViewMode(sessionMode: string) {
|
54
|
+
return sessionMode === 'view' || sessionMode === 'embedded-view';
|
55
|
+
}
|