@kenyaemr/esm-ward-app 8.1.1-pre.121 → 8.1.1-pre.124
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 +8 -8
- package/dist/124.js +1 -1
- package/dist/124.js.map +1 -1
- package/dist/125.js +1 -1
- package/dist/125.js.map +1 -1
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/153.js +1 -1
- package/dist/153.js.map +1 -1
- package/dist/303.js +2 -0
- package/dist/303.js.map +1 -0
- package/dist/471.js +1 -1
- package/dist/471.js.map +1 -1
- package/dist/53.js +1 -1
- package/dist/53.js.map +1 -1
- package/dist/574.js +1 -1
- package/dist/577.js +1 -1
- package/dist/577.js.map +1 -1
- package/dist/662.js +1 -1
- package/dist/662.js.map +1 -1
- package/dist/921.js +1 -1
- package/dist/921.js.map +1 -1
- package/dist/922.js +1 -1
- package/dist/922.js.map +1 -1
- package/dist/kenyaemr-esm-ward-app.js +1 -1
- package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +60 -60
- package/dist/kenyaemr-esm-ward-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/beds/ward-bed.test.tsx +16 -5
- package/src/hooks/useWardPatientGrouping.ts +5 -0
- package/src/ward-view/materal-ward/maternal-ward-patient-card.test.tsx +11 -0
- package/src/ward-view/ward-view.scss +1 -1
- package/src/ward-view/ward-view.test.tsx +8 -0
- package/src/ward-view-header/ward-metric.scss +5 -5
- package/src/ward-view-header/ward-metrics.scss +1 -2
- package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +59 -6
- package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +45 -1
- package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +26 -5
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +30 -94
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +101 -189
- package/src/ward-workspace/bed-selector.component.tsx +119 -0
- package/src/ward-workspace/bed-selector.scss +15 -0
- package/src/ward-workspace/patient-discharge/patient-discharge.workspace.tsx +4 -11
- package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +56 -77
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +3 -10
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +10 -6
- package/src/ward.resource.ts +32 -2
- package/translations/en.json +6 -5
- package/dist/67.js +0 -2
- package/dist/67.js.map +0 -1
- /package/dist/{67.js.LICENSE.txt → 303.js.LICENSE.txt} +0 -0
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.2.0","emrapi":"^2.0.0 || 2.0.0-SNAPSHOT"},"optionalBackendDependencies":{"bedmanagement":{"version":"^6.0.0 || 6.0.0-SNAPSHOT","feature":{"flagName":"bedmanagement-module","label":"Ward App Patient Service","description":"This module, if installed, provides services for managing patients admitted to the ward."}}},"extensions":[{"name":"ward-dashboard-link","component":"wardDashboardLink","slot":"homepage-dashboard-slot","meta":{"name":"ward","slot":"ward-dashboard-slot","title":"Wards"}},{"component":"root","name":"ward-dashboard","slot":"ward-dashboard-slot"},{"component":"wardPatientActionButtonExtension","name":"ward-patient-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"wardPatientNotesActionButtonExtension","name":"ward-inpatient-notes-form-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"coloredObsTagCardRowExtension","name":"colored-obs-tags-card-row","slot":"ward-patient-card-slot"},{"name":"transfer-swap-patient-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientTransferAndSwapWorkspaceSiderailIcon"},{"name":"patient-discharge-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientDischargeWorkspaceSideRailIcon"},{"name":"clinical-forms-workspace-siderail-button","component":"clinicalFormWorkspaceSideRailIcon","slot":"action-menu-ward-patient-items-slot"},{"component":"defaultWardView","name":"default-ward","slot":"default-ward"},{"component":"maternalWardView","name":"maternal-ward","slot":"maternal-ward"}],"workspaces":[{"name":"admission-requests-workspace","component":"admissionRequestWorkspace","title":"admissionRequests","type":"admission-requests"},{"name":"ward-patient-notes-workspace","component":"wardPatientNotesWorkspace","type":"ward-patient-notes","title":"inpatientNotesWorkspaceTitle","sidebarFamily":"ward-patient","hasOwnSidebar":true},{"name":"admit-patient-form-workspace","component":"admitPatientFormWorkspace","title":"admissionRequests","type":"admission-requests"},{"name":"ward-patient-workspace","component":"wardPatientWorkspace","type":"ward","title":"Ward Patient","width":"extra-wide","hasOwnSidebar":true,"sidebarFamily":"ward-patient"},{"name":"patient-transfer-swap-workspace","component":"patientTransferAndSwapWorkspace","title":"transfers","type":"transfer-swap-bed-form","hasOwnSidebar":true,"sidebarFamily":"ward-patient"},{"name":"patient-transfer-request-workspace","component":"patientTransferRequestWorkspace","title":"transferRequest","type":"transfer-request-form"},{"name":"patient-discharge-workspace","component":"patientDischargeWorkspace","title":"discharge","type":"ward-patient-discharge","hasOwnSidebar":true,"sidebarFamily":"ward-patient"},{"name":"ward-patient-clinical-forms-workspace","component":"patientClinicalFormsWorkspace","title":"clinicalForms","type":"ward-patient-clinical-forms","hasOwnSidebar":true,"sidebarFamily":"ward-patient","width":"wider"}],"version":"8.1.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.2.0","emrapi":"^2.0.0 || 2.0.0-SNAPSHOT"},"optionalBackendDependencies":{"bedmanagement":{"version":"^6.0.0 || 6.0.0-SNAPSHOT","feature":{"flagName":"bedmanagement-module","label":"Ward App Patient Service","description":"This module, if installed, provides services for managing patients admitted to the ward."}}},"extensions":[{"name":"ward-dashboard-link","component":"wardDashboardLink","slot":"homepage-dashboard-slot","meta":{"name":"ward","slot":"ward-dashboard-slot","title":"Wards"}},{"component":"root","name":"ward-dashboard","slot":"ward-dashboard-slot"},{"component":"wardPatientActionButtonExtension","name":"ward-patient-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"wardPatientNotesActionButtonExtension","name":"ward-inpatient-notes-form-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"coloredObsTagCardRowExtension","name":"colored-obs-tags-card-row","slot":"ward-patient-card-slot"},{"name":"transfer-swap-patient-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientTransferAndSwapWorkspaceSiderailIcon"},{"name":"patient-discharge-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientDischargeWorkspaceSideRailIcon"},{"name":"clinical-forms-workspace-siderail-button","component":"clinicalFormWorkspaceSideRailIcon","slot":"action-menu-ward-patient-items-slot"},{"component":"defaultWardView","name":"default-ward","slot":"default-ward"},{"component":"maternalWardView","name":"maternal-ward","slot":"maternal-ward"}],"workspaces":[{"name":"admission-requests-workspace","component":"admissionRequestWorkspace","title":"admissionRequests","type":"admission-requests"},{"name":"ward-patient-notes-workspace","component":"wardPatientNotesWorkspace","type":"ward-patient-notes","title":"inpatientNotesWorkspaceTitle","sidebarFamily":"ward-patient","hasOwnSidebar":true},{"name":"admit-patient-form-workspace","component":"admitPatientFormWorkspace","title":"admissionRequests","type":"admission-requests"},{"name":"ward-patient-workspace","component":"wardPatientWorkspace","type":"ward","title":"Ward Patient","width":"extra-wide","hasOwnSidebar":true,"sidebarFamily":"ward-patient"},{"name":"patient-transfer-swap-workspace","component":"patientTransferAndSwapWorkspace","title":"transfers","type":"transfer-swap-bed-form","hasOwnSidebar":true,"sidebarFamily":"ward-patient"},{"name":"patient-transfer-request-workspace","component":"patientTransferRequestWorkspace","title":"transferRequest","type":"transfer-request-form"},{"name":"patient-discharge-workspace","component":"patientDischargeWorkspace","title":"discharge","type":"ward-patient-discharge","hasOwnSidebar":true,"sidebarFamily":"ward-patient"},{"name":"ward-patient-clinical-forms-workspace","component":"patientClinicalFormsWorkspace","title":"clinicalForms","type":"ward-patient-clinical-forms","hasOwnSidebar":true,"sidebarFamily":"ward-patient","width":"wider"}],"version":"8.1.1-pre.124"}
|
package/package.json
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
1
|
import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
4
|
-
import {
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import React from 'react';
|
|
5
4
|
import {
|
|
6
5
|
mockAdmissionLocation,
|
|
7
6
|
mockLocationInpatientWard,
|
|
8
7
|
mockPatientAlice,
|
|
9
8
|
mockPatientBrian,
|
|
10
9
|
} from '../../../../__mocks__';
|
|
11
|
-
import {
|
|
10
|
+
import { configSchema, type WardConfigObject } from '../config-schema';
|
|
11
|
+
import { useObs } from '../hooks/useObs';
|
|
12
12
|
import useWardLocation from '../hooks/useWardLocation';
|
|
13
|
-
import WardBed from './ward-bed.component';
|
|
14
13
|
import { type WardPatient } from '../types';
|
|
15
14
|
import DefaultWardPatientCard from '../ward-view/default-ward/default-ward-patient-card.component';
|
|
15
|
+
import { bedLayoutToBed, filterBeds } from '../ward-view/ward-view.resource';
|
|
16
|
+
import WardBed from './ward-bed.component';
|
|
16
17
|
|
|
17
18
|
const defaultConfig: WardConfigObject = getDefaultsFromConfigSchema(configSchema);
|
|
18
19
|
|
|
19
20
|
jest.mocked(useConfig).mockReturnValue(defaultConfig);
|
|
21
|
+
jest.mock('../hooks/useObs', () => ({
|
|
22
|
+
useObs: jest.fn(),
|
|
23
|
+
}));
|
|
24
|
+
jest.mock('../ward-patient-card/row-elements/ward-patient-obs.resource', () => ({
|
|
25
|
+
useConceptToTagColorMap: jest.fn(),
|
|
26
|
+
}));
|
|
20
27
|
|
|
21
28
|
const mockBedLayouts = filterBeds(mockAdmissionLocation);
|
|
22
29
|
|
|
23
30
|
jest.mock('../hooks/useWardLocation', () => jest.fn());
|
|
31
|
+
//@ts-ignore
|
|
32
|
+
jest.mocked(useObs).mockReturnValue({
|
|
33
|
+
data: [],
|
|
34
|
+
});
|
|
24
35
|
|
|
25
36
|
const mockedUseWardLocation = useWardLocation as jest.Mock;
|
|
26
37
|
mockedUseWardLocation.mockReturnValue({
|
|
@@ -23,5 +23,10 @@ export function useWardPatientGrouping() {
|
|
|
23
23
|
inpatientRequestResponse,
|
|
24
24
|
isLoading:
|
|
25
25
|
admissionLocationResponse.isLoading || inpatientAdmissionResponse.isLoading || inpatientRequestResponse.isLoading,
|
|
26
|
+
mutate() {
|
|
27
|
+
admissionLocationResponse?.mutate();
|
|
28
|
+
inpatientAdmissionResponse?.mutate();
|
|
29
|
+
inpatientRequestResponse?.mutate();
|
|
30
|
+
},
|
|
26
31
|
};
|
|
27
32
|
}
|
|
@@ -7,14 +7,25 @@ import { mockInpatientAdmissionAlice } from '../../../../../__mocks__/inpatient-
|
|
|
7
7
|
import { mockWardBeds } from '../../../../../__mocks__/wardBeds.mock';
|
|
8
8
|
import { mockWardViewContext } from '../../../mock';
|
|
9
9
|
import { configSchema, type WardConfigObject } from '../../config-schema';
|
|
10
|
+
import { useObs } from '../../hooks/useObs';
|
|
10
11
|
import { type WardPatient, type WardViewContext } from '../../types';
|
|
11
12
|
import MaternalWardPatientCard from './maternal-ward-patient-card.component';
|
|
12
13
|
|
|
13
14
|
jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
|
|
15
|
+
jest.mock('../../hooks/useObs', () => ({
|
|
16
|
+
useObs: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
jest.mock('../../ward-patient-card/row-elements/ward-patient-obs.resource', () => ({
|
|
19
|
+
useConceptToTagColorMap: jest.fn(),
|
|
20
|
+
}));
|
|
14
21
|
|
|
15
22
|
const defaultConfig: WardConfigObject = getDefaultsFromConfigSchema(configSchema);
|
|
16
23
|
|
|
17
24
|
jest.mocked(useConfig).mockReturnValue(defaultConfig);
|
|
25
|
+
//@ts-ignore
|
|
26
|
+
jest.mocked(useObs).mockReturnValue({
|
|
27
|
+
data: [],
|
|
28
|
+
});
|
|
18
29
|
|
|
19
30
|
describe('MaternalWardPatientCard', () => {
|
|
20
31
|
it('renders a patient with no child', () => {
|
|
@@ -11,6 +11,7 @@ import { useParams } from 'react-router-dom';
|
|
|
11
11
|
import { renderWithSwr } from 'tools';
|
|
12
12
|
import { mockWardPatientGroupDetails, mockWardViewContext } from '../../mock';
|
|
13
13
|
import { configSchema } from '../config-schema';
|
|
14
|
+
import { useObs } from '../hooks/useObs';
|
|
14
15
|
import useWardLocation from '../hooks/useWardLocation';
|
|
15
16
|
import { type WardViewContext } from '../types';
|
|
16
17
|
import DefaultWardView from './default-ward/default-ward-view.component';
|
|
@@ -30,6 +31,9 @@ jest.mock('../hooks/useWardLocation', () =>
|
|
|
30
31
|
invalidLocation: false,
|
|
31
32
|
}),
|
|
32
33
|
);
|
|
34
|
+
jest.mock('../hooks/useObs', () => ({
|
|
35
|
+
useObs: jest.fn(),
|
|
36
|
+
}));
|
|
33
37
|
|
|
34
38
|
const mockUseWardLocation = jest.mocked(useWardLocation);
|
|
35
39
|
|
|
@@ -40,6 +44,10 @@ jest.mock('react-router-dom', () => ({
|
|
|
40
44
|
const mockUseParams = useParams as jest.Mock;
|
|
41
45
|
|
|
42
46
|
jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
|
|
47
|
+
//@ts-ignore
|
|
48
|
+
jest.mocked(useObs).mockReturnValue({
|
|
49
|
+
data: [],
|
|
50
|
+
});
|
|
43
51
|
|
|
44
52
|
const intersectionObserverMock = () => ({
|
|
45
53
|
observe: () => null,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
@use '@carbon/
|
|
1
|
+
@use '@carbon/layout';
|
|
2
2
|
@use '@carbon/type';
|
|
3
|
-
@
|
|
3
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
4
|
|
|
5
5
|
.metric {
|
|
6
|
-
margin-left:
|
|
6
|
+
margin-left: layout.$spacing-05;
|
|
7
7
|
display: flex;
|
|
8
8
|
align-items: end;
|
|
9
9
|
gap: 5px;
|
|
@@ -20,6 +20,6 @@
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.skeleton {
|
|
23
|
-
height:
|
|
24
|
-
width:
|
|
23
|
+
height: layout.$spacing-05;
|
|
24
|
+
width: layout.$spacing-05;
|
|
25
25
|
}
|
package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { Button } from '@carbon/react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
ArrowRightIcon,
|
|
4
|
+
launchWorkspace,
|
|
5
|
+
showSnackbar,
|
|
6
|
+
useAppContext,
|
|
7
|
+
useFeatureFlag,
|
|
8
|
+
useLayoutType,
|
|
9
|
+
} from '@openmrs/esm-framework';
|
|
10
|
+
import React, { useCallback, useContext } from 'react';
|
|
4
11
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import
|
|
12
|
+
import useWardLocation from '../../hooks/useWardLocation';
|
|
13
|
+
import type { WardPatientCardType, WardPatientWorkspaceProps, WardViewContext } from '../../types';
|
|
14
|
+
import { useAdmitPatient } from '../../ward.resource';
|
|
15
|
+
import { AdmissionRequestsWorkspaceContext } from '../admission-request-workspace/admission-requests.workspace';
|
|
6
16
|
import type { AdmitPatientFormWorkspaceProps } from '../admit-patient-form-workspace/types';
|
|
7
17
|
import styles from './admission-request-card.scss';
|
|
8
18
|
|
|
@@ -10,8 +20,10 @@ const AdmissionRequestCardActions: WardPatientCardType = (wardPatient) => {
|
|
|
10
20
|
const { patient, inpatientRequest } = wardPatient;
|
|
11
21
|
const { dispositionType } = inpatientRequest;
|
|
12
22
|
const { t } = useTranslation();
|
|
23
|
+
const { location } = useWardLocation();
|
|
13
24
|
const responsiveSize = useLayoutType() === 'tablet' ? 'lg' : 'md';
|
|
14
|
-
const {WardPatientHeader} = useAppContext<WardViewContext>('ward-view-context') ?? {};
|
|
25
|
+
const { WardPatientHeader, wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
|
|
26
|
+
const { admitPatient, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useAdmitPatient();
|
|
15
27
|
|
|
16
28
|
const launchPatientAdmissionForm = useCallback(
|
|
17
29
|
() => launchWorkspace<AdmitPatientFormWorkspaceProps>('admit-patient-form-workspace', { patient, dispositionType }),
|
|
@@ -21,16 +33,57 @@ const AdmissionRequestCardActions: WardPatientCardType = (wardPatient) => {
|
|
|
21
33
|
const launchPatientTransferForm = useCallback(() => {
|
|
22
34
|
launchWorkspace<WardPatientWorkspaceProps>('patient-transfer-request-workspace', {
|
|
23
35
|
wardPatient,
|
|
24
|
-
WardPatientHeader
|
|
36
|
+
WardPatientHeader,
|
|
25
37
|
});
|
|
26
38
|
}, [wardPatient, WardPatientHeader]);
|
|
39
|
+
const isBedManagementModuleInstalled = useFeatureFlag('bedmanagement-module');
|
|
40
|
+
const { closeWorkspaceWithSavedChanges } = useContext(AdmissionRequestsWorkspaceContext);
|
|
41
|
+
|
|
42
|
+
// If bed management module is installed, open the next form
|
|
43
|
+
// for bed selection. If not, admit patient directly
|
|
44
|
+
const onAdmit = () => {
|
|
45
|
+
if (isBedManagementModuleInstalled) {
|
|
46
|
+
launchPatientAdmissionForm();
|
|
47
|
+
} else {
|
|
48
|
+
admitPatient(patient, dispositionType)
|
|
49
|
+
.then(
|
|
50
|
+
(response) => {
|
|
51
|
+
if (response && response?.ok) {
|
|
52
|
+
showSnackbar({
|
|
53
|
+
kind: 'success',
|
|
54
|
+
title: t('patientAdmittedSuccessfully', 'Patient admitted successfully'),
|
|
55
|
+
subtitle: t('patientAdmittedWoBed', 'Patient admitted successfully to {{location}}', {
|
|
56
|
+
location: location?.display,
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
(err: Error) => {
|
|
62
|
+
showSnackbar({
|
|
63
|
+
kind: 'error',
|
|
64
|
+
title: t('errorCreatingEncounter', 'Failed to admit patient'),
|
|
65
|
+
subtitle: err.message,
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
.finally(() => {
|
|
70
|
+
wardPatientGroupDetails?.mutate?.();
|
|
71
|
+
closeWorkspaceWithSavedChanges();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
27
75
|
|
|
28
76
|
return (
|
|
29
77
|
<div className={styles.admissionRequestActionBar}>
|
|
30
78
|
<Button kind="ghost" size={responsiveSize} onClick={launchPatientTransferForm}>
|
|
31
79
|
{t('transferElsewhere', 'Transfer elsewhere')}
|
|
32
80
|
</Button>
|
|
33
|
-
<Button
|
|
81
|
+
<Button
|
|
82
|
+
kind="ghost"
|
|
83
|
+
renderIcon={ArrowRightIcon}
|
|
84
|
+
size={responsiveSize}
|
|
85
|
+
disabled={isLoadingEmrConfiguration || errorFetchingEmrConfiguration}
|
|
86
|
+
onClick={onAdmit}>
|
|
34
87
|
{t('admitPatient', 'Admit patient')}
|
|
35
88
|
</Button>
|
|
36
89
|
</div>
|
package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx
CHANGED
|
@@ -3,12 +3,16 @@ import { screen } from '@testing-library/react';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { renderWithSwr } from '../../../../../tools';
|
|
5
5
|
import { mockWardViewContext } from '../../../mock';
|
|
6
|
+
import useEmrConfiguration from '../../hooks/useEmrConfiguration';
|
|
6
7
|
import { type WardViewContext } from '../../types';
|
|
7
8
|
import DefaultWardPendingPatients from '../../ward-view/default-ward/default-ward-pending-patients.component';
|
|
8
9
|
import AdmissionRequestsWorkspace, { type AdmissionRequestsWorkspaceProps } from './admission-requests.workspace';
|
|
9
10
|
|
|
10
11
|
jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
|
|
11
12
|
|
|
13
|
+
jest.mock('../../hooks/useEmrConfiguration', () => jest.fn());
|
|
14
|
+
const mockedUseEmrConfiguration = jest.mocked(useEmrConfiguration);
|
|
15
|
+
|
|
12
16
|
const workspaceProps: AdmissionRequestsWorkspaceProps = {
|
|
13
17
|
closeWorkspace: jest.fn(),
|
|
14
18
|
promptBeforeClosing: jest.fn(),
|
|
@@ -18,9 +22,49 @@ const workspaceProps: AdmissionRequestsWorkspaceProps = {
|
|
|
18
22
|
};
|
|
19
23
|
|
|
20
24
|
describe('Admission Requests Workspace', () => {
|
|
21
|
-
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
mockedUseEmrConfiguration.mockReturnValue({
|
|
27
|
+
isLoadingEmrConfiguration: false,
|
|
28
|
+
errorFetchingEmrConfiguration: null,
|
|
29
|
+
// @ts-ignore - we only need these keys for now
|
|
30
|
+
emrConfiguration: {
|
|
31
|
+
admissionEncounterType: {
|
|
32
|
+
uuid: 'admission-encounter-type-uuid',
|
|
33
|
+
display: 'Admission Encounter',
|
|
34
|
+
},
|
|
35
|
+
transferWithinHospitalEncounterType: {
|
|
36
|
+
uuid: 'transfer-within-hospital-encounter-type-uuid',
|
|
37
|
+
display: 'Transfer Within Hospital Encounter Type',
|
|
38
|
+
},
|
|
39
|
+
clinicianEncounterRole: {
|
|
40
|
+
uuid: 'clinician-encounter-role-uuid',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
mutateEmrConfiguration: jest.fn(),
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should render an admission request card', () => {
|
|
22
48
|
renderWithSwr(<AdmissionRequestsWorkspace {...workspaceProps} />);
|
|
23
49
|
const alice = mockWardViewContext.wardPatientGroupDetails.inpatientRequestResponse.inpatientRequests[0].patient;
|
|
24
50
|
expect(screen.getByText(alice.person?.preferredName?.display as string)).toBeInTheDocument();
|
|
25
51
|
});
|
|
52
|
+
|
|
53
|
+
it('should render an admission request card with disabled "admit patient" button when emr config fails to load', () => {
|
|
54
|
+
mockedUseEmrConfiguration.mockReturnValue({
|
|
55
|
+
isLoadingEmrConfiguration: false,
|
|
56
|
+
errorFetchingEmrConfiguration: true,
|
|
57
|
+
emrConfiguration: null,
|
|
58
|
+
mutateEmrConfiguration: jest.fn(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
renderWithSwr(<AdmissionRequestsWorkspace {...workspaceProps} />);
|
|
62
|
+
expect(screen.getByText("Some parts of the form didn't load")).toBeInTheDocument();
|
|
63
|
+
expect(
|
|
64
|
+
screen.getByText(
|
|
65
|
+
'Fetching EMR configuration failed. Try refreshing the page or contact your system administrator.',
|
|
66
|
+
),
|
|
67
|
+
).toBeInTheDocument();
|
|
68
|
+
expect(screen.getByRole('button', { name: /Admit patient/i })).toBeDisabled();
|
|
69
|
+
});
|
|
26
70
|
});
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import { Search } from '@carbon/react';
|
|
1
|
+
import { InlineNotification, Search } from '@carbon/react';
|
|
2
2
|
import { type DefaultWorkspaceProps } from '@openmrs/esm-framework';
|
|
3
|
-
import React, { type ReactNode } from 'react';
|
|
3
|
+
import React, { createContext, type ReactNode } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import useEmrConfiguration from '../../hooks/useEmrConfiguration';
|
|
5
6
|
import styles from './admission-requests-workspace.scss';
|
|
6
7
|
|
|
7
8
|
export interface AdmissionRequestsWorkspaceProps extends DefaultWorkspaceProps {
|
|
8
9
|
wardPendingPatients: ReactNode;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
export const AdmissionRequestsWorkspaceContext = createContext<AdmissionRequestsWorkspaceProps>(null);
|
|
13
|
+
|
|
14
|
+
const AdmissionRequestsWorkspace: React.FC<AdmissionRequestsWorkspaceProps> = (props) => {
|
|
15
|
+
const { wardPendingPatients } = props;
|
|
11
16
|
const { t } = useTranslation();
|
|
12
17
|
const [searchTerm, setSearchTerm] = React.useState('');
|
|
13
18
|
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
14
19
|
setSearchTerm(event.target.value);
|
|
15
20
|
};
|
|
21
|
+
const { errorFetchingEmrConfiguration } = useEmrConfiguration();
|
|
16
22
|
|
|
17
23
|
return (
|
|
18
24
|
<div className={styles.admissionRequestsWorkspaceContainer}>
|
|
@@ -24,8 +30,23 @@ const AdmissionRequestsWorkspace: React.FC<AdmissionRequestsWorkspaceProps> = ({
|
|
|
24
30
|
placeholder={t('searchForPatient', 'Search for a patient')}
|
|
25
31
|
disabled
|
|
26
32
|
/>
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
{errorFetchingEmrConfiguration && (
|
|
34
|
+
<div className={styles.formError}>
|
|
35
|
+
<InlineNotification
|
|
36
|
+
kind="error"
|
|
37
|
+
title={t('somePartsOfTheFormDidntLoad', "Some parts of the form didn't load")}
|
|
38
|
+
subtitle={t(
|
|
39
|
+
'fetchingEmrConfigurationFailed',
|
|
40
|
+
'Fetching EMR configuration failed. Try refreshing the page or contact your system administrator.',
|
|
41
|
+
)}
|
|
42
|
+
lowContrast
|
|
43
|
+
hideCloseButton
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
)}
|
|
47
|
+
<AdmissionRequestsWorkspaceContext.Provider value={props}>
|
|
48
|
+
<div className={styles.content}>{wardPendingPatients}</div>
|
|
49
|
+
</AdmissionRequestsWorkspaceContext.Provider>
|
|
29
50
|
</div>
|
|
30
51
|
);
|
|
31
52
|
};
|
|
@@ -2,14 +2,13 @@ import { showSnackbar, useAppContext, useFeatureFlag, useSession } from '@openmr
|
|
|
2
2
|
import { screen } from '@testing-library/react';
|
|
3
3
|
import userEvent from '@testing-library/user-event';
|
|
4
4
|
import React from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { mockLocationInpatientWard, mockPatientAlice } from '../../../../../__mocks__';
|
|
6
6
|
import { renderWithSwr } from '../../../../../tools';
|
|
7
7
|
import { mockWardPatientGroupDetails, mockWardViewContext } from '../../../mock';
|
|
8
8
|
import { useAssignedBedByPatient } from '../../hooks/useAssignedBedByPatient';
|
|
9
|
-
import useEmrConfiguration from '../../hooks/useEmrConfiguration';
|
|
10
9
|
import useWardLocation from '../../hooks/useWardLocation';
|
|
11
10
|
import type { DispositionType, WardViewContext } from '../../types';
|
|
12
|
-
import { assignPatientToBed,
|
|
11
|
+
import { assignPatientToBed, removePatientFromBed, useAdmitPatient } from '../../ward.resource';
|
|
13
12
|
import AdmitPatientFormWorkspace from './admit-patient-form.workspace';
|
|
14
13
|
import type { AdmitPatientFormWorkspaceProps } from './types';
|
|
15
14
|
|
|
@@ -19,8 +18,6 @@ jest.mock('../../hooks/useAdmissionLocation', () => ({
|
|
|
19
18
|
|
|
20
19
|
jest.mock('../../hooks/useWardLocation', () => jest.fn());
|
|
21
20
|
|
|
22
|
-
jest.mock('../../hooks/useEmrConfiguration', () => jest.fn());
|
|
23
|
-
|
|
24
21
|
jest.mock('../../hooks/useInpatientRequest', () => ({
|
|
25
22
|
useInpatientRequest: jest.fn(),
|
|
26
23
|
}));
|
|
@@ -39,22 +36,30 @@ jest.mock('../../hooks/useAssignedBedByPatient', () => ({
|
|
|
39
36
|
|
|
40
37
|
jest.mock('../../ward.resource', () => ({
|
|
41
38
|
createEncounter: jest.fn(),
|
|
39
|
+
useAdmitPatient: jest.fn(),
|
|
42
40
|
assignPatientToBed: jest.fn(),
|
|
43
41
|
removePatientFromBed: jest.fn(),
|
|
44
42
|
}));
|
|
45
43
|
|
|
46
|
-
const mockedUseEmrConfiguration = jest.mocked(useEmrConfiguration);
|
|
47
44
|
const mockedUseWardLocation = jest.mocked(useWardLocation);
|
|
48
45
|
const mockedUseFeatureFlag = jest.mocked(useFeatureFlag);
|
|
49
46
|
const mockedShowSnackbar = jest.mocked(showSnackbar);
|
|
50
47
|
const mockedUseSession = jest.mocked(useSession);
|
|
51
48
|
const mockedUseAssignedBedByPatient = jest.mocked(useAssignedBedByPatient);
|
|
52
49
|
const mockedAssignPatientToBed = jest.mocked(assignPatientToBed);
|
|
53
|
-
const mockedCreateEncounter = jest.mocked(createEncounter);
|
|
54
50
|
const mockedRemovePatientFromBed = jest.mocked(removePatientFromBed);
|
|
51
|
+
const mockedUseAdmitPatient = jest.mocked(useAdmitPatient);
|
|
55
52
|
|
|
56
53
|
jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
|
|
57
54
|
|
|
55
|
+
const mockUseAdmitPatientObj: ReturnType<typeof useAdmitPatient> = {
|
|
56
|
+
admitPatient: jest.fn(),
|
|
57
|
+
isLoadingEmrConfiguration: false,
|
|
58
|
+
errorFetchingEmrConfiguration: false,
|
|
59
|
+
};
|
|
60
|
+
jest.mocked(useAdmitPatient).mockReturnValue(mockUseAdmitPatientObj);
|
|
61
|
+
const mockedAdmitPatient = mockUseAdmitPatientObj.admitPatient;
|
|
62
|
+
|
|
58
63
|
const mockWorkspaceProps: AdmitPatientFormWorkspaceProps = {
|
|
59
64
|
patient: mockPatientAlice,
|
|
60
65
|
closeWorkspace: jest.fn(),
|
|
@@ -81,31 +86,15 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
81
86
|
sessionId: 'session-id',
|
|
82
87
|
});
|
|
83
88
|
mockedUseFeatureFlag.mockReturnValue(true);
|
|
84
|
-
|
|
85
|
-
isLoadingEmrConfiguration: false,
|
|
86
|
-
errorFetchingEmrConfiguration: null,
|
|
87
|
-
// @ts-ignore - we only need these two keys for now
|
|
88
|
-
emrConfiguration: {
|
|
89
|
-
admissionEncounterType: {
|
|
90
|
-
uuid: 'admission-encounter-type-uuid',
|
|
91
|
-
display: 'Admission Encounter',
|
|
92
|
-
},
|
|
93
|
-
transferWithinHospitalEncounterType: {
|
|
94
|
-
uuid: 'transfer-within-hospital-encounter-type-uuid',
|
|
95
|
-
display: 'Transfer Within Hospital Encounter Type',
|
|
96
|
-
},
|
|
97
|
-
clinicianEncounterRole: {
|
|
98
|
-
uuid: 'clinician-encounter-role-uuid',
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
mutateEmrConfiguration: jest.fn(),
|
|
102
|
-
});
|
|
89
|
+
|
|
103
90
|
mockedUseWardLocation.mockReturnValue({
|
|
104
91
|
location: mockLocationInpatientWard,
|
|
105
92
|
invalidLocation: false,
|
|
106
93
|
isLoadingLocation: false,
|
|
107
94
|
errorFetchingLocation: null,
|
|
108
95
|
});
|
|
96
|
+
|
|
97
|
+
// @ts-ignore - we don't need to mock the entire object
|
|
109
98
|
mockedUseAssignedBedByPatient.mockReturnValue({
|
|
110
99
|
data: {
|
|
111
100
|
data: {
|
|
@@ -120,10 +109,10 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
120
109
|
],
|
|
121
110
|
},
|
|
122
111
|
},
|
|
123
|
-
}
|
|
112
|
+
});
|
|
124
113
|
|
|
125
114
|
// @ts-ignore - we only need these two keys for now
|
|
126
|
-
|
|
115
|
+
mockedAdmitPatient.mockResolvedValue({
|
|
127
116
|
ok: true,
|
|
128
117
|
data: {
|
|
129
118
|
uuid: 'encounter-uuid',
|
|
@@ -151,43 +140,24 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
151
140
|
});
|
|
152
141
|
screen.getByText('Admit');
|
|
153
142
|
expect(screen.getByText('Select a bed')).toBeInTheDocument();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
);
|
|
159
|
-
expect(screen.getByText('bed1 · Alice Johnson')).toBeInTheDocument();
|
|
160
|
-
expect(screen.getByText('bed2 · Empty')).toBeInTheDocument();
|
|
161
|
-
expect(screen.getByText('bed3 · Empty')).toBeInTheDocument();
|
|
162
|
-
expect(screen.getByText('bed4 · Empty')).toBeInTheDocument();
|
|
143
|
+
|
|
144
|
+
expect(screen.getByRole('radio', { name: 'bed1 · Alice Johnson' })).toBeInTheDocument();
|
|
145
|
+
expect(screen.getByRole('radio', { name: 'bed2 · Empty' })).toBeInTheDocument();
|
|
146
|
+
expect(screen.getByRole('radio', { name: 'bed3 · Empty' })).toBeInTheDocument();
|
|
147
|
+
expect(screen.getByRole('radio', { name: 'bed4 · Empty' })).toBeInTheDocument();
|
|
163
148
|
});
|
|
164
149
|
|
|
165
150
|
it('should block the form if emr configuration is not fetched properly', () => {
|
|
166
|
-
|
|
151
|
+
mockedUseAdmitPatient.mockReturnValueOnce({
|
|
152
|
+
admitPatient: mockedAdmitPatient,
|
|
167
153
|
isLoadingEmrConfiguration: false,
|
|
168
154
|
errorFetchingEmrConfiguration: true,
|
|
169
|
-
emrConfiguration: null,
|
|
170
|
-
mutateEmrConfiguration: jest.fn(),
|
|
171
155
|
});
|
|
172
156
|
|
|
173
157
|
renderAdmissionForm();
|
|
174
158
|
|
|
175
159
|
const admitButton = screen.getByText('Admit');
|
|
176
160
|
expect(admitButton).toBeDisabled();
|
|
177
|
-
expect(screen.getByText("Some parts of the form didn't load")).toBeInTheDocument();
|
|
178
|
-
expect(
|
|
179
|
-
screen.getByText(
|
|
180
|
-
'Fetching EMR configuration failed. Try refreshing the page or contact your system administrator.',
|
|
181
|
-
),
|
|
182
|
-
).toBeInTheDocument();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('should render admit patient form if bed management module is not present', () => {
|
|
186
|
-
mockedUseFeatureFlag.mockReturnValue(false);
|
|
187
|
-
renderAdmissionForm();
|
|
188
|
-
expect(screen.getByText('Select a bed')).toBeInTheDocument();
|
|
189
|
-
expect(screen.getByText('Unable to select beds')).toBeInTheDocument();
|
|
190
|
-
expect(screen.getByText('Bed management module is not present to allow bed selection')).toBeInTheDocument();
|
|
191
161
|
});
|
|
192
162
|
|
|
193
163
|
it('should render admit patient form if bed management module is present, but no beds are configured', () => {
|
|
@@ -202,27 +172,12 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
202
172
|
it('should submit the form, create encounter and submit bed', async () => {
|
|
203
173
|
const user = userEvent.setup();
|
|
204
174
|
renderAdmissionForm();
|
|
205
|
-
const
|
|
206
|
-
name: 'Choose an option',
|
|
207
|
-
});
|
|
208
|
-
await user.click(combobox);
|
|
209
|
-
const bedOption = screen.getByText('bed3 · Empty');
|
|
175
|
+
const bedOption = screen.getByRole('radio', { name: 'bed3 · Empty' });
|
|
210
176
|
await user.click(bedOption);
|
|
211
177
|
const admitButton = screen.getByRole('button', { name: 'Admit' });
|
|
212
178
|
expect(admitButton).toBeEnabled();
|
|
213
179
|
await user.click(admitButton);
|
|
214
|
-
expect(
|
|
215
|
-
patient: mockPatientAlice.uuid,
|
|
216
|
-
encounterType: 'admission-encounter-type-uuid',
|
|
217
|
-
location: mockAdmissionLocation.ward.uuid,
|
|
218
|
-
obs: [],
|
|
219
|
-
encounterProviders: [
|
|
220
|
-
{
|
|
221
|
-
provider: 'current-provider-uuid',
|
|
222
|
-
encounterRole: 'clinician-encounter-role-uuid',
|
|
223
|
-
},
|
|
224
|
-
],
|
|
225
|
-
});
|
|
180
|
+
expect(mockedAdmitPatient).toHaveBeenCalledWith(mockPatientAlice, 'ADMIT');
|
|
226
181
|
expect(mockedAssignPatientToBed).toHaveBeenCalledWith(3, mockPatientAlice.uuid, 'encounter-uuid');
|
|
227
182
|
expect(mockedShowSnackbar).toHaveBeenCalledWith({
|
|
228
183
|
kind: 'success',
|
|
@@ -232,14 +187,10 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
232
187
|
});
|
|
233
188
|
|
|
234
189
|
it('should show snackbar if there was an issue creating an encounter', async () => {
|
|
235
|
-
|
|
190
|
+
mockedAdmitPatient.mockRejectedValue(new Error('Failed to create encounter'));
|
|
236
191
|
const user = userEvent.setup();
|
|
237
192
|
renderAdmissionForm();
|
|
238
|
-
const
|
|
239
|
-
name: 'Choose an option',
|
|
240
|
-
});
|
|
241
|
-
await user.click(combobox);
|
|
242
|
-
const bedOption = screen.getByText('bed3 · Empty');
|
|
193
|
+
const bedOption = screen.getByRole('radio', { name: 'bed3 · Empty' });
|
|
243
194
|
await user.click(bedOption);
|
|
244
195
|
const admitButton = screen.getByRole('button', { name: 'Admit' });
|
|
245
196
|
expect(admitButton).toBeEnabled();
|
|
@@ -256,11 +207,7 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
256
207
|
|
|
257
208
|
const user = userEvent.setup();
|
|
258
209
|
renderAdmissionForm();
|
|
259
|
-
const
|
|
260
|
-
name: 'Choose an option',
|
|
261
|
-
});
|
|
262
|
-
await user.click(combobox);
|
|
263
|
-
const bedOption = screen.getByText('bed3 · Empty');
|
|
210
|
+
const bedOption = screen.getByRole('radio', { name: 'bed3 · Empty' });
|
|
264
211
|
await user.click(bedOption);
|
|
265
212
|
const admitButton = screen.getByRole('button', { name: 'Admit' });
|
|
266
213
|
expect(admitButton).toBeEnabled();
|
|
@@ -278,18 +225,7 @@ describe('Testing AdmitPatientForm', () => {
|
|
|
278
225
|
const admitButton = screen.getByRole('button', { name: 'Admit' });
|
|
279
226
|
expect(admitButton).toBeEnabled();
|
|
280
227
|
await user.click(admitButton);
|
|
281
|
-
expect(
|
|
282
|
-
patient: mockPatientAlice.uuid,
|
|
283
|
-
encounterType: 'admission-encounter-type-uuid',
|
|
284
|
-
location: mockAdmissionLocation.ward.uuid,
|
|
285
|
-
obs: [],
|
|
286
|
-
encounterProviders: [
|
|
287
|
-
{
|
|
288
|
-
provider: 'current-provider-uuid',
|
|
289
|
-
encounterRole: 'clinician-encounter-role-uuid',
|
|
290
|
-
},
|
|
291
|
-
],
|
|
292
|
-
});
|
|
228
|
+
expect(mockedAdmitPatient).toHaveBeenCalledWith(mockPatientAlice, 'ADMIT');
|
|
293
229
|
expect(mockedRemovePatientFromBed).toHaveBeenCalledWith(1, mockPatientAlice.uuid);
|
|
294
230
|
expect(mockedShowSnackbar).toHaveBeenCalledWith({
|
|
295
231
|
kind: 'success',
|