@openmrs/esm-form-engine-lib 2.1.0-pre.1604 → 3.0.0-pre.1607

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.
@@ -26,7 +26,6 @@
26
26
  }
27
27
  }
28
28
  },
29
- "meta": {},
30
29
  "id": "patient_transfer_location"
31
30
  },
32
31
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "2.1.0-pre.1604",
3
+ "version": "3.0.0-pre.1607",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -18,8 +18,8 @@ export const InlineDateAdapter: FormFieldValueAdapter = {
18
18
  } else {
19
19
  targetField.meta.submission.newValue.obsDatetime = dateString;
20
20
  }
21
- } else if (!hasSubmission(targetField) && targetField.meta.previousValue) {
22
- if (isEmpty(value) && isEmpty(targetField.meta.previousValue.obsDatetime)) {
21
+ } else if (!hasSubmission(targetField) && targetField.meta.initialValue?.omrsObject) {
22
+ if (isEmpty(value) && isEmpty((targetField.meta.initialValue.omrsObject as OpenmrsResource)?.obsDatetime)) {
23
23
  return null;
24
24
  }
25
25
  // generate submission
@@ -37,8 +37,9 @@ export const InlineDateAdapter: FormFieldValueAdapter = {
37
37
  if (encounter) {
38
38
  const targetFieldId = field.id.split('_inline_date')[0];
39
39
  const targetField = context.formFields.find((field) => field.id === targetFieldId);
40
- if (targetField?.meta.previousValue?.obsDatetime) {
41
- return parseDate(targetField?.meta.previousValue?.obsDatetime);
40
+ const targetFieldInitialObs = targetField?.meta.initialValue?.omrsObject as OpenmrsResource;
41
+ if (targetFieldInitialObs?.obsDatetime) {
42
+ return parseDate(targetFieldInitialObs.obsDatetime);
42
43
  }
43
44
  }
44
45
  return null;
@@ -198,15 +198,17 @@ describe('ObsAdapter - transformFieldValue', () => {
198
198
  concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
199
199
  },
200
200
  meta: {
201
- previousValue: {
202
- uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
203
- person: '833db896-c1f0-11eb-8529-0242ac130003',
204
- concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
205
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
206
- order: null,
207
- groupMembers: [],
208
- voided: false,
209
- value: 'Can be discharged in next visit',
201
+ initialValue: {
202
+ omrsObject: {
203
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
204
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
205
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
206
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
207
+ order: null,
208
+ groupMembers: [],
209
+ voided: false,
210
+ value: 'Can be discharged in next visit',
211
+ },
210
212
  },
211
213
  },
212
214
  id: 'visit-note',
@@ -236,15 +238,17 @@ describe('ObsAdapter - transformFieldValue', () => {
236
238
  concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
237
239
  },
238
240
  meta: {
239
- previousValue: {
240
- uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
241
- person: '833db896-c1f0-11eb-8529-0242ac130003',
242
- concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
243
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
244
- order: null,
245
- groupMembers: [],
246
- voided: false,
247
- value: '5197ca4f-f0f7-4e63-9a68-8614224dce44',
241
+ initialValue: {
242
+ omrsObject: {
243
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
244
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
245
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
246
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
247
+ order: null,
248
+ groupMembers: [],
249
+ voided: false,
250
+ value: '5197ca4f-f0f7-4e63-9a68-8614224dce44',
251
+ },
248
252
  },
249
253
  },
250
254
  id: 'hts-result',
@@ -276,22 +280,24 @@ describe('ObsAdapter - transformFieldValue', () => {
276
280
  ],
277
281
  },
278
282
  meta: {
279
- previousValue: [
280
- {
281
- uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
282
- person: '833db896-c1f0-11eb-8529-0242ac130003',
283
- concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
284
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
285
- order: null,
286
- groupMembers: [],
287
- voided: false,
288
- formFieldNamespace: 'rfe-forms',
289
- formFieldPath: 'rfe-forms-past-patient-programs',
290
- value: {
291
- uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
283
+ initialValue: {
284
+ omrsObject: [
285
+ {
286
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
287
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
288
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
289
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
290
+ order: null,
291
+ groupMembers: [],
292
+ voided: false,
293
+ formFieldNamespace: 'rfe-forms',
294
+ formFieldPath: 'rfe-forms-past-patient-programs',
295
+ value: {
296
+ uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
297
+ },
292
298
  },
293
- },
294
- ],
299
+ ],
300
+ },
295
301
  },
296
302
  id: 'past-patient-programs',
297
303
  };
@@ -327,15 +333,17 @@ describe('ObsAdapter - transformFieldValue', () => {
327
333
  concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
328
334
  },
329
335
  meta: {
330
- previousValue: {
331
- uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
332
- person: '833db896-c1f0-11eb-8529-0242ac130003',
333
- concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
334
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
335
- order: null,
336
- groupMembers: [],
337
- voided: false,
338
- value: new Date(2020, 11, 16),
336
+ initialValue: {
337
+ omrsObject: {
338
+ uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
339
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
340
+ concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
341
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
342
+ order: null,
343
+ groupMembers: [],
344
+ voided: false,
345
+ value: new Date(2020, 11, 16),
346
+ },
339
347
  },
340
348
  },
341
349
  id: 'hts-date',
@@ -365,15 +373,17 @@ describe('ObsAdapter - transformFieldValue', () => {
365
373
  concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
366
374
  },
367
375
  meta: {
368
- previousValue: {
369
- uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
370
- person: '833db896-c1f0-11eb-8529-0242ac130003',
371
- concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
372
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
373
- order: null,
374
- groupMembers: [],
375
- voided: false,
376
- value: 'Can be discharged in next visit',
376
+ initialValue: {
377
+ omrsObject: {
378
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
379
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
380
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
381
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
382
+ order: null,
383
+ groupMembers: [],
384
+ voided: false,
385
+ value: 'Can be discharged in next visit',
386
+ },
377
387
  },
378
388
  },
379
389
  id: 'visit-note',
@@ -401,15 +411,17 @@ describe('ObsAdapter - transformFieldValue', () => {
401
411
  concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
402
412
  },
403
413
  meta: {
404
- previousValue: {
405
- uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
406
- person: '833db896-c1f0-11eb-8529-0242ac130003',
407
- concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
408
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
409
- order: null,
410
- groupMembers: [],
411
- voided: false,
412
- value: '5197ca4f-f0f7-4e63-9a68-8614224dce44',
414
+ initialValue: {
415
+ omrsObject: {
416
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
417
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
418
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
419
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
420
+ order: null,
421
+ groupMembers: [],
422
+ voided: false,
423
+ value: '5197ca4f-f0f7-4e63-9a68-8614224dce44',
424
+ },
413
425
  },
414
426
  },
415
427
  id: 'hts-result',
@@ -435,20 +447,22 @@ describe('ObsAdapter - transformFieldValue', () => {
435
447
  answers: [{ label: 'Option 1', concept: '105e7ad6-c1fd-11eb-8529-0242ac130ju9' }],
436
448
  },
437
449
  meta: {
438
- previousValue: [
439
- {
440
- uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
441
- person: '833db896-c1f0-11eb-8529-0242ac130003',
442
- concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
443
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
444
- order: null,
445
- groupMembers: [],
446
- voided: false,
447
- value: {
448
- uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
450
+ initialValue: {
451
+ omrsObject: [
452
+ {
453
+ uuid: 'f2487de5-e55f-4689-8791-0c919179818b',
454
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
455
+ concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
456
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
457
+ order: null,
458
+ groupMembers: [],
459
+ voided: false,
460
+ value: {
461
+ uuid: '105e7ad6-c1fd-11eb-8529-0242ac130ju9',
462
+ },
449
463
  },
450
- },
451
- ],
464
+ ],
465
+ },
452
466
  },
453
467
  id: 'past-patient-programs',
454
468
  };
@@ -479,15 +493,17 @@ describe('ObsAdapter - transformFieldValue', () => {
479
493
  concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
480
494
  },
481
495
  meta: {
482
- previousValue: {
483
- uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
484
- person: '833db896-c1f0-11eb-8529-0242ac130003',
485
- concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
486
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
487
- order: null,
488
- groupMembers: [],
489
- voided: false,
490
- value: htsDate,
496
+ initialValue: {
497
+ omrsObject: {
498
+ uuid: 'bca7277f-a726-4d3d-9db8-40937228ead5',
499
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
500
+ concept: '3e432ad5-7b19-4866-a68f-abf0d9f52a01',
501
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
502
+ order: null,
503
+ groupMembers: [],
504
+ voided: false,
505
+ value: htsDate,
506
+ },
491
507
  },
492
508
  },
493
509
  id: 'hts-date',
@@ -790,9 +806,11 @@ describe('hasPreviousObsValueChanged', () => {
790
806
  rendering: 'radio',
791
807
  },
792
808
  meta: {
793
- previousValue: {
794
- value: {
795
- uuid: '1065AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
809
+ initialValue: {
810
+ omrsObject: {
811
+ value: {
812
+ uuid: '1065AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
813
+ },
796
814
  },
797
815
  },
798
816
  },
@@ -808,8 +826,10 @@ describe('hasPreviousObsValueChanged', () => {
808
826
  rendering: 'date',
809
827
  },
810
828
  meta: {
811
- previousValue: {
812
- value: '2024-05-01T19:49:50.000+0000',
829
+ initialValue: {
830
+ omrsObject: {
831
+ value: '2024-05-01T19:49:50.000+0000',
832
+ },
813
833
  },
814
834
  },
815
835
  } as any as FormField;
@@ -823,8 +843,10 @@ describe('hasPreviousObsValueChanged', () => {
823
843
  rendering: 'datetime',
824
844
  },
825
845
  meta: {
826
- previousValue: {
827
- value: '2024-04-01T19:50:00.000+0000',
846
+ initialValue: {
847
+ omrsObject: {
848
+ value: '2024-04-01T19:50:00.000+0000',
849
+ },
828
850
  },
829
851
  },
830
852
  } as any as FormField;
@@ -837,8 +859,10 @@ describe('hasPreviousObsValueChanged', () => {
837
859
  rendering: 'text',
838
860
  },
839
861
  meta: {
840
- previousValue: {
841
- value: 'Text value',
862
+ initialValue: {
863
+ omrsObject: {
864
+ value: 'Text value',
865
+ },
842
866
  },
843
867
  },
844
868
  } as any as FormField;
@@ -1128,10 +1152,12 @@ describe('ObsAdapter - handling nested obsGroups', () => {
1128
1152
 
1129
1153
  const healthCenterField = fields.questions[0];
1130
1154
  healthCenterField.meta = {
1131
- previousValue: {
1132
- uuid: 'health-center-uuid',
1133
- value: {
1134
- uuid: '1588AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1155
+ initialValue: {
1156
+ omrsObject: {
1157
+ uuid: 'health-center-uuid',
1158
+ value: {
1159
+ uuid: '1588AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1160
+ },
1135
1161
  },
1136
1162
  },
1137
1163
  };
@@ -1151,9 +1177,11 @@ describe('ObsAdapter - handling nested obsGroups', () => {
1151
1177
 
1152
1178
  const commentField = fields.questions[1].questions[0];
1153
1179
  commentField.meta = {
1154
- previousValue: {
1155
- uuid: 'comment-uuid',
1156
- value: 'Test comment for nested group',
1180
+ initialValue: {
1181
+ omrsObject: {
1182
+ uuid: 'comment-uuid',
1183
+ value: 'Test comment for nested group',
1184
+ },
1157
1185
  },
1158
1186
  };
1159
1187
 
@@ -1173,9 +1201,11 @@ describe('ObsAdapter - handling nested obsGroups', () => {
1173
1201
 
1174
1202
  const commentField = fields.questions[1].questions[0];
1175
1203
  commentField.meta = {
1176
- previousValue: {
1177
- uuid: 'comment-uuid',
1178
- value: 'Test comment for nested group',
1204
+ initialValue: {
1205
+ omrsObject: {
1206
+ uuid: 'comment-uuid',
1207
+ value: 'Test comment for nested group',
1208
+ },
1179
1209
  },
1180
1210
  };
1181
1211
 
@@ -1188,10 +1218,12 @@ describe('ObsAdapter - handling nested obsGroups', () => {
1188
1218
 
1189
1219
  const diagnosisField = fields.questions[1].questions[1];
1190
1220
  diagnosisField.meta = {
1191
- previousValue: {
1192
- uuid: 'diagnosis-uuid',
1193
- value: {
1194
- uuid: '159394AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1221
+ initialValue: {
1222
+ omrsObject: {
1223
+ uuid: 'diagnosis-uuid',
1224
+ value: {
1225
+ uuid: '159394AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
1226
+ },
1195
1227
  },
1196
1228
  },
1197
1229
  };
@@ -20,7 +20,7 @@ import {
20
20
  import { type FormContextProps } from '../provider/form-provider';
21
21
  import { isEmpty } from '../validators/form-validator';
22
22
  import { getAttachmentByUuid } from '../api';
23
- import { formatDate, restBaseUrl } from '@openmrs/esm-framework';
23
+ import { formatDate, type OpenmrsResource, restBaseUrl } from '@openmrs/esm-framework';
24
24
 
25
25
  // Temporarily holds observations that have already been bound with matching fields
26
26
  export let assignedObsIds: string[] = [];
@@ -63,7 +63,7 @@ export const ObsAdapter: FormFieldValueAdapter = {
63
63
  if (value instanceof Date) {
64
64
  return formatDateAsDisplayString(field, value);
65
65
  }
66
- if (rendering == 'checkbox') {
66
+ if (rendering === 'checkbox') {
67
67
  return value.map(
68
68
  (selected) => field.questionOptions.answers?.find((option) => option.concept == selected)?.label,
69
69
  );
@@ -79,7 +79,7 @@ export const ObsAdapter: FormFieldValueAdapter = {
79
79
  transformFieldValue: (field: FormField, value: any, context: FormContextProps) => {
80
80
  // clear previous submission
81
81
  clearSubmission(field);
82
- if (!field.meta.previousValue && isEmpty(value)) {
82
+ if (!field.meta.initialValue?.omrsObject && isEmpty(value)) {
83
83
  return null;
84
84
  }
85
85
  if (hasRendering(field, 'checkbox')) {
@@ -88,8 +88,8 @@ export const ObsAdapter: FormFieldValueAdapter = {
88
88
  if (!isEmpty(value) && hasPreviousObsValueChanged(field, value)) {
89
89
  return gracefullySetSubmission(field, editObs(field, value), undefined);
90
90
  }
91
- if (field.meta.previousValue && isEmpty(value)) {
92
- return gracefullySetSubmission(field, undefined, voidObs(field.meta.previousValue));
91
+ if (field.meta.initialValue?.omrsObject && isEmpty(value)) {
92
+ return gracefullySetSubmission(field, undefined, voidObs(field.meta.initialValue.omrsObject as OpenmrsObs));
93
93
  }
94
94
  if (!isEmpty(value)) {
95
95
  return gracefullySetSubmission(field, constructObs(field, value), undefined);
@@ -110,25 +110,29 @@ function extractFieldValue(field: FormField, obsList: OpenmrsObs[] = [], makeFie
110
110
  const rendering = field.questionOptions.rendering;
111
111
  if (!field.meta) {
112
112
  field.meta = {
113
- previousValue: null,
113
+ initialValue: {
114
+ omrsObject: null,
115
+ refinedValue: null,
116
+ },
114
117
  };
115
118
  }
116
119
  if (obsList.length) {
117
120
  if (rendering == 'checkbox') {
118
121
  assignedObsIds.push(...obsList.map((obs) => obs.uuid));
119
- field.meta.previousValue = makeFieldDirty ? obsList : null;
122
+ field.meta.initialValue.omrsObject = makeFieldDirty ? obsList : null;
120
123
  return obsList.map((o) => o.value.uuid);
121
124
  }
122
125
  const obs = obsList[0];
123
126
  if (makeFieldDirty) {
124
- field.meta.previousValue = { ...obs };
127
+ field.meta.initialValue.omrsObject = { ...obs };
125
128
  }
126
129
  assignedObsIds.push(obs.uuid);
127
130
  if (typeof obs.value === 'string' || typeof obs.value === 'number') {
128
131
  if (rendering.startsWith('date')) {
129
132
  const dateObject = parseToLocalDateTime(obs.value as string);
130
133
  if (makeFieldDirty) {
131
- field.meta.previousValue.value = dayjs(dateObject).format('YYYY-MM-DD HH:mm');
134
+ const obsObject = field.meta.initialValue.omrsObject as OpenmrsObs;
135
+ obsObject.value = dayjs(dateObject).format('YYYY-MM-DD HH:mm');
132
136
  }
133
137
  return dateObject;
134
138
  }
@@ -168,7 +172,7 @@ export function voidObs(obs: OpenmrsObs) {
168
172
  }
169
173
 
170
174
  export function editObs(field: FormField, newValue: any) {
171
- const oldObs = field.meta.previousValue;
175
+ const oldObs = field.meta.initialValue?.omrsObject as OpenmrsResource;
172
176
  const formattedValue = field.questionOptions.rendering.startsWith('date')
173
177
  ? formatDateByPickerType(field, newValue)
174
178
  : newValue;
@@ -197,7 +201,7 @@ function formatDateByPickerType(field: FormField, value: Date) {
197
201
  }
198
202
 
199
203
  export function hasPreviousObsValueChanged(field: FormField, newValue: any) {
200
- const previousObs = field.meta.previousValue;
204
+ const previousObs = field.meta.initialValue?.omrsObject as OpenmrsResource;
201
205
  if (isEmpty(previousObs)) {
202
206
  return false;
203
207
  }
@@ -221,19 +225,19 @@ function handleMultiSelect(field: FormField, values: Array<string> = []) {
221
225
  // 1. we have a previous value and an empty current value
222
226
  // 2. a mix of both (previous and current)
223
227
  // 3. we only have a current value
224
-
225
- if (field.meta.previousValue && isEmpty(values)) {
228
+ const obsArray = field.meta.initialValue?.omrsObject as Array<OpenmrsResource>;
229
+ if (obsArray?.length && isEmpty(values)) {
226
230
  // we assume the user cleared the existing value(s)
227
231
  // so we void all previous values
228
232
  return gracefullySetSubmission(
229
233
  field,
230
234
  null,
231
- field.meta.previousValue.map((previousValue) => voidObs(previousValue)),
235
+ obsArray.map((previousValue) => voidObs(previousValue)),
232
236
  );
233
237
  }
234
- if (field.meta.previousValue && !isEmpty(values)) {
235
- const toBeVoided = field.meta.previousValue.filter((obs) => !values.includes(obs.value.uuid));
236
- const toBeCreated = values.filter((v) => !field.meta.previousValue.some((obs) => obs.value.uuid === v));
238
+ if (obsArray?.length && !isEmpty(values)) {
239
+ const toBeVoided = obsArray.filter((obs) => !values.includes(obs.value.uuid));
240
+ const toBeCreated = values.filter((v) => !obsArray.some((obs) => obs.value.uuid === v));
237
241
  return gracefullySetSubmission(
238
242
  field,
239
243
  toBeCreated.map((value) => constructObs(field, value)),
@@ -9,6 +9,7 @@ export const ObsCommentAdapter: FormFieldValueAdapter = {
9
9
  transformFieldValue: function (field: FormField, value: any, context: FormContextProps) {
10
10
  const targetField = context.getFormField(field.meta.targetField);
11
11
  const targetFieldCurrentValue = context.methods.getValues(targetField.id);
12
+ const targetFieldInitialObs = targetField.meta.initialValue?.omrsObject as OpenmrsResource;
12
13
 
13
14
  if (targetField.meta.submission?.newValue) {
14
15
  if (isEmpty(value) && !isNewSubmissionEffective(targetField, targetFieldCurrentValue)) {
@@ -17,8 +18,8 @@ export const ObsCommentAdapter: FormFieldValueAdapter = {
17
18
  } else {
18
19
  targetField.meta.submission.newValue.comment = value;
19
20
  }
20
- } else if (!hasSubmission(targetField) && targetField.meta.previousValue) {
21
- if (isEmpty(value) && isEmpty(targetField.meta.previousValue.comment)) {
21
+ } else if (!hasSubmission(targetField) && targetFieldInitialObs) {
22
+ if (isEmpty(value) && isEmpty(targetFieldInitialObs.comment)) {
22
23
  return null;
23
24
  }
24
25
  // generate submission
@@ -37,7 +38,7 @@ export const ObsCommentAdapter: FormFieldValueAdapter = {
37
38
  if (encounter) {
38
39
  const targetFieldId = field.id.split('_obs_comment')[0];
39
40
  const targetField = context.formFields.find((field) => field.id === targetFieldId);
40
- return targetField?.meta.previousValue?.comment;
41
+ return (targetField?.meta.initialValue.omrsObject as OpenmrsResource)?.comment;
41
42
  }
42
43
  return null;
43
44
  },
@@ -10,7 +10,7 @@ const defaultCareSetting = '6f0c9a92-6f24-11e3-af88-005056821db0';
10
10
 
11
11
  export const OrdersAdapter: FormFieldValueAdapter = {
12
12
  transformFieldValue: function (field: FormField, value: any, context: FormContextProps) {
13
- if (context.sessionMode == 'edit' && field.meta?.previousValue?.uuid) {
13
+ if (context.sessionMode == 'edit' && field.meta.initialValue?.omrsObject) {
14
14
  return editOrder(value, field, context.currentProvider.uuid);
15
15
  }
16
16
  const newValue = constructNewOrder(value, field, context.currentProvider.uuid);
@@ -27,7 +27,13 @@ export const OrdersAdapter: FormFieldValueAdapter = {
27
27
  .filter((order) => !assignedOrderIds.includes(order.uuid) && !order.voided)
28
28
  .find((order) => availableOrderables.includes(order.concept.uuid));
29
29
  if (matchedOrder) {
30
- field.meta = { ...(field.meta || {}), previousValue: matchedOrder };
30
+ field.meta = {
31
+ ...(field.meta || {}),
32
+ initialValue: {
33
+ omrsObject: matchedOrder,
34
+ refinedValue: matchedOrder.concept.uuid,
35
+ },
36
+ };
31
37
  assignedOrderIds.push(matchedOrder.uuid);
32
38
  return matchedOrder.concept.uuid;
33
39
  }
@@ -62,12 +68,13 @@ function constructNewOrder(value: any, field: FormField, orderer: string) {
62
68
  }
63
69
 
64
70
  function editOrder(newOrder: any, field: FormField, orderer: string) {
65
- if (newOrder === field.meta.previousValue?.concept?.uuid) {
71
+ const previousOrder = field.meta.initialValue?.omrsObject as OpenmrsResource;
72
+ if (newOrder === previousOrder?.concept?.uuid) {
66
73
  clearSubmission(field);
67
74
  return null;
68
75
  }
69
76
  const voided = {
70
- uuid: field.meta.previousValue?.uuid,
77
+ uuid: previousOrder?.uuid,
71
78
  voided: true,
72
79
  };
73
80
  gracefullySetSubmission(field, constructNewOrder(newOrder, field, orderer), voided);
@@ -7,13 +7,13 @@ import { isEmpty } from '../validators/form-validator';
7
7
  export const PatientIdentifierAdapter: FormFieldValueAdapter = {
8
8
  transformFieldValue: function (field: FormField, value: any, context: FormContextProps) {
9
9
  clearSubmission(field);
10
- if (field.meta?.previousValue?.value === value || isEmpty(value)) {
10
+ if (field.meta.initialValue?.refinedValue === value || isEmpty(value)) {
11
11
  return null;
12
12
  }
13
13
  field.meta.submission.newValue = {
14
14
  identifier: value,
15
15
  identifierType: field.questionOptions.identifierType,
16
- uuid: field.meta.previousValue?.id,
16
+ uuid: (field.meta.initialValue?.omrsObject as OpenmrsResource)?.id,
17
17
  location: context.location,
18
18
  };
19
19
  return field.meta.submission.newValue;
@@ -22,7 +22,13 @@ export const PatientIdentifierAdapter: FormFieldValueAdapter = {
22
22
  const latestIdentifier = context.patient?.identifier?.find(
23
23
  (identifier) => identifier.type?.coding[0]?.code === field.questionOptions.identifierType,
24
24
  );
25
- field.meta = { ...(field.meta || {}), previousValue: latestIdentifier };
25
+ field.meta = {
26
+ ...(field.meta || {}),
27
+ initialValue: {
28
+ omrsObject: latestIdentifier as any,
29
+ refinedValue: latestIdentifier?.value,
30
+ },
31
+ };
26
32
  return latestIdentifier?.value;
27
33
  },
28
34
  getPreviousValue: function (field: FormField, sourceObject: OpenmrsResource, context: FormProcessorContextProps) {
@@ -364,7 +364,7 @@ describe('ProgramStateAdapter', () => {
364
364
  });
365
365
 
366
366
  it('should return null if the new value is the same as the previous value', () => {
367
- field.meta.previousValue = { uuid: '7293cb90-c93f-4386-b32f-e8cfc633dc3e' };
367
+ field.meta.initialValue = { omrsObject: { uuid: '7293cb90-c93f-4386-b32f-e8cfc633dc3e' } };
368
368
  const value = '7293cb90-c93f-4386-b32f-e8cfc633dc3e';
369
369
  const result = ProgramStateAdapter.transformFieldValue(field, value, formContext);
370
370
  expect(result).toBeNull();
@@ -8,7 +8,7 @@ import { isEmpty } from '../validators/form-validator';
8
8
  export const ProgramStateAdapter: FormFieldValueAdapter = {
9
9
  transformFieldValue: function (field: FormField, value: any, context: FormContextProps) {
10
10
  clearSubmission(field);
11
- if (field.meta?.previousValue?.uuid === value || isEmpty(value)) {
11
+ if ((field.meta?.initialValue?.omrsObject as OpenmrsResource)?.uuid === value || isEmpty(value)) {
12
12
  return null;
13
13
  }
14
14
  field.meta.submission.newValue = {
@@ -28,7 +28,12 @@ export const ProgramStateAdapter: FormFieldValueAdapter = {
28
28
  const currentState = program.states
29
29
  .filter((state) => !state.endDate)
30
30
  .find((state) => state.state.programWorkflow?.uuid === field.questionOptions.workflowUuid)?.state;
31
- field.meta = { ...(field.meta || {}), previousValue: currentState };
31
+ field.meta = {
32
+ ...(field.meta || {}),
33
+ initialValue: {
34
+ omrsObject: currentState,
35
+ },
36
+ };
32
37
  return currentState?.uuid;
33
38
  }
34
39
  return null;
@@ -7,7 +7,7 @@ const FixedValue: React.FC<FormFieldInputProps> = ({ field, setFieldValue }) =>
7
7
  const context = useFormProviderContext();
8
8
 
9
9
  useEffect(() => {
10
- if (!field.meta?.previousValue && !isEmpty(field.meta.fixedValue)) {
10
+ if (!field.meta?.initialValue?.omrsObject && !isEmpty(field.meta.fixedValue)) {
11
11
  setFieldValue(field.meta.fixedValue);
12
12
  context.formFieldAdapters[field.type].transformFieldValue(field, field.meta.fixedValue, context);
13
13
  }
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { act, fireEvent, render, screen } from '@testing-library/react';
3
3
  import Dropdown from './dropdown.component';
4
4
  import { type FormField } from '../../../types';
5
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
5
6
 
6
7
  const question: FormField = {
7
8
  label: 'Patient past program.',
@@ -87,18 +88,20 @@ describe.skip('dropdown input field', () => {
87
88
 
88
89
  it('should edit obs', async () => {
89
90
  // setup
90
- question.meta.previousValue = {
91
- uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
92
- person: '833db896-c1f0-11eb-8529-0242ac130003',
93
- obsDatetime: encounterContext.encounterDate,
94
- concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
95
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
96
- order: null,
97
- groupMembers: [],
98
- voided: false,
99
- value: '6ddd933a-e65c-4f35-8884-c555b50c55e1',
91
+ question.meta.initialValue = {
92
+ omrsObject: {
93
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
94
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
95
+ obsDatetime: encounterContext.encounterDate,
96
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
97
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
98
+ order: null,
99
+ groupMembers: [],
100
+ voided: false,
101
+ value: '6ddd933a-e65c-4f35-8884-c555b50c55e1',
102
+ },
100
103
  };
101
- await renderForm({ 'patient-past-program': question.meta.previousValue.value });
104
+ await renderForm({ 'patient-past-program': (question.meta.initialValue.omrsObject as OpenmrsResource).value });
102
105
  const dropdownWidget = screen.getByRole('combobox', { name: /Patient past program./ });
103
106
 
104
107
  // do some edits
@@ -119,18 +122,20 @@ describe.skip('dropdown input field', () => {
119
122
 
120
123
  it('should clear selection when empty option is selected', async () => {
121
124
  // setup
122
- question.meta.previousValue = {
123
- uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
124
- person: '833db896-c1f0-11eb-8529-0242ac130003',
125
- obsDatetime: encounterContext.encounterDate,
126
- concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
127
- location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
128
- order: null,
129
- groupMembers: [],
130
- voided: false,
131
- value: '6ddd933a-e65c-4f35-8884-c555b50c55e1',
125
+ question.meta.initialValue = {
126
+ omrsObject: {
127
+ uuid: '305ed1fc-c1fd-11eb-8529-0242ac130003',
128
+ person: '833db896-c1f0-11eb-8529-0242ac130003',
129
+ obsDatetime: encounterContext.encounterDate,
130
+ concept: '1c43b05b-b6d8-4eb5-8f37-0b14f5347568',
131
+ location: { uuid: '41e6e516-c1f0-11eb-8529-0242ac130003' },
132
+ order: null,
133
+ groupMembers: [],
134
+ voided: false,
135
+ value: '6ddd933a-e65c-4f35-8884-c555b50c55e1',
136
+ },
132
137
  };
133
- await renderForm({ 'patient-past-program': question.meta.previousValue.value });
138
+ await renderForm({ 'patient-past-program': (question.meta.initialValue.omrsObject as OpenmrsResource).value });
134
139
  const dropdownWidget = screen.getByRole('combobox', { name: /Patient past program./ });
135
140
 
136
141
  // select an option first
@@ -20,7 +20,7 @@ const Toggle: React.FC<FormFieldInputProps> = ({ field, value, errors, warnings,
20
20
  useEffect(() => {
21
21
  // The toggle input doesn't support blank values
22
22
  // by default, the value should be false
23
- if (!field.meta?.previousValue && context.sessionMode == 'enter') {
23
+ if (!field.meta?.initialValue?.omrsObject && context.sessionMode == 'enter') {
24
24
  context.formFieldAdapters[field.type].transformFieldValue(field, value ?? false, context);
25
25
  }
26
26
  }, []);
@@ -8,13 +8,21 @@ export function cloneRepeatField(srcField: FormField, value: OpenmrsResource, id
8
8
  const originalGroupMembersIds: string[] = [];
9
9
  const clonedField = cloneDeep(srcField) as FormField;
10
10
  clonedField.questionOptions.repeatOptions = { ...(clonedField.questionOptions.repeatOptions ?? {}) };
11
- clonedField.meta = { repeat: { ...(clonedField.meta ?? {}), isClone: true }, previousValue: value };
11
+ clonedField.meta = {
12
+ repeat: { ...(clonedField.meta ?? {}), isClone: true },
13
+ initialValue: {
14
+ omrsObject: value,
15
+ },
16
+ };
12
17
  clonedField.id = `${clonedField.id}_${idSuffix}`;
13
18
  clonedField.questions?.forEach((childField) => {
14
19
  originalGroupMembersIds.push(childField.id);
15
20
  childField.id = `${childField.id}_${idSuffix}`;
16
21
  childField.meta.groupId = clonedField.id;
17
- childField.meta.previousValue = null;
22
+ childField.meta.initialValue = {
23
+ omrsObject: null,
24
+ refinedValue: null,
25
+ };
18
26
  childField.fieldDependents = new Set();
19
27
  clearSubmission(childField);
20
28
 
@@ -100,7 +100,7 @@ const Repeat: React.FC<FormFieldInputProps> = ({ field }) => {
100
100
  );
101
101
 
102
102
  const removeNthRow = (field: FormField) => {
103
- if (field.meta.previousValue) {
103
+ if (field.meta.initialValue?.omrsObject) {
104
104
  formFieldAdapters[field.type]?.transformFieldValue(field, null, context);
105
105
  field.meta.repeat = { ...(field.meta.repeat || {}), wasDeleted: true };
106
106
  if (field.type === 'obsGroup') {
@@ -216,10 +216,13 @@ export class EncounterFormProcessor extends FormProcessor {
216
216
  const initialValues = {};
217
217
  const repeatableFields = [];
218
218
  if (encounter) {
219
- const filteredFields = formFields.filter((field) => isEmpty(field.meta?.previousValue));
220
219
  await Promise.all(
221
- filteredFields.map(async (field) => {
220
+ formFields.map(async (field) => {
222
221
  const adapter = formFieldAdapters[field.type];
222
+ if (field.meta.initialValue?.omrsObject && !isEmpty(field.meta.initialValue.refinedValue)) {
223
+ initialValues[field.id] = field.meta.initialValue.refinedValue;
224
+ return;
225
+ }
223
226
  if (adapter) {
224
227
  if (hasRendering(field, 'repeating') && !field.meta?.repeat?.isClone) {
225
228
  repeatableFields.push(field);
@@ -227,6 +230,7 @@ export class EncounterFormProcessor extends FormProcessor {
227
230
  let value = null;
228
231
  try {
229
232
  value = await adapter.getInitialValue(field, encounter, context);
233
+ field.meta.initialValue.refinedValue = value;
230
234
  } catch (error) {
231
235
  console.error(error);
232
236
  }
@@ -241,7 +245,11 @@ export class EncounterFormProcessor extends FormProcessor {
241
245
  initialValues[field.id] = emptyValues[field.questionOptions.rendering] ?? '';
242
246
  }
243
247
  if (field.questionOptions.calculate?.calculateExpression) {
244
- await evaluateCalculateExpression(field, initialValues, context);
248
+ try {
249
+ await evaluateCalculateExpression(field, initialValues, context);
250
+ } catch (error) {
251
+ console.error(error);
252
+ }
245
253
  }
246
254
  } else {
247
255
  console.warn(`No adapter found for field type ${field.type}`);
@@ -16,6 +16,7 @@ import { ConceptTrue } from '../../constants';
16
16
  import { DefaultValueValidator } from '../../validators/default-value-validator';
17
17
  import { cloneRepeatField } from '../../components/repeat/helpers';
18
18
  import { assignedOrderIds } from '../../adapters/orders-adapter';
19
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
19
20
 
20
21
  export function prepareEncounter(
21
22
  context: FormContextProps,
@@ -189,8 +190,10 @@ function prepareObs(obsForSubmission: OpenmrsObs[], fields: FormField[]) {
189
190
  }
190
191
 
191
192
  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];
193
+ if ((field.isHidden || field.isParentHidden) && field.meta.initialValue.omrsObject) {
194
+ const valuesArray = Array.isArray(field.meta.initialValue.omrsObject)
195
+ ? field.meta.initialValue.omrsObject
196
+ : [field.meta.initialValue.omrsObject];
194
197
  addObsToList(
195
198
  obsForSubmission,
196
199
  valuesArray.map((obs) => voidObs(obs)),
@@ -214,8 +217,8 @@ function processObsGroup(obsForSubmission: OpenmrsObs[], groupField: FormField)
214
217
  }
215
218
 
216
219
  const obsGroup = constructObs(groupField, null);
217
- if (groupField.meta.previousValue) {
218
- obsGroup.uuid = groupField.meta.previousValue.uuid;
220
+ if (groupField.meta.initialValue?.omrsObject) {
221
+ obsGroup.uuid = (groupField.meta.initialValue.omrsObject as OpenmrsResource).uuid;
219
222
  }
220
223
 
221
224
  groupField.questions.forEach((nestedField) => {
@@ -261,7 +264,7 @@ function hasSubmittableObs(field: FormField) {
261
264
  if (isTransient || !['obs', 'obsGroup'].includes(type) || hasRendering(field, 'file') || field.meta.groupId) {
262
265
  return false;
263
266
  }
264
- if ((field.isHidden || field.isParentHidden) && field.meta.previousValue) {
267
+ if ((field.isHidden || field.isParentHidden) && field.meta.initialValue?.omrsObject) {
265
268
  return true;
266
269
  }
267
270
  return !field.isHidden && !field.isParentHidden && (type === 'obsGroup' || hasSubmission(field));
@@ -288,7 +291,7 @@ export async function hydrateRepeatField(
288
291
  const unMappedGroups = encounter.obs.filter(
289
292
  (obs) =>
290
293
  obs.concept.uuid === field.questionOptions.concept &&
291
- obs.uuid != field.meta.previousValue?.uuid &&
294
+ obs.uuid != (field.meta.initialValue?.omrsObject as OpenmrsResource)?.uuid &&
292
295
  !assignedObsIds.includes(obs.uuid),
293
296
  );
294
297
  const unMappedOrders = encounter.orders.filter((order) => {
@@ -35,6 +35,10 @@ const expectedTransformedSchema = {
35
35
  meta: {
36
36
  submission: null,
37
37
  pageId: 'page-Page1-0',
38
+ initialValue: {
39
+ omrsObject: null,
40
+ refinedValue: null,
41
+ },
38
42
  },
39
43
  },
40
44
  {
@@ -56,6 +60,10 @@ const expectedTransformedSchema = {
56
60
  meta: {
57
61
  submission: null,
58
62
  pageId: 'page-Page1-0',
63
+ initialValue: {
64
+ omrsObject: null,
65
+ refinedValue: null,
66
+ },
59
67
  },
60
68
  },
61
69
  {
@@ -77,6 +85,10 @@ const expectedTransformedSchema = {
77
85
  meta: {
78
86
  submission: null,
79
87
  pageId: 'page-Page1-0',
88
+ initialValue: {
89
+ omrsObject: null,
90
+ refinedValue: null,
91
+ },
80
92
  },
81
93
  },
82
94
  {
@@ -105,12 +117,20 @@ const expectedTransformedSchema = {
105
117
  meta: {
106
118
  submission: null,
107
119
  pageId: 'page-Page1-0',
120
+ initialValue: {
121
+ omrsObject: null,
122
+ refinedValue: null,
123
+ },
108
124
  },
109
125
  },
110
126
  ],
111
127
  meta: {
112
128
  submission: null,
113
129
  pageId: 'page-Page1-0',
130
+ initialValue: {
131
+ omrsObject: null,
132
+ refinedValue: null,
133
+ },
114
134
  },
115
135
  },
116
136
  {
@@ -148,6 +168,10 @@ const expectedTransformedSchema = {
148
168
  meta: {
149
169
  submission: null,
150
170
  pageId: 'page-Page1-0',
171
+ initialValue: {
172
+ omrsObject: null,
173
+ refinedValue: null,
174
+ },
151
175
  },
152
176
  },
153
177
  ],
@@ -1,3 +1,4 @@
1
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
1
2
  import { type FormField, type FormSchema, type FormSchemaTransformer, type RenderType, type FormPage } from '../types';
2
3
  import { isTrue } from '../utils/boolean-utils';
3
4
  import { hasRendering } from '../utils/common-utils';
@@ -74,7 +75,9 @@ function handleQuestionsWithDateOptions(sectionQuestions: Array<FormField>): Arr
74
75
  hide: question.questionOptions.shownDateOptions?.hide || question.hide,
75
76
  meta: {
76
77
  targetField: question.id,
77
- previousValue: question.meta?.previousValue?.obsDatetime,
78
+ initialValue: {
79
+ omrsObject: (question.meta?.initialValue?.omrsObject as OpenmrsResource)?.obsDatetime,
80
+ },
78
81
  },
79
82
  };
80
83
 
@@ -97,6 +100,10 @@ function sanitizeQuestion(question: FormField) {
97
100
  if (!question.meta) {
98
101
  question.meta = {
99
102
  submission: null,
103
+ initialValue: {
104
+ omrsObject: null,
105
+ refinedValue: null,
106
+ },
100
107
  };
101
108
  }
102
109
  }
@@ -257,7 +264,9 @@ function handleQuestionsWithObsComments(sectionQuestions: Array<FormField>): Arr
257
264
  hide: question.questionOptions.shownCommentOptions?.hide || question.hide,
258
265
  meta: {
259
266
  targetField: question.id,
260
- previousValue: question.meta?.previousValue?.comment,
267
+ initialValue: {
268
+ omrsObject: (question.meta?.initialValue?.omrsObject as OpenmrsResource)?.comment,
269
+ },
261
270
  },
262
271
  };
263
272
 
@@ -113,7 +113,10 @@ export interface RepeatOptions {
113
113
 
114
114
  export interface QuestionMetaProps {
115
115
  concept?: OpenmrsResource;
116
- previousValue?: any;
116
+ initialValue?: {
117
+ omrsObject: OpenmrsResource | Array<OpenmrsResource>;
118
+ refinedValue?: string | number | Date | Array<string | number>;
119
+ };
117
120
  submission?: {
118
121
  voidedValue?: any;
119
122
  newValue?: any;