@digihmis/esm-triage-app 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +4 -0
  2. package/dist/117.js +1 -0
  3. package/dist/117.js.map +1 -0
  4. package/dist/132.js +1 -0
  5. package/dist/132.js.map +1 -0
  6. package/dist/152.js +1 -0
  7. package/dist/152.js.map +1 -0
  8. package/dist/183.js +66 -0
  9. package/dist/183.js.map +1 -0
  10. package/dist/209.js +1 -0
  11. package/dist/209.js.map +1 -0
  12. package/dist/22.js +1 -0
  13. package/dist/22.js.map +1 -0
  14. package/dist/222.js +1 -0
  15. package/dist/222.js.map +1 -0
  16. package/dist/317.js +1 -0
  17. package/dist/317.js.map +1 -0
  18. package/dist/349.js +1 -0
  19. package/dist/349.js.map +1 -0
  20. package/dist/371.js +1 -0
  21. package/dist/371.js.map +1 -0
  22. package/dist/378.js +1 -0
  23. package/dist/378.js.map +1 -0
  24. package/dist/442.js +1 -0
  25. package/dist/442.js.map +1 -0
  26. package/dist/45.js +1 -0
  27. package/dist/45.js.map +1 -0
  28. package/dist/466.js +1 -0
  29. package/dist/466.js.map +1 -0
  30. package/dist/508.js +1 -0
  31. package/dist/508.js.map +1 -0
  32. package/dist/61.js +1 -0
  33. package/dist/61.js.map +1 -0
  34. package/dist/640.js +1 -0
  35. package/dist/640.js.map +1 -0
  36. package/dist/689.js +1 -0
  37. package/dist/689.js.map +1 -0
  38. package/dist/709.js +1 -0
  39. package/dist/709.js.map +1 -0
  40. package/dist/712.js +1 -0
  41. package/dist/712.js.map +1 -0
  42. package/dist/720.js +1 -0
  43. package/dist/720.js.map +1 -0
  44. package/dist/737.js +12 -0
  45. package/dist/737.js.map +1 -0
  46. package/dist/742.js +1 -0
  47. package/dist/742.js.map +1 -0
  48. package/dist/771.js +1 -0
  49. package/dist/771.js.map +1 -0
  50. package/dist/806.js +1 -0
  51. package/dist/806.js.map +1 -0
  52. package/dist/809.js +1 -0
  53. package/dist/809.js.map +1 -0
  54. package/dist/9.js +15 -0
  55. package/dist/9.js.map +1 -0
  56. package/dist/912.js +1 -0
  57. package/dist/912.js.map +1 -0
  58. package/dist/913.js +1 -0
  59. package/dist/913.js.map +1 -0
  60. package/dist/940.js +1 -0
  61. package/dist/940.js.map +1 -0
  62. package/dist/974.js +1 -0
  63. package/dist/974.js.map +1 -0
  64. package/dist/976.js +1 -0
  65. package/dist/976.js.map +1 -0
  66. package/dist/digihmis-esm-triage-app.js +6 -0
  67. package/dist/digihmis-esm-triage-app.js.buildmanifest.json +905 -0
  68. package/dist/digihmis-esm-triage-app.js.map +1 -0
  69. package/dist/main.js +90 -0
  70. package/dist/main.js.map +1 -0
  71. package/dist/routes.json +1 -0
  72. package/jest.config.js +26 -0
  73. package/package.json +51 -0
  74. package/rspack.config.js +1 -0
  75. package/src/config-schema.ts +142 -0
  76. package/src/dashboard.meta.ts +16 -0
  77. package/src/declarations.d.ts +6 -0
  78. package/src/icon-wrapper/icon-wrapper.extension.tsx +17 -0
  79. package/src/index.ts +41 -0
  80. package/src/left-panel/left-panel.component.tsx +100 -0
  81. package/src/left-panel/left-panel.scss +13 -0
  82. package/src/page-header/page-header.component.tsx +35 -0
  83. package/src/page-header/page-header.scss +76 -0
  84. package/src/past-vitals/action-menu/vitals-anthropometrics.component.tsx +44 -0
  85. package/src/past-vitals/action-menu/vitals-anthropometrics.scss +0 -0
  86. package/src/past-vitals/paginated-antropometrics/paginated-antropometrics.component.tsx +132 -0
  87. package/src/past-vitals/paginated-antropometrics/paginated-antropometrics.scss +9 -0
  88. package/src/past-vitals/paginated-vitals/paginated-vitals.component.tsx +157 -0
  89. package/src/past-vitals/paginated-vitals/paginated-vitals.scss +94 -0
  90. package/src/past-vitals/paginated-vitals/type/index.ts +1 -0
  91. package/src/past-vitals/patient-banner/patient-banner.component.tsx +60 -0
  92. package/src/past-vitals/patient-banner/patient-banner.scss +72 -0
  93. package/src/past-vitals/patient-vitals-history.component.tsx +85 -0
  94. package/src/past-vitals/patient-vitals-history.resource.ts +30 -0
  95. package/src/past-vitals/patient-vitals-history.scss +75 -0
  96. package/src/past-vitals/triage-anthropometrics/antropometrics-chart.component.tsx +170 -0
  97. package/src/past-vitals/triage-anthropometrics/antropometrics-chart.scss +70 -0
  98. package/src/past-vitals/triage-anthropometrics/triage-antropometrics.component.tsx +124 -0
  99. package/src/past-vitals/triage-anthropometrics/triage-antropometrics.scss +37 -0
  100. package/src/past-vitals/triage-anthropometrics/type/index.ts +17 -0
  101. package/src/past-vitals/triage-notes/triage-note.scss +94 -0
  102. package/src/past-vitals/triage-notes/triage-notes.component.tsx +75 -0
  103. package/src/past-vitals/triage-notes/triage-notes.resource.ts +72 -0
  104. package/src/past-vitals/triage-notes/type/index.ts +50 -0
  105. package/src/past-vitals/triage-vitals/triage-vitals-history.component.tsx +229 -0
  106. package/src/past-vitals/triage-vitals/triage-vitals-history.scss +48 -0
  107. package/src/past-vitals/triage-vitals/type/index.ts +32 -0
  108. package/src/past-vitals/triage-vitals/vital-chart.component.tsx +198 -0
  109. package/src/past-vitals/triage-vitals/vitals-chart.scss +70 -0
  110. package/src/past-vitals/utils/index.ts +5 -0
  111. package/src/past-vitals/vitals-and-biometrics-header/vitals-header-item.component.tsx +61 -0
  112. package/src/past-vitals/vitals-and-biometrics-header/vitals-header-item.scss +171 -0
  113. package/src/past-vitals/vitals-and-biometrics-header/vitals-header-item.test.tsx +31 -0
  114. package/src/past-vitals/vitals-and-biometrics-header/vitals-header.extension.tsx +225 -0
  115. package/src/past-vitals/vitals-and-biometrics-header/vitals-header.scss +233 -0
  116. package/src/queue-room-table/queue-room-table.component.tsx +380 -0
  117. package/src/queue-room-table/queue-room-table.resource.ts +73 -0
  118. package/src/queue-room-table/queue-room-table.scss +193 -0
  119. package/src/queue-room-table/type/index.ts +30 -0
  120. package/src/queue-room-table/useQueueActions.ts +24 -0
  121. package/src/record-vitals-workspace/common/index.ts +19 -0
  122. package/src/record-vitals-workspace/component/vitals-biometrics-input.component.tsx +433 -0
  123. package/src/record-vitals-workspace/component/vitals-biometrics-input.scss +358 -0
  124. package/src/record-vitals-workspace/helper/index.ts +200 -0
  125. package/src/record-vitals-workspace/record-vitals.resource.ts +570 -0
  126. package/src/record-vitals-workspace/record-vitals.scss +140 -0
  127. package/src/record-vitals-workspace/record-vitals.workspace.tsx +612 -0
  128. package/src/record-vitals-workspace/schema/index.ts +28 -0
  129. package/src/record-vitals-workspace/type/index.ts +170 -0
  130. package/src/record-vitals-workspace/utils/index.ts +100 -0
  131. package/src/routes.json +70 -0
  132. package/src/setup-tests.ts +1 -0
  133. package/src/triage-queue-dashboard/triage-dashboard.resource.ts +75 -0
  134. package/src/triage-queue-dashboard/triage-queue-dashboard.component.tsx +261 -0
  135. package/src/triage-queue-dashboard/triage-queue-dashboard.scss +166 -0
  136. package/src/triage-queue-dashboard/type/index.ts +31 -0
  137. package/translations/am.json +92 -0
  138. package/translations/ar.json +92 -0
  139. package/translations/en.json +92 -0
  140. package/translations/es.json +92 -0
  141. package/translations/fr.json +92 -0
  142. package/translations/he.json +92 -0
  143. package/translations/km.json +92 -0
  144. package/translations/pt.json +92 -0
  145. package/translations/sw.json +92 -0
  146. package/translations/vi.json +92 -0
  147. package/translations/zh.json +92 -0
  148. package/tsconfig.json +5 -0
@@ -0,0 +1,358 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
+
6
+ .label01 {
7
+ @include type.type-style('label-01');
8
+ }
9
+
10
+ .bodyLong01 {
11
+ @include type.type-style('body-01');
12
+ }
13
+
14
+ .productiveHeading04 {
15
+ @include type.type-style('heading-04');
16
+ }
17
+
18
+ .container {
19
+ width: 21.625rem;
20
+ border-bottom: 1px solid $ui-04;
21
+ margin-bottom: layout.$spacing-03;
22
+ :global(.omrs-breakpoint-lt-desktop) & {
23
+ width: 23.625rem;
24
+ }
25
+ }
26
+
27
+ .label {
28
+ @extend .label01;
29
+ color: $text-02;
30
+ margin-bottom: layout.$spacing-03;
31
+ }
32
+
33
+ .numberInput {
34
+ border: none;
35
+ @extend .productiveHeading04;
36
+ text-align: center;
37
+ padding: 0;
38
+ color: $ui-05;
39
+
40
+ // TODO: Move to styleguide overrides
41
+ :global(.cds--number input[type='number']) {
42
+ border: none !important;
43
+ text-align: center;
44
+ @include type.type-style('heading-04');
45
+ font-family:
46
+ ibm plex sans,
47
+ system-ui,
48
+ -apple-system,
49
+ BlinkMacSystemFont,
50
+ '.sfnstext-regular',
51
+ sans-serif;
52
+ min-width: auto !important;
53
+ }
54
+
55
+ :global(.cds--number--nosteppers input[type='number']) {
56
+ padding-right: layout.$spacing-05 !important;
57
+ }
58
+
59
+ ::placeholder {
60
+ color: $text-03;
61
+ }
62
+ }
63
+
64
+ .inputContainer {
65
+ @extend .productiveHeading04;
66
+ height: 4.875rem;
67
+ display: flex;
68
+ justify-content: center;
69
+ flex-direction: column;
70
+ text-align: center;
71
+ color: $text-02;
72
+ background-color: $ui-01;
73
+
74
+ &.focused {
75
+ outline: layout.$spacing-01 solid colors.$blue-60;
76
+
77
+ input {
78
+ outline: none !important;
79
+ }
80
+
81
+ input[data-invalid]:not(:focus) {
82
+ outline: none;
83
+ }
84
+
85
+ svg {
86
+ display: none;
87
+ }
88
+ }
89
+ }
90
+
91
+ :global(.omrs-breakpoint-lt-desktop) .inputContainer {
92
+ background-color: $ui-02;
93
+ }
94
+
95
+ .inputContainer {
96
+ &:global(.red) {
97
+ background-color: colors.$red-20;
98
+
99
+ input {
100
+ background-color: colors.$red-20;
101
+ }
102
+ }
103
+
104
+ &:global(.green) {
105
+ background-color: colors.$green-20;
106
+
107
+ input {
108
+ background-color: colors.$green-20;
109
+ }
110
+ }
111
+
112
+ &:global(.yellow) {
113
+ background-color: colors.$yellow-10;
114
+ input {
115
+ background-color: colors.$yellow-10;
116
+ }
117
+ }
118
+ }
119
+
120
+ .numberInput {
121
+ svg {
122
+ display: none;
123
+ }
124
+
125
+ input[data-invalid]:not(:focus) {
126
+ outline: none !important;
127
+ }
128
+ }
129
+
130
+ .textarea {
131
+ border: none;
132
+ @extend .bodyLong01;
133
+ text-align: left;
134
+
135
+ textarea {
136
+ border-bottom: none;
137
+
138
+ &:focus {
139
+ outline: none;
140
+ }
141
+ }
142
+ }
143
+
144
+ .centered {
145
+ display: flex;
146
+ flex-direction: row;
147
+ align-items: center;
148
+ text-align: center;
149
+ margin: 0;
150
+ padding: 0;
151
+ }
152
+
153
+ .unitName {
154
+ @include type.type-style('body-compact-01');
155
+ color: $text-02;
156
+ margin-top: layout.$spacing-03;
157
+ }
158
+
159
+ .invalidInputError {
160
+ max-width: 11rem;
161
+ color: $danger;
162
+ margin-bottom: layout.$spacing-03;
163
+ }
164
+
165
+ .layer {
166
+ width: 100%;
167
+ }
168
+
169
+ .labelContainer {
170
+ display: flex;
171
+ justify-content: space-between;
172
+ align-items: center;
173
+ }
174
+
175
+ .invalidInput {
176
+ outline: layout.$spacing-01 solid $danger;
177
+
178
+ &.focused {
179
+ border: none;
180
+ }
181
+ }
182
+
183
+ .invalidInputIcon {
184
+ svg {
185
+ color: $danger;
186
+ }
187
+ }
188
+
189
+ .critical-value {
190
+ background-color: colors.$red-10;
191
+ .numberInput {
192
+ svg {
193
+ display: none;
194
+ }
195
+
196
+ input {
197
+ background-color: colors.$red-10;
198
+ }
199
+
200
+ input[data-invalid]:not(:focus) {
201
+ outline: none;
202
+ }
203
+ }
204
+ }
205
+
206
+ .critically-low,
207
+ .critically-high,
208
+ .low,
209
+ .high {
210
+ &::after {
211
+ @include type.type-style('heading-compact-01');
212
+ color: $text-02;
213
+ }
214
+ }
215
+
216
+ .low::after {
217
+ content: ' ↓';
218
+ }
219
+
220
+ .critically-low::after {
221
+ content: ' ↓↓';
222
+ }
223
+
224
+ .high::after {
225
+ content: ' ↑';
226
+ }
227
+
228
+ .critically-high::after {
229
+ content: ' ↑↑';
230
+ }
231
+
232
+ .inputInTabletView {
233
+ &.inputWithAbnormalValue {
234
+ .inputContainer {
235
+ background-color: colors.$red-20;
236
+
237
+ .numberInput {
238
+ input {
239
+ background-color: colors.$red-20;
240
+ outline: none;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ .readonly {
248
+ cursor: default;
249
+ }
250
+
251
+ :global(.omrs-breakpoint-lt-desktop) .readonly {
252
+ background-color: colors.$gray-10;
253
+ }
254
+
255
+ :global(.omrs-breakpoint-gt-tablet) .readonly {
256
+ background-color: colors.$white-0;
257
+ }
258
+
259
+ .valueStatusContainer {
260
+ margin-top: 0.5rem;
261
+ padding: 0.75rem 1rem;
262
+ background-color: #f4f4f4;
263
+ border-left: 4px solid;
264
+ border-radius: 4px;
265
+ animation: slideDown 0.3s ease-out;
266
+ }
267
+
268
+ @keyframes slideDown {
269
+ from {
270
+ opacity: 0;
271
+ transform: translateY(-10px);
272
+ }
273
+ to {
274
+ opacity: 1;
275
+ transform: translateY(0);
276
+ }
277
+ }
278
+
279
+ .statusHeader {
280
+ display: flex;
281
+ align-items: center;
282
+ gap: 0.5rem;
283
+ margin-bottom: 0.5rem;
284
+ }
285
+
286
+ .statusLevel {
287
+ font-weight: 600;
288
+ font-size: 0.875rem;
289
+ text-transform: uppercase;
290
+ letter-spacing: 0.5px;
291
+ }
292
+
293
+ .statusMessage {
294
+ margin: 0;
295
+ font-size: 0.875rem;
296
+ color: #525252;
297
+ line-height: 1.4;
298
+ }
299
+
300
+ .rangeIndicator {
301
+ margin-top: 0.75rem;
302
+ }
303
+
304
+ .rangeBar {
305
+ position: relative;
306
+ display: flex;
307
+ height: 8px;
308
+ border-radius: 4px;
309
+ overflow: hidden;
310
+ margin-bottom: 0.25rem;
311
+ }
312
+
313
+ .rangeSegment {
314
+ flex: 1;
315
+
316
+ &[data-level='critical-low'] {
317
+ background-color: #da1e28;
318
+ }
319
+
320
+ &[data-level='low'] {
321
+ background-color: #f1c21b;
322
+ }
323
+
324
+ &[data-level='normal'] {
325
+ background-color: #24a148;
326
+ }
327
+
328
+ &[data-level='high'] {
329
+ background-color: #f1c21b;
330
+ }
331
+
332
+ &[data-level='critical-high'] {
333
+ background-color: #da1e28;
334
+ }
335
+ }
336
+
337
+ .currentValueMarker {
338
+ position: absolute;
339
+ top: -4px;
340
+ width: 4px;
341
+ height: 16px;
342
+ border-radius: 2px;
343
+ transform: translateX(-50%);
344
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
345
+ z-index: 1;
346
+ }
347
+
348
+ .rangeLabels {
349
+ display: flex;
350
+ justify-content: space-between;
351
+ font-size: 0.75rem;
352
+ color: #8d8d8d;
353
+ margin-top: 0.25rem;
354
+ }
355
+
356
+ .rangeLabel {
357
+ white-space: nowrap;
358
+ }
@@ -0,0 +1,200 @@
1
+ import { type OpenmrsResource } from '@openmrs/esm-framework';
2
+ import { type ConceptMetadata } from '../common';
3
+ import { type VitalsBiometricsFormData } from '../schema';
4
+ import { VitalsAndBiometricsFieldValuesMap } from '../record-vitals.resource';
5
+ import { FHIRInterpretation, ObservationInterpretation, ObsReferenceRanges } from '../type';
6
+
7
+ export function calculateBodyMassIndex(
8
+ weight: number,
9
+ height: number,
10
+ weightUnit: 'lb' | 'lbs' | 'g',
11
+ heightUnit: 'm' | 'cm' | 'in',
12
+ ) {
13
+ if (weight > 0 && height > 0) {
14
+ // Convert weight to kg
15
+ let weightInKg = weight;
16
+ if (weightUnit === 'lb' || weightUnit === 'lbs') {
17
+ weightInKg = weight * 0.453592;
18
+ } else if (weightUnit === 'g') {
19
+ weightInKg = weight / 1000;
20
+ }
21
+
22
+ // Convert height to cm
23
+ let heightInCm = height;
24
+ if (heightUnit === 'm') {
25
+ heightInCm = height * 100;
26
+ } else if (heightUnit === 'in') {
27
+ heightInCm = height * 2.54;
28
+ }
29
+
30
+ return Number((weightInKg / (heightInCm / 100) ** 2).toFixed(1));
31
+ }
32
+ return null;
33
+ }
34
+
35
+ export function assessValue(value: number | undefined, range?: ObsReferenceRanges): ObservationInterpretation {
36
+ if (range && value) {
37
+ if (range.hiCritical && value >= range.hiCritical) {
38
+ return 'critically_high';
39
+ }
40
+
41
+ if (range.hiNormal && value > range.hiNormal) {
42
+ return 'high';
43
+ }
44
+
45
+ if (range.lowCritical && value <= range.lowCritical) {
46
+ return 'critically_low';
47
+ }
48
+
49
+ if (range.lowNormal && value < range.lowNormal) {
50
+ return 'low';
51
+ }
52
+ }
53
+
54
+ return 'normal';
55
+ }
56
+
57
+ export function mapFhirInterpretationToObservationInterpretation(
58
+ interpretation: FHIRInterpretation,
59
+ ): ObservationInterpretation {
60
+ const normalized = interpretation?.trim();
61
+ switch (normalized) {
62
+ case 'Critically Low':
63
+ return 'critically_low';
64
+ case 'Critically High':
65
+ return 'critically_high';
66
+ case 'High':
67
+ return 'high';
68
+ case 'Low':
69
+ return 'low';
70
+ case 'Normal':
71
+ return 'normal';
72
+ default:
73
+ return 'normal';
74
+ }
75
+ }
76
+
77
+ export function interpretBloodPressure(
78
+ systolic: number | undefined,
79
+ diastolic: number | undefined,
80
+ concepts: { systolicBloodPressureUuid?: string; diastolicBloodPressureUuid?: string } | undefined,
81
+ conceptMetadata: Array<ConceptMetadata> | undefined,
82
+ systolicInterpretation?: ObservationInterpretation,
83
+ diastolicInterpretation?: ObservationInterpretation,
84
+ ): ObservationInterpretation {
85
+ if (!conceptMetadata) {
86
+ return 'normal';
87
+ }
88
+
89
+ // Use interpretation from FHIR Observation when available (preferred).
90
+ // Fallback to calculation for backward compatibility: existing observations may not have
91
+ // interpretation set if they were created before interpretation was added, or if reference
92
+ // ranges weren't available at creation time.
93
+ const systolicAssessment =
94
+ systolicInterpretation ??
95
+ (concepts?.systolicBloodPressureUuid
96
+ ? assessValue(systolic, getReferenceRangesForConcept(concepts.systolicBloodPressureUuid, conceptMetadata))
97
+ : 'normal');
98
+
99
+ const diastolicAssessment =
100
+ diastolicInterpretation ??
101
+ (concepts?.diastolicBloodPressureUuid
102
+ ? assessValue(diastolic, getReferenceRangesForConcept(concepts.diastolicBloodPressureUuid, conceptMetadata))
103
+ : 'normal');
104
+
105
+ if (systolicAssessment === 'critically_high' || diastolicAssessment === 'critically_high') {
106
+ return 'critically_high';
107
+ }
108
+
109
+ if (systolicAssessment === 'critically_low' || diastolicAssessment === 'critically_low') {
110
+ return 'critically_low';
111
+ }
112
+
113
+ if (systolicAssessment === 'high' || diastolicAssessment === 'high') {
114
+ return 'high';
115
+ }
116
+
117
+ if (systolicAssessment === 'low' || diastolicAssessment === 'low') {
118
+ return 'low';
119
+ }
120
+
121
+ return 'normal';
122
+ }
123
+
124
+ export function generatePlaceholder(value: string) {
125
+ switch (value) {
126
+ case 'BMI':
127
+ return '';
128
+
129
+ case 'Temperature':
130
+ case 'Weight':
131
+ return '--.-';
132
+
133
+ case 'Height':
134
+ case 'diastolic':
135
+ case 'systolic':
136
+ case 'Pulse':
137
+ return '---';
138
+
139
+ default:
140
+ return '--';
141
+ }
142
+ }
143
+
144
+ export function getReferenceRangesForConcept(
145
+ conceptUuid: string | undefined | null,
146
+ conceptMetadata: Array<ConceptMetadata> | undefined,
147
+ ): ConceptMetadata | undefined {
148
+ if (!conceptUuid || !conceptMetadata?.length) {
149
+ return undefined;
150
+ }
151
+
152
+ return conceptMetadata?.find((metadata) => metadata.uuid === conceptUuid);
153
+ }
154
+
155
+ export function prepareObsForSubmission(
156
+ formData: VitalsBiometricsFormData,
157
+ dirtyFields: Record<string, boolean>,
158
+ formContext: 'creating' | 'editing',
159
+ initialFieldValuesMap: VitalsAndBiometricsFieldValuesMap,
160
+ fieldToConceptMap: Record<string, string>,
161
+ ): {
162
+ toBeVoided: Array<OpenmrsResource>;
163
+ newObs: Array<OpenmrsResource>;
164
+ } {
165
+ return Object.entries(formData).reduce(
166
+ (obsForSubmission, [field, newValue]) => {
167
+ if (!fieldToConceptMap[`${field}Uuid`]) {
168
+ console.error(`Missing concept mapping for field: ${field}`);
169
+ return obsForSubmission;
170
+ }
171
+ if (formContext === 'editing' && initialFieldValuesMap.has(field) && dirtyFields[field]) {
172
+ // void old obs
173
+ const { obs } = initialFieldValuesMap.get(field);
174
+ obsForSubmission.toBeVoided.push({
175
+ uuid: obs.uuid,
176
+ voided: true,
177
+ });
178
+
179
+ if (newValue) {
180
+ obsForSubmission.newObs.push({
181
+ concept: fieldToConceptMap[`${field}Uuid`],
182
+ value: newValue,
183
+ });
184
+ }
185
+ } else if (dirtyFields[field] && newValue) {
186
+ // create new obs
187
+ obsForSubmission.newObs.push({
188
+ concept: fieldToConceptMap[`${field}Uuid`],
189
+ value: newValue,
190
+ });
191
+ }
192
+
193
+ return obsForSubmission;
194
+ },
195
+ {
196
+ toBeVoided: [],
197
+ newObs: [],
198
+ },
199
+ );
200
+ }