@openmrs/esm-patient-vitals-app 9.2.3-pre.7186 → 9.2.3-pre.7198
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/.turbo/turbo-build.log +20 -20
- package/dist/101.js +2 -0
- package/dist/101.js.map +1 -0
- package/dist/1197.js +1 -1
- package/dist/{9057.js → 1423.js} +1 -1
- package/dist/1423.js.map +1 -0
- package/dist/2146.js +1 -1
- package/dist/2690.js +1 -1
- package/dist/3099.js +1 -1
- package/dist/3584.js +1 -1
- package/dist/4055.js +1 -1
- package/dist/4132.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/4335.js +1 -1
- package/dist/4618.js +1 -1
- package/dist/4652.js +1 -1
- package/dist/4944.js +1 -1
- package/dist/5173.js +1 -1
- package/dist/5207.js +1 -0
- package/dist/5207.js.map +1 -0
- package/dist/5241.js +1 -1
- package/dist/5387.js +1 -0
- package/dist/5387.js.map +1 -0
- package/dist/5395.js +2 -0
- package/dist/{3368.js.LICENSE.txt → 5395.js.LICENSE.txt} +19 -1
- package/dist/5395.js.map +1 -0
- package/dist/5442.js +1 -1
- package/dist/5661.js +1 -1
- package/dist/6468.js +1 -1
- package/dist/6679.js +1 -1
- package/dist/6840.js +1 -1
- package/dist/6859.js +1 -1
- package/dist/7159.js +1 -1
- package/dist/723.js +1 -1
- package/dist/8163.js +1 -1
- package/dist/8295.js +2 -0
- package/dist/8295.js.LICENSE.txt +7 -0
- package/dist/8295.js.map +1 -0
- package/dist/8618.js +1 -1
- package/dist/890.js +1 -1
- package/dist/8953.js +1 -0
- package/dist/8953.js.map +1 -0
- package/dist/9214.js +1 -1
- package/dist/9538.js +1 -1
- package/dist/986.js +1 -1
- package/dist/9879.js +1 -1
- package/dist/9895.js +1 -1
- package/dist/9900.js +1 -1
- package/dist/9913.js +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-vitals-app.js +1 -1
- package/dist/openmrs-esm-patient-vitals-app.js.buildmanifest.json +238 -214
- package/dist/openmrs-esm-patient-vitals-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/biometrics/biometrics-base.component.tsx +0 -1
- package/src/biometrics/biometrics-overview.test.tsx +26 -5
- package/src/biometrics/paginated-biometrics.component.tsx +5 -0
- package/src/common/data.resource.ts +128 -75
- package/src/common/helpers.ts +50 -0
- package/src/common/index.ts +3 -2
- package/src/common/types.ts +2 -1
- package/src/components/action-menu/vitals-biometrics-action-menu.component.tsx +60 -0
- package/src/components/action-menu/vitals-biometrics-action-menu.scss +11 -0
- package/src/components/delete-vitals-biometrics-modal/delete-vitals-biometrics.modal.tsx +84 -0
- package/src/{weight-tile → components/weight-tile}/weight-tile.component.tsx +2 -2
- package/src/{weight-tile → components/weight-tile}/weight-tile.test.tsx +4 -4
- package/src/index.ts +6 -1
- package/src/routes.json +6 -0
- package/src/vitals/paginated-vitals.component.tsx +5 -0
- package/src/vitals/vitals-overview.component.tsx +3 -2
- package/src/vitals-biometrics-form/schema.ts +28 -0
- package/src/vitals-biometrics-form/vitals-biometrics-form.test.tsx +203 -21
- package/src/vitals-biometrics-form/vitals-biometrics-form.workspace.tsx +78 -60
- package/src/vitals-biometrics-form/vitals-biometrics-input.component.tsx +1 -1
- package/translations/am.json +14 -1
- package/translations/ar.json +14 -1
- package/translations/bn.json +14 -1
- package/translations/de.json +14 -1
- package/translations/en.json +14 -1
- package/translations/es.json +14 -1
- package/translations/fr.json +14 -1
- package/translations/he.json +14 -1
- package/translations/hi.json +14 -1
- package/translations/hi_IN.json +14 -1
- package/translations/id.json +14 -1
- package/translations/it.json +14 -1
- package/translations/km.json +14 -1
- package/translations/ku.json +14 -1
- package/translations/ky.json +14 -1
- package/translations/lg.json +14 -1
- package/translations/ne.json +14 -1
- package/translations/pt.json +14 -1
- package/translations/pt_BR.json +14 -1
- package/translations/qu.json +14 -1
- package/translations/ru_RU.json +14 -1
- package/translations/si.json +14 -1
- package/translations/sw.json +14 -1
- package/translations/sw_KE.json +14 -1
- package/translations/tr.json +14 -1
- package/translations/tr_TR.json +14 -1
- package/translations/uk.json +14 -1
- package/translations/uz.json +14 -1
- package/translations/uz_UZ.json +14 -1
- package/translations/vi.json +14 -1
- package/translations/zh.json +14 -1
- package/translations/zh_CN.json +14 -1
- package/dist/3368.js +0 -2
- package/dist/3368.js.map +0 -1
- package/dist/4716.js +0 -1
- package/dist/4716.js.map +0 -1
- package/dist/7738.js +0 -2
- package/dist/7738.js.LICENSE.txt +0 -25
- package/dist/7738.js.map +0 -1
- package/dist/8895.js +0 -2
- package/dist/8895.js.map +0 -1
- package/dist/8957.js +0 -1
- package/dist/8957.js.map +0 -1
- package/dist/9057.js.map +0 -1
- /package/dist/{8895.js.LICENSE.txt → 101.js.LICENSE.txt} +0 -0
- /package/src/{weight-tile → components/weight-tile}/weight-tile.scss +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react';
|
|
2
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { deleteEncounter, invalidateCachedVitalsAndBiometrics } from '../../common';
|
|
5
|
+
import { ModalHeader, ModalBody, ModalFooter, Button, InlineLoading } from '@carbon/react';
|
|
6
|
+
|
|
7
|
+
interface DeleteVitalsAndBiometricsModalProps {
|
|
8
|
+
patientUuid: string;
|
|
9
|
+
encounterUuid: string;
|
|
10
|
+
closeDeleteModal: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DeleteVitalsAndBiometricsModal: React.FC<DeleteVitalsAndBiometricsModalProps> = ({
|
|
14
|
+
encounterUuid,
|
|
15
|
+
closeDeleteModal,
|
|
16
|
+
}) => {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
19
|
+
|
|
20
|
+
const handleDelete = useCallback(async () => {
|
|
21
|
+
if (!encounterUuid) {
|
|
22
|
+
showSnackbar({
|
|
23
|
+
isLowContrast: false,
|
|
24
|
+
kind: 'error',
|
|
25
|
+
title: t('errorDeleting', 'Error deleting vitals and biometrics'),
|
|
26
|
+
subtitle: t('encounterUuidRequired', 'Encounter UUID is required to delete vitals and biometrics'),
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setIsDeleting(true);
|
|
32
|
+
deleteEncounter(encounterUuid)
|
|
33
|
+
.then(() => {
|
|
34
|
+
invalidateCachedVitalsAndBiometrics();
|
|
35
|
+
closeDeleteModal();
|
|
36
|
+
showSnackbar({
|
|
37
|
+
isLowContrast: true,
|
|
38
|
+
kind: 'success',
|
|
39
|
+
title: t('vitalsAndBiometricsDeleted', 'Vitals and biometrics deleted'),
|
|
40
|
+
});
|
|
41
|
+
})
|
|
42
|
+
.catch((error) => {
|
|
43
|
+
console.error('Error deleting encounter: ', error);
|
|
44
|
+
showSnackbar({
|
|
45
|
+
isLowContrast: false,
|
|
46
|
+
kind: 'error',
|
|
47
|
+
title: t('errorDeleting', 'Error deleting vitals and biometrics'),
|
|
48
|
+
subtitle: error?.message,
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
.finally(() => setIsDeleting(false));
|
|
52
|
+
}, [encounterUuid, t, closeDeleteModal]);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<ModalHeader
|
|
57
|
+
closeModal={closeDeleteModal}
|
|
58
|
+
title={t('deleteVitalsAndBiometrics', 'Delete vitals and biometrics')}
|
|
59
|
+
/>
|
|
60
|
+
<ModalBody>
|
|
61
|
+
<p>
|
|
62
|
+
{t(
|
|
63
|
+
'deleteConfirmationText',
|
|
64
|
+
'Note: Deleting these entries will also remove related vitals or biometrics data. Are you sure you want to continue?',
|
|
65
|
+
)}
|
|
66
|
+
</p>
|
|
67
|
+
</ModalBody>
|
|
68
|
+
<ModalFooter>
|
|
69
|
+
<Button kind="secondary" onClick={closeDeleteModal}>
|
|
70
|
+
{t('cancel', 'Cancel')}
|
|
71
|
+
</Button>
|
|
72
|
+
<Button kind="danger" onClick={handleDelete} disabled={isDeleting}>
|
|
73
|
+
{isDeleting ? (
|
|
74
|
+
<InlineLoading description={t('deleting', 'Deleting') + '...'} />
|
|
75
|
+
) : (
|
|
76
|
+
<span>{t('delete', 'Delete')}</span>
|
|
77
|
+
)}
|
|
78
|
+
</Button>
|
|
79
|
+
</ModalFooter>
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default DeleteVitalsAndBiometricsModal;
|
|
@@ -2,8 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { InlineLoading } from '@carbon/react';
|
|
4
4
|
import { useConfig } from '@openmrs/esm-framework';
|
|
5
|
-
import { useVitalsAndBiometrics, useVitalsConceptMetadata } from '
|
|
6
|
-
import { type ConfigObject } from '
|
|
5
|
+
import { useVitalsAndBiometrics, useVitalsConceptMetadata } from '../../common';
|
|
6
|
+
import { type ConfigObject } from '../../config-schema';
|
|
7
7
|
import styles from './weight-tile.scss';
|
|
8
8
|
|
|
9
9
|
interface WeightTileInterface {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { screen } from '@testing-library/react';
|
|
3
3
|
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
4
|
-
import { configSchema, type ConfigObject } from '
|
|
4
|
+
import { configSchema, type ConfigObject } from '../../config-schema';
|
|
5
5
|
import { getByTextWithMarkup, mockPatient, renderWithSwr, waitForLoadingToFinish } from 'tools';
|
|
6
6
|
import { formattedBiometrics, mockBiometricsConfig, mockConceptMetadata, mockVitalsSignsConcepts } from '__mocks__';
|
|
7
|
-
import { useVitalsAndBiometrics } from '
|
|
7
|
+
import { useVitalsAndBiometrics } from '../../common';
|
|
8
8
|
import WeightTile from './weight-tile.component';
|
|
9
9
|
|
|
10
10
|
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);
|
|
@@ -13,8 +13,8 @@ const mockConceptUnits = new Map<string, string>(
|
|
|
13
13
|
mockVitalsSignsConcepts.data.results[0].setMembers.map((concept) => [concept.uuid, concept.units]),
|
|
14
14
|
);
|
|
15
15
|
|
|
16
|
-
jest.mock('
|
|
17
|
-
const originalModule = jest.requireActual('
|
|
16
|
+
jest.mock('../../common', () => {
|
|
17
|
+
const originalModule = jest.requireActual('../../common');
|
|
18
18
|
|
|
19
19
|
return {
|
|
20
20
|
...originalModule,
|
package/src/index.ts
CHANGED
|
@@ -58,10 +58,15 @@ export const vitalsAndBiometricsDashboardLink =
|
|
|
58
58
|
options,
|
|
59
59
|
);
|
|
60
60
|
|
|
61
|
-
export const weightTile = getAsyncLifecycle(() => import('./weight-tile/weight-tile.component'), options);
|
|
61
|
+
export const weightTile = getAsyncLifecycle(() => import('./components/weight-tile/weight-tile.component'), options);
|
|
62
62
|
|
|
63
63
|
// t('recordVitalsAndBiometrics', 'Record Vitals and Biometrics')
|
|
64
64
|
export const vitalsBiometricsFormWorkspace = getAsyncLifecycle(
|
|
65
65
|
() => import('./vitals-biometrics-form/vitals-biometrics-form.workspace'),
|
|
66
66
|
options,
|
|
67
67
|
);
|
|
68
|
+
|
|
69
|
+
export const vitalsAndBiometricsDeleteConfirmationModal = getAsyncLifecycle(
|
|
70
|
+
() => import('./components/delete-vitals-biometrics-modal/delete-vitals-biometrics.modal'),
|
|
71
|
+
options,
|
|
72
|
+
);
|
package/src/routes.json
CHANGED
|
@@ -13,6 +13,7 @@ import { useLayoutType, usePagination } from '@openmrs/esm-framework';
|
|
|
13
13
|
import { PatientChartPagination } from '@openmrs/esm-patient-common-lib';
|
|
14
14
|
import type { VitalsTableHeader, VitalsTableRow } from './types';
|
|
15
15
|
import styles from './paginated-vitals.scss';
|
|
16
|
+
import { VitalsAndBiometricsActionMenu } from '../components/action-menu/vitals-biometrics-action-menu.component';
|
|
16
17
|
|
|
17
18
|
interface PaginatedVitalsProps {
|
|
18
19
|
isPrinting?: boolean;
|
|
@@ -108,6 +109,7 @@ const PaginatedVitals: React.FC<PaginatedVitalsProps> = ({
|
|
|
108
109
|
{header.header?.content ?? header.header}
|
|
109
110
|
</TableHeader>
|
|
110
111
|
))}
|
|
112
|
+
<TableHeader />
|
|
111
113
|
</TableRow>
|
|
112
114
|
</TableHead>
|
|
113
115
|
<TableBody>
|
|
@@ -123,6 +125,9 @@ const PaginatedVitals: React.FC<PaginatedVitalsProps> = ({
|
|
|
123
125
|
</StyledTableCell>
|
|
124
126
|
);
|
|
125
127
|
})}
|
|
128
|
+
<TableCell className="cds--table-column-menu" id="actions">
|
|
129
|
+
<VitalsAndBiometricsActionMenu encounterUuid={row.id} />
|
|
130
|
+
</TableCell>
|
|
126
131
|
</TableRow>
|
|
127
132
|
))}
|
|
128
133
|
</TableBody>
|
|
@@ -22,6 +22,7 @@ import PaginatedVitals from './paginated-vitals.component';
|
|
|
22
22
|
import PrintComponent from './print/print.component';
|
|
23
23
|
import VitalsChart from './vitals-chart.component';
|
|
24
24
|
import styles from './vitals-overview.scss';
|
|
25
|
+
import { useEncounterVitalsAndBiometrics } from '../common/data.resource';
|
|
25
26
|
|
|
26
27
|
interface VitalsOverviewProps {
|
|
27
28
|
patientUuid: string;
|
|
@@ -50,6 +51,7 @@ const VitalsOverview: React.FC<VitalsOverviewProps> = ({ patientUuid, patient, p
|
|
|
50
51
|
launchVitalsAndBiometricsForm(currentVisit, config);
|
|
51
52
|
}, [config, currentVisit]);
|
|
52
53
|
|
|
54
|
+
useEncounterVitalsAndBiometrics('771bbc44-8d45-4ac3-af6e-059814dd7cde');
|
|
53
55
|
const patientDetails = useMemo(() => {
|
|
54
56
|
const getGender = (gender: string): string => {
|
|
55
57
|
switch (gender) {
|
|
@@ -139,10 +141,9 @@ const VitalsOverview: React.FC<VitalsOverviewProps> = ({ patientUuid, patient, p
|
|
|
139
141
|
|
|
140
142
|
const tableRows: Array<VitalsTableRow> = useMemo(
|
|
141
143
|
() =>
|
|
142
|
-
vitals?.map((vitalSigns
|
|
144
|
+
vitals?.map((vitalSigns) => {
|
|
143
145
|
return {
|
|
144
146
|
...vitalSigns,
|
|
145
|
-
id: `${index}`,
|
|
146
147
|
dateRender: formatDate(parseDate(vitalSigns.date.toString()), { mode: 'wide', time: true }),
|
|
147
148
|
bloodPressureRender: `${vitalSigns.systolic ?? '--'} / ${vitalSigns.diastolic ?? '--'}`,
|
|
148
149
|
pulseRender: vitalSigns.pulse ?? '--',
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const VitalsAndBiometricsFormSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
systolicBloodPressure: z.number(),
|
|
6
|
+
diastolicBloodPressure: z.number(),
|
|
7
|
+
respiratoryRate: z.number(),
|
|
8
|
+
oxygenSaturation: z.number(),
|
|
9
|
+
pulse: z.number(),
|
|
10
|
+
temperature: z.number(),
|
|
11
|
+
generalPatientNote: z.string(),
|
|
12
|
+
weight: z.number(),
|
|
13
|
+
height: z.number(),
|
|
14
|
+
midUpperArmCircumference: z.number(),
|
|
15
|
+
computedBodyMassIndex: z.number(),
|
|
16
|
+
})
|
|
17
|
+
.partial()
|
|
18
|
+
.refine(
|
|
19
|
+
(fields) => {
|
|
20
|
+
return Object.values(fields).some((value) => Boolean(value));
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
message: 'Please fill at least one field',
|
|
24
|
+
path: ['oneFieldRequired'],
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export type VitalsBiometricsFormData = z.infer<typeof VitalsAndBiometricsFormSchema>;
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { screen, render } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import { type FetchResponse, showSnackbar, useConfig, getDefaultsFromConfigSchema } from '@openmrs/esm-framework';
|
|
5
|
-
import {
|
|
5
|
+
import { createOrUpdateVitalsAndBiometrics, useEncounterVitalsAndBiometrics } from '../common';
|
|
6
6
|
import { type ConfigObject, configSchema } from '../config-schema';
|
|
7
7
|
import { mockConceptMetadata, mockConceptRanges, mockConceptUnits, mockVitalsConfig } from '__mocks__';
|
|
8
8
|
import { mockPatient } from 'tools';
|
|
@@ -28,8 +28,9 @@ const testProps = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
const mockShowSnackbar = jest.mocked(showSnackbar);
|
|
31
|
-
const
|
|
31
|
+
const mockCreateOrUpdateVitalsAndBiometrics = jest.mocked(createOrUpdateVitalsAndBiometrics);
|
|
32
32
|
const mockUseConfig = jest.mocked(useConfig<ConfigObject>);
|
|
33
|
+
const mockUseEncounterVitalsAndBiometrics = jest.mocked(useEncounterVitalsAndBiometrics);
|
|
33
34
|
|
|
34
35
|
jest.mock('../common', () => ({
|
|
35
36
|
assessValue: jest.fn(),
|
|
@@ -37,13 +38,18 @@ jest.mock('../common', () => ({
|
|
|
37
38
|
generatePlaceholder: jest.fn(),
|
|
38
39
|
interpretBloodPressure: jest.fn(),
|
|
39
40
|
invalidateCachedVitalsAndBiometrics: jest.fn(),
|
|
40
|
-
|
|
41
|
+
createOrUpdateVitalsAndBiometrics: jest.fn(),
|
|
41
42
|
useVitalsAndBiometrics: jest.fn(),
|
|
42
43
|
useVitalsConceptMetadata: jest.fn().mockImplementation(() => ({
|
|
43
44
|
data: mockConceptUnits,
|
|
44
45
|
conceptMetadata: mockConceptMetadata,
|
|
45
46
|
conceptRanges: mockConceptRanges,
|
|
46
47
|
})),
|
|
48
|
+
useEncounterVitalsAndBiometrics: jest.fn().mockImplementation(() => ({
|
|
49
|
+
isLoading: false,
|
|
50
|
+
vitalsAndBiometrics: null,
|
|
51
|
+
mutate: jest.fn(),
|
|
52
|
+
})),
|
|
47
53
|
}));
|
|
48
54
|
|
|
49
55
|
mockUseConfig.mockReturnValue({
|
|
@@ -51,6 +57,91 @@ mockUseConfig.mockReturnValue({
|
|
|
51
57
|
...mockVitalsConfig,
|
|
52
58
|
});
|
|
53
59
|
|
|
60
|
+
function setupMockUseEncounterVitalsAndBiometrics() {
|
|
61
|
+
mockUseEncounterVitalsAndBiometrics.mockReturnValue({
|
|
62
|
+
isLoading: false,
|
|
63
|
+
vitalsAndBiometrics: new Map([
|
|
64
|
+
[
|
|
65
|
+
'systolicBloodPressure',
|
|
66
|
+
{
|
|
67
|
+
value: 120,
|
|
68
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174001', display: 'Systolic Blood Pressure: 120' },
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
'diastolicBloodPressure',
|
|
73
|
+
{
|
|
74
|
+
value: 80,
|
|
75
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174002', display: 'Diastolic Blood Pressure: 80' },
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
'pulse',
|
|
80
|
+
{
|
|
81
|
+
value: 75,
|
|
82
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174003', display: 'Pulse Rate: 75' },
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
'temperature',
|
|
87
|
+
{
|
|
88
|
+
value: 36.5,
|
|
89
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174004', display: 'Body Temperature: 36.5°C' },
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
'oxygenSaturation',
|
|
94
|
+
{
|
|
95
|
+
value: 98,
|
|
96
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174005', display: 'Oxygen Saturation: 98%' },
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
[
|
|
100
|
+
'height',
|
|
101
|
+
{
|
|
102
|
+
value: 170,
|
|
103
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174006', display: 'Height: 170 cm' },
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
'weight',
|
|
108
|
+
{
|
|
109
|
+
value: 65,
|
|
110
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174007', display: 'Weight: 65 kg' },
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
'respiratoryRate',
|
|
115
|
+
{
|
|
116
|
+
value: 16,
|
|
117
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174008', display: 'Respiratory Rate: 16 breaths/min' },
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
[
|
|
121
|
+
'midUpperArmCircumference',
|
|
122
|
+
{
|
|
123
|
+
value: 25,
|
|
124
|
+
obs: { uuid: '123e4567-e89b-12d3-a456-426614174009', display: 'Mid-Upper Arm Circumference: 25 cm' },
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
]),
|
|
128
|
+
encounter: null,
|
|
129
|
+
error: null,
|
|
130
|
+
mutate: jest.fn(),
|
|
131
|
+
getRefinedInitialValues: () => ({
|
|
132
|
+
height: 170,
|
|
133
|
+
weight: 65,
|
|
134
|
+
systolicBloodPressure: 120,
|
|
135
|
+
diastolicBloodPressure: 80,
|
|
136
|
+
pulse: 75,
|
|
137
|
+
oxygenSaturation: 98,
|
|
138
|
+
respiratoryRate: 16,
|
|
139
|
+
temperature: 36.5,
|
|
140
|
+
midUpperArmCircumference: 25,
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
54
145
|
describe('VitalsBiometricsForm', () => {
|
|
55
146
|
it('renders the vitals and biometrics form', async () => {
|
|
56
147
|
render(<VitalsAndBiometricsForm {...testProps} />);
|
|
@@ -107,7 +198,9 @@ describe('VitalsBiometricsForm', () => {
|
|
|
107
198
|
data: [],
|
|
108
199
|
};
|
|
109
200
|
|
|
110
|
-
|
|
201
|
+
mockCreateOrUpdateVitalsAndBiometrics.mockResolvedValue(
|
|
202
|
+
response as ReturnType<typeof createOrUpdateVitalsAndBiometrics>,
|
|
203
|
+
);
|
|
111
204
|
|
|
112
205
|
render(<VitalsAndBiometricsForm {...testProps} />);
|
|
113
206
|
|
|
@@ -141,24 +234,28 @@ describe('VitalsBiometricsForm', () => {
|
|
|
141
234
|
|
|
142
235
|
await user.click(saveButton);
|
|
143
236
|
|
|
144
|
-
expect(
|
|
145
|
-
expect(
|
|
146
|
-
mockVitalsConfig.vitals.encounterTypeUuid,
|
|
147
|
-
mockVitalsConfig.vitals.formUuid,
|
|
148
|
-
mockVitalsConfig.concepts,
|
|
237
|
+
expect(mockCreateOrUpdateVitalsAndBiometrics).toHaveBeenCalledTimes(1);
|
|
238
|
+
expect(mockCreateOrUpdateVitalsAndBiometrics).toHaveBeenCalledWith(
|
|
149
239
|
mockPatient.id,
|
|
240
|
+
mockVitalsConfig.vitals.encounterTypeUuid,
|
|
241
|
+
undefined,
|
|
242
|
+
undefined,
|
|
243
|
+
expect.arrayContaining([
|
|
244
|
+
{ concept: '5085AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 120 },
|
|
245
|
+
{ concept: '5242AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 16 },
|
|
246
|
+
{ concept: '5092AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 100 },
|
|
247
|
+
{ concept: '5087AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 80 },
|
|
248
|
+
{ concept: '5088AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 37 },
|
|
249
|
+
{ concept: '5089AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 62 },
|
|
250
|
+
{ concept: '5090AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 180 },
|
|
251
|
+
{ concept: '1343AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 23 },
|
|
252
|
+
]),
|
|
150
253
|
expect.objectContaining({
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
respiratoryRate: respiratoryRateValue,
|
|
156
|
-
systolicBloodPressure: systolicBloodPressureValue,
|
|
157
|
-
temperature: temperatureValue,
|
|
158
|
-
weight: weightValue,
|
|
254
|
+
signal: {
|
|
255
|
+
aborted: false,
|
|
256
|
+
},
|
|
257
|
+
abort: expect.any(Function),
|
|
159
258
|
}),
|
|
160
|
-
new AbortController(),
|
|
161
|
-
undefined,
|
|
162
259
|
);
|
|
163
260
|
|
|
164
261
|
expect(mockShowSnackbar).toHaveBeenCalledTimes(1);
|
|
@@ -172,6 +269,91 @@ describe('VitalsBiometricsForm', () => {
|
|
|
172
269
|
);
|
|
173
270
|
});
|
|
174
271
|
|
|
272
|
+
it('correctly initializes the form with existing vitals and biometrics data while in edit mode', async () => {
|
|
273
|
+
setupMockUseEncounterVitalsAndBiometrics();
|
|
274
|
+
render(<VitalsAndBiometricsForm {...testProps} formContext="editing" editEncounterUuid="encounter-uuid" />);
|
|
275
|
+
|
|
276
|
+
expect(screen.getByRole('spinbutton', { name: /height/i })).toHaveValue(170);
|
|
277
|
+
expect(screen.getByRole('spinbutton', { name: /weight/i })).toHaveValue(65);
|
|
278
|
+
expect(screen.getByRole('spinbutton', { name: /systolic/i })).toHaveValue(120);
|
|
279
|
+
expect(screen.getByRole('spinbutton', { name: /diastolic/i })).toHaveValue(80);
|
|
280
|
+
expect(screen.getByRole('spinbutton', { name: /pulse/i })).toHaveValue(75);
|
|
281
|
+
expect(screen.getByRole('spinbutton', { name: /oxygen saturation/i })).toHaveValue(98);
|
|
282
|
+
expect(screen.getByRole('spinbutton', { name: /respiration rate/i })).toHaveValue(16);
|
|
283
|
+
expect(screen.getByRole('spinbutton', { name: /temperature/i })).toHaveValue(36.5);
|
|
284
|
+
expect(screen.getByRole('spinbutton', { name: /muac/i })).toHaveValue(25);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('edits patient vitals and biometrics', async () => {
|
|
288
|
+
const user = userEvent.setup();
|
|
289
|
+
setupMockUseEncounterVitalsAndBiometrics();
|
|
290
|
+
|
|
291
|
+
const response: Partial<FetchResponse> = {
|
|
292
|
+
statusText: 'created',
|
|
293
|
+
status: 201,
|
|
294
|
+
data: [],
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
mockCreateOrUpdateVitalsAndBiometrics.mockResolvedValue(
|
|
298
|
+
response as ReturnType<typeof createOrUpdateVitalsAndBiometrics>,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
render(<VitalsAndBiometricsForm {...testProps} formContext="editing" editEncounterUuid="encounter-uuid" />);
|
|
302
|
+
|
|
303
|
+
const weightInput = screen.getByRole('spinbutton', { name: /weight/i });
|
|
304
|
+
const systolicInput = screen.getByRole('spinbutton', { name: /systolic/i });
|
|
305
|
+
const pulseInput = screen.getByRole('spinbutton', { name: /pulse/i });
|
|
306
|
+
const temperatureInput = screen.getByRole('spinbutton', { name: /temperature/i });
|
|
307
|
+
const saveButton = screen.getByRole('button', { name: /Save and close/i });
|
|
308
|
+
|
|
309
|
+
// the save button should be disabled until the user makes a change
|
|
310
|
+
expect(saveButton).toBeDisabled();
|
|
311
|
+
await user.clear(weightInput);
|
|
312
|
+
await user.type(weightInput, '70');
|
|
313
|
+
await user.clear(systolicInput);
|
|
314
|
+
await user.type(systolicInput, '130');
|
|
315
|
+
await user.clear(temperatureInput);
|
|
316
|
+
await user.type(temperatureInput, '37.5');
|
|
317
|
+
// delete the pulse value
|
|
318
|
+
await user.clear(pulseInput);
|
|
319
|
+
|
|
320
|
+
expect(saveButton).toBeEnabled();
|
|
321
|
+
await user.click(saveButton);
|
|
322
|
+
|
|
323
|
+
expect(mockCreateOrUpdateVitalsAndBiometrics).toHaveBeenCalledTimes(1);
|
|
324
|
+
expect(mockCreateOrUpdateVitalsAndBiometrics).toHaveBeenCalledWith(
|
|
325
|
+
mockPatient.id,
|
|
326
|
+
mockVitalsConfig.vitals.encounterTypeUuid,
|
|
327
|
+
'encounter-uuid',
|
|
328
|
+
undefined,
|
|
329
|
+
expect.arrayContaining([
|
|
330
|
+
{ concept: '5085AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 130 },
|
|
331
|
+
{ concept: '5088AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 37.5 },
|
|
332
|
+
{ concept: '5089AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', value: 70 },
|
|
333
|
+
{ uuid: '123e4567-e89b-12d3-a456-426614174001', voided: true },
|
|
334
|
+
{ uuid: '123e4567-e89b-12d3-a456-426614174003', voided: true },
|
|
335
|
+
{ uuid: '123e4567-e89b-12d3-a456-426614174004', voided: true },
|
|
336
|
+
{ uuid: '123e4567-e89b-12d3-a456-426614174007', voided: true },
|
|
337
|
+
]),
|
|
338
|
+
expect.objectContaining({
|
|
339
|
+
signal: {
|
|
340
|
+
aborted: false,
|
|
341
|
+
},
|
|
342
|
+
abort: expect.any(Function),
|
|
343
|
+
}),
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
expect(mockShowSnackbar).toHaveBeenCalledTimes(1);
|
|
347
|
+
expect(mockShowSnackbar).toHaveBeenCalledWith(
|
|
348
|
+
expect.objectContaining({
|
|
349
|
+
isLowContrast: true,
|
|
350
|
+
kind: 'success',
|
|
351
|
+
subtitle: 'They are now visible on the Vitals and Biometrics page',
|
|
352
|
+
title: 'Vitals and Biometrics updated',
|
|
353
|
+
}),
|
|
354
|
+
);
|
|
355
|
+
});
|
|
356
|
+
|
|
175
357
|
it('renders an error snackbar if there was a problem saving vitals and biometrics', async () => {
|
|
176
358
|
const user = userEvent.setup();
|
|
177
359
|
|
|
@@ -183,7 +365,7 @@ describe('VitalsBiometricsForm', () => {
|
|
|
183
365
|
},
|
|
184
366
|
};
|
|
185
367
|
|
|
186
|
-
|
|
368
|
+
mockCreateOrUpdateVitalsAndBiometrics.mockRejectedValueOnce(error);
|
|
187
369
|
|
|
188
370
|
render(<VitalsAndBiometricsForm {...testProps} />);
|
|
189
371
|
|
|
@@ -214,7 +396,7 @@ describe('VitalsBiometricsForm', () => {
|
|
|
214
396
|
isLowContrast: false,
|
|
215
397
|
kind: 'error',
|
|
216
398
|
subtitle: 'Some of the values entered are invalid',
|
|
217
|
-
title: 'Error saving
|
|
399
|
+
title: 'Error saving Vitals and Biometrics',
|
|
218
400
|
});
|
|
219
401
|
});
|
|
220
402
|
|