@openmrs/esm-form-engine-lib 2.1.0-pre.1561 → 2.1.0-pre.1564

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "2.1.0-pre.1561",
3
+ "version": "2.1.0-pre.1564",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  import { type FormContextProps } from '../provider/form-provider';
2
2
  import { type FormField } from '../types';
3
- import { findObsByFormField, hasPreviousObsValueChanged, ObsAdapter } from './obs-adapter';
3
+ import { hasPreviousObsValueChanged, findObsByFormField, ObsAdapter } from './obs-adapter';
4
4
 
5
5
  const formContext = {
6
6
  methods: null,
@@ -944,280 +944,3 @@ describe('findObsByFormField', () => {
944
944
  expect(matchedObs[0]).toBe(obsList[3]);
945
945
  });
946
946
  });
947
-
948
- describe('ObsAdapter - handling nested obsGroups', () => {
949
- const createNestedFields = (): FormField => ({
950
- label: 'Parent obsGroup',
951
- type: 'obsGroup',
952
- required: false,
953
- id: 'parentObsgroup',
954
- questionOptions: {
955
- rendering: 'group',
956
- concept: '163770AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
957
- },
958
- questions: [
959
- {
960
- label: 'Health Center',
961
- type: 'obs',
962
- required: false,
963
- id: 'healthCenter',
964
- questionOptions: {
965
- rendering: 'select',
966
- concept: '1745AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
967
- answers: [
968
- {
969
- concept: '1560AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
970
- label: 'Family member',
971
- },
972
- {
973
- concept: '1588AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
974
- label: 'Health clinic/post',
975
- },
976
- {
977
- concept: '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
978
- label: 'Other',
979
- },
980
- ],
981
- },
982
- },
983
- {
984
- label: 'Nested obsGroup',
985
- type: 'obsGroup',
986
- required: false,
987
- id: 'nestedObsgroup',
988
- questionOptions: {
989
- rendering: 'group',
990
- concept: '3f824eeb-8452-4df0-b346-6ed056cbc5b9',
991
- },
992
- questions: [
993
- {
994
- label: 'Comment',
995
- type: 'obs',
996
- required: false,
997
- id: 'comment',
998
- questionOptions: {
999
- rendering: 'textarea',
1000
- concept: '161011AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1001
- },
1002
- },
1003
- {
1004
- label: 'Other Diagnoses',
1005
- type: 'obs',
1006
- required: false,
1007
- id: 'otherDiagnoses',
1008
- questionOptions: {
1009
- rendering: 'select',
1010
- concept: '159947AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1011
- answers: [
1012
- {
1013
- concept: '159394AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1014
- label: 'Diagnosis certainty',
1015
- },
1016
- ],
1017
- },
1018
- },
1019
- ],
1020
- },
1021
- ],
1022
- });
1023
-
1024
- const createNestedObs = () => ({
1025
- uuid: 'encounter-uuid',
1026
- obs: [
1027
- {
1028
- uuid: 'parent-group-uuid',
1029
- concept: {
1030
- uuid: '163770AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1031
- },
1032
- groupMembers: [
1033
- {
1034
- uuid: 'health-center-uuid',
1035
- concept: {
1036
- uuid: '1745AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1037
- },
1038
- value: {
1039
- uuid: '1588AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1040
- },
1041
- formFieldPath: 'rfe-forms-healthCenter',
1042
- },
1043
- {
1044
- uuid: 'nested-group-uuid',
1045
- concept: {
1046
- uuid: '3f824eeb-8452-4df0-b346-6ed056cbc5b9',
1047
- },
1048
- groupMembers: [
1049
- {
1050
- uuid: 'comment-uuid',
1051
- concept: {
1052
- uuid: '161011AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1053
- },
1054
- value: 'Test comment for nested group',
1055
- formFieldPath: 'rfe-forms-comment',
1056
- },
1057
- {
1058
- uuid: 'diagnosis-uuid',
1059
- concept: {
1060
- uuid: '159947AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1061
- },
1062
- value: {
1063
- uuid: '159394AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1064
- },
1065
- formFieldPath: 'rfe-forms-otherDiagnoses',
1066
- },
1067
- ],
1068
- },
1069
- ],
1070
- },
1071
- ],
1072
- });
1073
-
1074
- beforeEach(() => {
1075
- formContext.domainObjectValue = createNestedObs();
1076
- ObsAdapter.tearDown();
1077
- });
1078
-
1079
- it('should get initial values from nested obs groups', async () => {
1080
- const fields = createNestedFields();
1081
-
1082
- const healthCenterField = fields.questions[0];
1083
- const healthCenterValue = await ObsAdapter.getInitialValue(
1084
- healthCenterField,
1085
- formContext.domainObjectValue,
1086
- formContext,
1087
- );
1088
- expect(healthCenterValue).toBe('1588AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
1089
-
1090
- const commentField = fields.questions[1].questions[0];
1091
- const commentValue = await ObsAdapter.getInitialValue(commentField, formContext.domainObjectValue, formContext);
1092
- expect(commentValue).toBe('Test comment for nested group');
1093
-
1094
- const diagnosisField = fields.questions[1].questions[1];
1095
- const diagnosisValue = await ObsAdapter.getInitialValue(diagnosisField, formContext.domainObjectValue, formContext);
1096
- expect(diagnosisValue).toBe('159394AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
1097
- });
1098
-
1099
- it('should transform values in nested groups', () => {
1100
- const fields = createNestedFields();
1101
-
1102
- const healthCenterField = fields.questions[0];
1103
- const healthCenterObs = ObsAdapter.transformFieldValue(
1104
- healthCenterField,
1105
- '1560AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1106
- formContext,
1107
- );
1108
- expect(healthCenterObs).toEqual({
1109
- concept: '1745AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1110
- formFieldNamespace: 'rfe-forms',
1111
- formFieldPath: 'rfe-forms-healthCenter',
1112
- value: '1560AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1113
- });
1114
-
1115
- const commentField = fields.questions[1].questions[0];
1116
- const commentObs = ObsAdapter.transformFieldValue(commentField, 'New test comment', formContext);
1117
- expect(commentObs).toEqual({
1118
- concept: '161011AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1119
- formFieldNamespace: 'rfe-forms',
1120
- formFieldPath: 'rfe-forms-comment',
1121
- value: 'New test comment',
1122
- });
1123
- });
1124
-
1125
- it('should edit existing values in nested groups', () => {
1126
- formContext.sessionMode = 'edit';
1127
- const fields = createNestedFields();
1128
-
1129
- const healthCenterField = fields.questions[0];
1130
- healthCenterField.meta = {
1131
- previousValue: {
1132
- uuid: 'health-center-uuid',
1133
- value: {
1134
- uuid: '1588AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1135
- },
1136
- },
1137
- };
1138
-
1139
- const healthCenterObs = ObsAdapter.transformFieldValue(
1140
- healthCenterField,
1141
- '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1142
- formContext,
1143
- );
1144
-
1145
- expect(healthCenterObs).toEqual({
1146
- uuid: 'health-center-uuid',
1147
- formFieldNamespace: 'rfe-forms',
1148
- formFieldPath: 'rfe-forms-healthCenter',
1149
- value: '5622AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1150
- });
1151
-
1152
- const commentField = fields.questions[1].questions[0];
1153
- commentField.meta = {
1154
- previousValue: {
1155
- uuid: 'comment-uuid',
1156
- value: 'Test comment for nested group',
1157
- },
1158
- };
1159
-
1160
- const commentObs = ObsAdapter.transformFieldValue(commentField, 'Updated comment text', formContext);
1161
-
1162
- expect(commentObs).toEqual({
1163
- uuid: 'comment-uuid',
1164
- formFieldNamespace: 'rfe-forms',
1165
- formFieldPath: 'rfe-forms-comment',
1166
- value: 'Updated comment text',
1167
- });
1168
- });
1169
-
1170
- it('should void deleted values in nested groups', () => {
1171
- formContext.sessionMode = 'edit';
1172
- const fields = createNestedFields();
1173
-
1174
- const commentField = fields.questions[1].questions[0];
1175
- commentField.meta = {
1176
- previousValue: {
1177
- uuid: 'comment-uuid',
1178
- value: 'Test comment for nested group',
1179
- },
1180
- };
1181
-
1182
- ObsAdapter.transformFieldValue(commentField, '', formContext);
1183
- expect(commentField.meta.submission.voidedValue).toEqual({
1184
- uuid: 'comment-uuid',
1185
- voided: true,
1186
- });
1187
- expect(commentField.meta.submission.newValue).toBe(null);
1188
-
1189
- const diagnosisField = fields.questions[1].questions[1];
1190
- diagnosisField.meta = {
1191
- previousValue: {
1192
- uuid: 'diagnosis-uuid',
1193
- value: {
1194
- uuid: '159394AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1195
- },
1196
- },
1197
- };
1198
-
1199
- ObsAdapter.transformFieldValue(diagnosisField, null, formContext);
1200
- expect(diagnosisField.meta.submission.voidedValue).toEqual({
1201
- uuid: 'diagnosis-uuid',
1202
- voided: true,
1203
- });
1204
- expect(diagnosisField.meta.submission.newValue).toBe(null);
1205
- });
1206
-
1207
- it('should handle empty nested groups', async () => {
1208
- const emptyEncounter = {
1209
- uuid: 'encounter-uuid',
1210
- obs: [],
1211
- };
1212
-
1213
- const fields = createNestedFields();
1214
-
1215
- const healthCenterField = fields.questions[0];
1216
- const healthCenterValue = await ObsAdapter.getInitialValue(healthCenterField, emptyEncounter, formContext);
1217
- expect(healthCenterValue).toBe('');
1218
-
1219
- const commentField = fields.questions[1].questions[0];
1220
- const commentValue = await ObsAdapter.getInitialValue(commentField, emptyEncounter, formContext);
1221
- expect(commentValue).toBe('');
1222
- });
1223
- });
@@ -1,23 +1,23 @@
1
1
  import dayjs from 'dayjs';
2
- import { codedTypes, ConceptTrue } from '../constants';
2
+ import { ConceptTrue, codedTypes } from '../constants';
3
3
  import {
4
- type Attachment,
5
- type AttachmentResponse,
4
+ type OpenmrsObs,
6
5
  type FormField,
7
- type FormFieldValueAdapter,
8
6
  type OpenmrsEncounter,
9
- type OpenmrsObs,
7
+ type AttachmentResponse,
8
+ type Attachment,
10
9
  type ValueAndDisplay,
11
10
  } from '../types';
12
11
  import {
12
+ hasRendering,
13
+ gracefullySetSubmission,
13
14
  clearSubmission,
14
15
  flattenObsList,
15
- formatDateAsDisplayString,
16
- gracefullySetSubmission,
17
- hasRendering,
18
16
  parseToLocalDateTime,
17
+ formatDateAsDisplayString,
19
18
  } from '../utils/common-utils';
20
19
  import { type FormContextProps } from '../provider/form-provider';
20
+ import { type FormFieldValueAdapter } from '../types';
21
21
  import { isEmpty } from '../validators/form-validator';
22
22
  import { getAttachmentByUuid } from '../api';
23
23
  import { formatDate, restBaseUrl } from '@openmrs/esm-framework';
@@ -60,45 +60,40 @@ export const ObsAdapter: FormFieldValueAdapter = {
60
60
  if (isEmpty(value)) {
61
61
  return value;
62
62
  }
63
-
64
- if (Array.isArray(value)) {
65
- return value.map((val) => getDisplayValueForSingleObs(field, val)).join(', ');
63
+ if (value instanceof Date) {
64
+ return formatDateAsDisplayString(field, value);
66
65
  }
67
-
68
- return getDisplayValueForSingleObs(field, value);
66
+ if (rendering == 'checkbox') {
67
+ return value.map(
68
+ (selected) => field.questionOptions.answers?.find((option) => option.concept == selected)?.label,
69
+ );
70
+ }
71
+ if (rendering === 'toggle') {
72
+ return value ? field.questionOptions.toggleOptions.labelTrue : field.questionOptions.toggleOptions.labelFalse;
73
+ }
74
+ if (codedTypes.includes(rendering)) {
75
+ return field.questionOptions.answers?.find((option) => option.concept == value)?.label;
76
+ }
77
+ return value;
69
78
  },
70
79
  transformFieldValue: (field: FormField, value: any, context: FormContextProps) => {
71
80
  // clear previous submission
72
81
  clearSubmission(field);
73
-
74
82
  if (!field.meta.previousValue && isEmpty(value)) {
75
83
  return null;
76
84
  }
77
-
78
- // Handle `obsGroup` recursively
79
- if (field.type === 'obsGroup' && Array.isArray(field.questions)) {
80
- const groupObs = field.questions.map((nestedField) =>
81
- ObsAdapter.transformFieldValue(nestedField, nestedField.value, context),
82
- );
83
- return gracefullySetSubmission(field, { groupMembers: groupObs }, undefined);
84
- }
85
-
86
85
  if (hasRendering(field, 'checkbox')) {
87
86
  return handleMultiSelect(field, value);
88
87
  }
89
-
90
88
  if (!isEmpty(value) && hasPreviousObsValueChanged(field, value)) {
91
89
  return gracefullySetSubmission(field, editObs(field, value), undefined);
92
90
  }
93
-
94
91
  if (field.meta.previousValue && isEmpty(value)) {
95
92
  return gracefullySetSubmission(field, undefined, voidObs(field.meta.previousValue));
96
93
  }
97
-
98
94
  if (!isEmpty(value)) {
99
95
  return gracefullySetSubmission(field, constructObs(field, value), undefined);
100
96
  }
101
-
102
97
  return null;
103
98
  },
104
99
  tearDown: function (): void {
@@ -150,46 +145,22 @@ function extractFieldValue(field: FormField, obsList: OpenmrsObs[] = [], makeFie
150
145
  return '';
151
146
  }
152
147
 
153
- /**
154
- * Extracts field's display value
155
- */
156
-
157
- function getDisplayValueForSingleObs(field: FormField, value: any) {
158
- const rendering = field.questionOptions.rendering;
159
-
160
- if (value instanceof Date) {
161
- return formatDateAsDisplayString(field, value);
162
- }
163
- if (rendering == 'checkbox') {
164
- return value.map((selected) => field.questionOptions.answers?.find((option) => option.concept == selected)?.label);
165
- }
166
- if (rendering === 'toggle') {
167
- return value ? field.questionOptions.toggleOptions.labelTrue : field.questionOptions.toggleOptions.labelFalse;
168
- }
169
- if (codedTypes.includes(rendering)) {
170
- return field.questionOptions.answers?.find((option) => option.concept == value)?.label;
171
- }
172
- return value;
173
- }
174
-
175
148
  export function constructObs(field: FormField, value: any): Partial<OpenmrsObs> {
176
149
  if (isEmpty(value) && field.type !== 'obsGroup') {
177
150
  return null;
178
151
  }
179
-
180
- const draftObs: Partial<OpenmrsObs> = {
152
+ const draftObs =
153
+ field.type === 'obsGroup'
154
+ ? { groupMembers: [] }
155
+ : {
156
+ value: field.questionOptions.rendering.startsWith('date') ? formatDateByPickerType(field, value) : value,
157
+ };
158
+ return {
159
+ ...draftObs,
181
160
  concept: field.questionOptions.concept,
182
161
  formFieldNamespace: 'rfe-forms',
183
162
  formFieldPath: `rfe-forms-${field.id}`,
184
163
  };
185
-
186
- if (field.type === 'obsGroup') {
187
- draftObs.groupMembers = value?.map((v: any) => constructObs(field, v)) || [];
188
- } else {
189
- draftObs.value = field.questionOptions.rendering.startsWith('date') ? formatDateByPickerType(field, value) : value;
190
- }
191
-
192
- return draftObs;
193
164
  }
194
165
 
195
166
  export function voidObs(obs: OpenmrsObs) {
@@ -251,11 +222,6 @@ function handleMultiSelect(field: FormField, values: Array<string> = []) {
251
222
  // 2. a mix of both (previous and current)
252
223
  // 3. we only have a current value
253
224
 
254
- // For `obsGroup` types, handle nested groups
255
- if (field.type === 'obsGroup' && Array.isArray(field.questions)) {
256
- return field.questions.map((nestedField) => handleMultiSelect(nestedField, nestedField.value));
257
- }
258
-
259
225
  if (field.meta.previousValue && isEmpty(values)) {
260
226
  // we assume the user cleared the existing value(s)
261
227
  // so we void all previous values
@@ -265,7 +231,6 @@ function handleMultiSelect(field: FormField, values: Array<string> = []) {
265
231
  field.meta.previousValue.map((previousValue) => voidObs(previousValue)),
266
232
  );
267
233
  }
268
-
269
234
  if (field.meta.previousValue && !isEmpty(values)) {
270
235
  const toBeVoided = field.meta.previousValue.filter((obs) => !values.includes(obs.value.uuid));
271
236
  const toBeCreated = values.filter((v) => !field.meta.previousValue.some((obs) => obs.value.uuid === v));
@@ -275,7 +240,6 @@ function handleMultiSelect(field: FormField, values: Array<string> = []) {
275
240
  toBeVoided.map((obs) => voidObs(obs)),
276
241
  );
277
242
  }
278
-
279
243
  return gracefullySetSubmission(
280
244
  field,
281
245
  values.map((value) => constructObs(field, value)),
@@ -1,54 +1,29 @@
1
- import React, { useMemo } from 'react';
1
+ import React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import { type FormFieldInputProps } from '../../types';
4
4
  import styles from './obs-group.scss';
5
- import { FormFieldRenderer, isGroupField } from '../renderer/field/form-field-renderer.component';
5
+ import { FormFieldRenderer } from '../renderer/field/form-field-renderer.component';
6
6
  import { useFormProviderContext } from '../../provider/form-provider';
7
- import { FormGroup } from '@carbon/react';
8
- import { useTranslation } from 'react-i18next';
9
7
 
10
- export const ObsGroup: React.FC<FormFieldInputProps> = ({ field, ...restProps }) => {
11
- const { t } = useTranslation();
8
+ export const ObsGroup: React.FC<FormFieldInputProps> = ({ field }) => {
12
9
  const { formFieldAdapters } = useFormProviderContext();
13
- const showLabel = useMemo(() => field.questions?.length > 1, [field]);
14
10
 
15
- const content = useMemo(
16
- () =>
17
- field.questions
18
- ?.filter((child) => !child.isHidden)
19
- .map((child, index) => {
20
- const keyId = `${child.id}_${index}`;
11
+ const groupContent = field.questions
12
+ ?.filter((child) => !child.isHidden)
13
+ .map((child, index) => {
14
+ const keyId = child.id + '_' + index;
15
+ if (formFieldAdapters[child.type]) {
16
+ return (
17
+ <div className={classNames(styles.flexColumn)} key={keyId}>
18
+ <div className={styles.groupContainer}>
19
+ <FormFieldRenderer fieldId={child.id} valueAdapter={formFieldAdapters[child.type]} />
20
+ </div>
21
+ </div>
22
+ );
23
+ }
24
+ });
21
25
 
22
- if (child.type === 'obsGroup' && isGroupField(child.questionOptions.rendering)) {
23
- return (
24
- <div key={keyId} className={styles.nestedGroupContainer}>
25
- <ObsGroup field={child} {...restProps} />
26
- </div>
27
- );
28
- } else if (formFieldAdapters[child.type]) {
29
- return (
30
- <div className={classNames(styles.flexColumn)} key={keyId}>
31
- <div className={styles.groupContainer}>
32
- <FormFieldRenderer fieldId={child.id} valueAdapter={formFieldAdapters[child.type]} />
33
- </div>
34
- </div>
35
- );
36
- }
37
- }),
38
- [field],
39
- );
40
-
41
- return (
42
- <div className={styles.groupContainer}>
43
- {showLabel ? (
44
- <FormGroup legendText={t(field.label)} className={styles.boldLegend}>
45
- {content}
46
- </FormGroup>
47
- ) : (
48
- content
49
- )}
50
- </div>
51
- );
26
+ return <div className={styles.flexRow}>{groupContent}</div>;
52
27
  };
53
28
 
54
29
  export default ObsGroup;
@@ -10,7 +10,3 @@
10
10
  .groupContainer {
11
11
  margin: 0.5rem 0;
12
12
  }
13
-
14
- .boldLegend > legend {
15
- font-weight: bolder;
16
- }
@@ -282,6 +282,6 @@ export function isUnspecifiedSupported(question: FormField) {
282
282
  );
283
283
  }
284
284
 
285
- export function isGroupField(rendering: RenderType) {
285
+ function isGroupField(rendering: RenderType) {
286
286
  return rendering === 'group' || rendering === 'repeating';
287
287
  }
@@ -1,4 +1,5 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { FormGroup } from '@carbon/react';
2
3
  import { useTranslation } from 'react-i18next';
3
4
  import type { FormField, FormFieldInputProps, RenderType } from '../../types';
4
5
  import { evaluateAsyncExpression, evaluateExpression } from '../../utils/expression-runner';
@@ -77,7 +78,6 @@ const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
77
78
  });
78
79
  }
79
80
  }
80
-
81
81
  const clonedField = cloneRepeatField(field, null, counter);
82
82
  // run necessary expressions
83
83
  if (clonedField.type === 'obsGroup') {
@@ -168,7 +168,15 @@ const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
168
168
 
169
169
  return (
170
170
  <React.Fragment>
171
- <div>{nodes}</div>
171
+ {isGrouped ? (
172
+ <div className={styles.container}>
173
+ <FormGroup legendText={t(field.label)} className={styles.boldLegend}>
174
+ {nodes}
175
+ </FormGroup>
176
+ </div>
177
+ ) : (
178
+ <div>{nodes}</div>
179
+ )}
172
180
  </React.Fragment>
173
181
  );
174
182
  };
@@ -1,45 +1,35 @@
1
1
  import { useMemo } from 'react';
2
- import { type FormField, type FormSchema } from '../types';
2
+ import { type FormSchema, type FormField } from '../types';
3
3
 
4
4
  export function useFormFields(form: FormSchema): { formFields: FormField[]; conceptReferences: Set<string> } {
5
5
  const [flattenedFields, conceptReferences] = useMemo(() => {
6
- const flattenedFieldsTemp: FormField[] = [];
6
+ const flattenedFieldsTemp = [];
7
7
  const conceptReferencesTemp = new Set<string>();
8
-
9
- const flattenFields = (fields: FormField[]) => {
10
- fields.forEach((field) => {
11
- flattenedFieldsTemp.push(field);
12
-
13
- // If the field is an obsGroup, we need to flatten its nested questions
14
- if (field.type === 'obsGroup' && field.questions) {
15
- field.questions.forEach((groupedField) => {
16
- groupedField.meta.groupId = field.id;
17
- flattenFields([groupedField]);
18
- });
19
- }
20
-
21
- // Collect concept references
22
- if (field.questionOptions?.concept) {
23
- conceptReferencesTemp.add(field.questionOptions.concept);
24
- }
25
- if (field.questionOptions?.answers) {
26
- field.questionOptions.answers.forEach((answer) => {
27
- if (answer.concept) {
28
- conceptReferencesTemp.add(answer.concept);
29
- }
30
- });
31
- }
32
- });
33
- };
34
-
35
8
  form.pages?.forEach((page) =>
36
9
  page.sections?.forEach((section) => {
37
- if (section.questions) {
38
- flattenFields(section.questions);
39
- }
10
+ section.questions?.forEach((question) => {
11
+ flattenedFieldsTemp.push(question);
12
+ if (question.type == 'obsGroup') {
13
+ question.questions.forEach((groupedField) => {
14
+ groupedField.meta.groupId = question.id;
15
+ flattenedFieldsTemp.push(groupedField);
16
+ });
17
+ }
18
+ });
40
19
  }),
41
20
  );
42
-
21
+ flattenedFieldsTemp.forEach((field) => {
22
+ if (field.questionOptions?.concept) {
23
+ conceptReferencesTemp.add(field.questionOptions.concept);
24
+ }
25
+ if (field.questionOptions?.answers) {
26
+ field.questionOptions.answers.forEach((answer) => {
27
+ if (answer.concept) {
28
+ conceptReferencesTemp.add(answer.concept);
29
+ }
30
+ });
31
+ }
32
+ });
43
33
  return [flattenedFieldsTemp, conceptReferencesTemp];
44
34
  }, [form]);
45
35
 
@@ -100,7 +100,7 @@ export class EncounterFormProcessor extends FormProcessor {
100
100
  field.meta.fixedValue = field.value;
101
101
  delete field.value;
102
102
  }
103
- if (field.questionOptions?.rendering == 'group' || field.type === 'obsGroup') {
103
+ if (field.questionOptions?.rendering == 'group') {
104
104
  field.questions?.forEach((child) => {
105
105
  child.readonly = child.readonly ?? field.readonly;
106
106
  return prepareFormField(child, section, page, schema);
@@ -1,16 +1,16 @@
1
1
  import {
2
+ type PatientProgram,
2
3
  type FormField,
3
- type FormProcessorContextProps,
4
4
  type OpenmrsEncounter,
5
5
  type OpenmrsObs,
6
6
  type PatientIdentifier,
7
- type PatientProgram,
8
7
  type PatientProgramPayload,
8
+ type FormProcessorContextProps,
9
9
  } from '../../types';
10
10
  import { saveAttachment, savePatientIdentifier, saveProgramEnrollment } from '../../api';
11
11
  import { hasRendering, hasSubmission } from '../../utils/common-utils';
12
12
  import dayjs from 'dayjs';
13
- import { assignedObsIds, constructObs, voidObs } from '../../adapters/obs-adapter';
13
+ import { voidObs, constructObs, assignedObsIds } from '../../adapters/obs-adapter';
14
14
  import { type FormContextProps } from '../../provider/form-provider';
15
15
  import { ConceptTrue } from '../../constants';
16
16
  import { DefaultValueValidator } from '../../validators/default-value-validator';
@@ -185,53 +185,43 @@ export function getMutableSessionProps(context: FormContextProps) {
185
185
  // Helpers
186
186
 
187
187
  function prepareObs(obsForSubmission: OpenmrsObs[], fields: FormField[]) {
188
- fields.filter((field) => hasSubmittableObs(field)).forEach((field) => processObsField(obsForSubmission, field));
189
- }
190
-
191
- function processObsField(obsForSubmission: OpenmrsObs[], field: FormField) {
192
- if ((field.isHidden || field.isParentHidden) && field.meta.previousValue) {
193
- const valuesArray = Array.isArray(field.meta.previousValue) ? field.meta.previousValue : [field.meta.previousValue];
194
- addObsToList(
195
- obsForSubmission,
196
- valuesArray.map((obs) => voidObs(obs)),
197
- );
198
- return;
199
- }
200
-
201
- if (field.type === 'obsGroup') {
202
- processObsGroup(obsForSubmission, field);
203
- } else if (hasSubmission(field)) {
204
- // For non-group obs with a submission
205
- addObsToList(obsForSubmission, field.meta.submission.newValue);
206
- addObsToList(obsForSubmission, field.meta.submission.voidedValue);
207
- }
208
- }
209
-
210
- function processObsGroup(obsForSubmission: OpenmrsObs[], groupField: FormField) {
211
- if (groupField.meta.submission?.voidedValue) {
212
- addObsToList(obsForSubmission, groupField.meta.submission.voidedValue);
213
- return;
214
- }
215
-
216
- const obsGroup = constructObs(groupField, null);
217
- if (groupField.meta.previousValue) {
218
- obsGroup.uuid = groupField.meta.previousValue.uuid;
219
- }
220
-
221
- groupField.questions.forEach((nestedField) => {
222
- if (nestedField.type === 'obsGroup') {
223
- const nestedObsGroup: OpenmrsObs[] = [];
224
- processObsGroup(nestedObsGroup, nestedField);
225
- addObsToList(obsGroup.groupMembers, nestedObsGroup);
226
- } else if (hasSubmission(nestedField)) {
227
- addObsToList(obsGroup.groupMembers, nestedField.meta.submission.newValue);
228
- addObsToList(obsGroup.groupMembers, nestedField.meta.submission.voidedValue);
229
- }
230
- });
231
-
232
- if (obsGroup.groupMembers?.length || obsGroup.voided) {
233
- addObsToList(obsForSubmission, obsGroup);
234
- }
188
+ fields
189
+ .filter((field) => hasSubmittableObs(field))
190
+ .forEach((field) => {
191
+ if ((field.isHidden || field.isParentHidden) && field.meta.previousValue) {
192
+ const valuesArray = Array.isArray(field.meta.previousValue)
193
+ ? field.meta.previousValue
194
+ : [field.meta.previousValue];
195
+ addObsToList(
196
+ obsForSubmission,
197
+ valuesArray.map((obs) => voidObs(obs)),
198
+ );
199
+ return;
200
+ }
201
+ if (field.type == 'obsGroup') {
202
+ if (field.meta.submission?.voidedValue) {
203
+ addObsToList(obsForSubmission, field.meta.submission.voidedValue);
204
+ return;
205
+ }
206
+ const obsGroup = constructObs(field, null);
207
+ if (field.meta.previousValue) {
208
+ obsGroup.uuid = field.meta.previousValue.uuid;
209
+ }
210
+ field.questions.forEach((groupedField) => {
211
+ if (hasSubmission(groupedField)) {
212
+ addObsToList(obsGroup.groupMembers, groupedField.meta.submission.newValue);
213
+ addObsToList(obsGroup.groupMembers, groupedField.meta.submission.voidedValue);
214
+ }
215
+ });
216
+ if (obsGroup.groupMembers.length || obsGroup.voided) {
217
+ addObsToList(obsForSubmission, obsGroup);
218
+ }
219
+ }
220
+ if (hasSubmission(field)) {
221
+ addObsToList(obsForSubmission, field.meta.submission.newValue);
222
+ addObsToList(obsForSubmission, field.meta.submission.voidedValue);
223
+ }
224
+ });
235
225
  }
236
226
 
237
227
  function prepareOrders(fields: FormField[]) {
@@ -1,4 +1,4 @@
1
- import { type FormField, type FormSchema, type FormSchemaTransformer, type RenderType } from '../types';
1
+ import { type FormField, type FormSchemaTransformer, type FormSchema, type RenderType } from '../types';
2
2
  import { isTrue } from '../utils/boolean-utils';
3
3
  import { hasRendering } from '../utils/common-utils';
4
4
 
@@ -39,13 +39,8 @@ function handleQuestion(question: FormField, form: FormSchema) {
39
39
  setFieldValidators(question);
40
40
  transformByType(question);
41
41
  transformByRendering(question);
42
-
43
- if (question.questions?.length) {
44
- if (question.type === 'obsGroup' && question.questions.length) {
45
- question.questions.forEach((nestedQuestion) => handleQuestion(nestedQuestion, form));
46
- } else {
47
- question.questions.forEach((nestedQuestion) => handleQuestion(nestedQuestion, form));
48
- }
42
+ if (question?.questions?.length) {
43
+ question.questions.forEach((question) => handleQuestion(question, form));
49
44
  }
50
45
  } catch (error) {
51
46
  console.error(error);