@openmrs/esm-ward-app 7.0.1-pre.60 → 7.1.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.
- package/.turbo/turbo-build.log +23 -27
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/152.js +1 -0
- package/dist/152.js.map +1 -0
- package/dist/255.js +2 -0
- package/dist/{697.js.LICENSE.txt → 255.js.LICENSE.txt} +0 -6
- package/dist/255.js.map +1 -0
- package/dist/303.js +1 -0
- package/dist/303.js.map +1 -0
- package/dist/331.js +1 -0
- package/dist/331.js.map +1 -0
- package/dist/430.js +1 -0
- package/dist/430.js.map +1 -0
- package/dist/574.js +1 -1
- package/dist/747.js +1 -0
- package/dist/747.js.map +1 -0
- package/dist/836.js +2 -0
- package/dist/836.js.LICENSE.txt +5 -0
- package/dist/836.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-ward-app.js +1 -1
- package/dist/openmrs-esm-ward-app.js.buildmanifest.json +190 -46
- package/dist/openmrs-esm-ward-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -3
- package/src/beds/occupied-bed.component.tsx +7 -12
- package/src/beds/occupied-bed.scss +1 -1
- package/src/beds/occupied-bed.test.tsx +14 -5
- package/src/config-schema.ts +173 -7
- package/src/constant.ts +1 -0
- package/src/createDashboardLink.component.tsx +42 -0
- package/src/hooks/useAdmissionLocation.ts +4 -5
- package/src/hooks/useAdmittedPatients.ts +13 -0
- package/src/hooks/useConcept.ts +11 -0
- package/src/hooks/useInpatientRequest.ts +15 -0
- package/src/hooks/useLocation.test.ts +38 -0
- package/src/hooks/useLocation.ts +9 -0
- package/src/hooks/useObs.ts +21 -0
- package/src/hooks/useWardLocation.test.ts +78 -0
- package/src/hooks/useWardLocation.ts +26 -0
- package/src/index.ts +24 -3
- package/src/root.component.tsx +2 -1
- package/src/routes.json +37 -7
- package/src/types/index.ts +40 -2
- package/src/ward-patient-card/row-elements/ward-patient-age.tsx +3 -1
- package/src/ward-patient-card/row-elements/ward-patient-bed-number.tsx +3 -0
- package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +80 -0
- package/src/ward-patient-card/row-elements/ward-patient-header-address.tsx +3 -1
- package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +52 -0
- package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +58 -0
- package/src/ward-patient-card/ward-patient-card-row.resources.tsx +12 -5
- package/src/ward-patient-card/ward-patient-card.scss +43 -0
- package/src/ward-patient-card/ward-patient-card.tsx +12 -2
- package/src/ward-patient-workspace/ward-patient-action-button.extension.tsx +18 -0
- package/src/ward-patient-workspace/ward-patient.style.scss +11 -0
- package/src/ward-patient-workspace/ward-patient.workspace.tsx +79 -0
- package/src/ward-view/ward-bed.component.tsx +6 -9
- package/src/ward-view/ward-view.component.tsx +80 -39
- package/src/ward-view/ward-view.scss +0 -15
- package/src/ward-view/ward-view.test.tsx +38 -25
- package/src/ward-view-header/admission-requests-bar.component.tsx +50 -0
- package/src/ward-view-header/admission-requests-bar.test.tsx +43 -0
- package/src/ward-view-header/admission-requests.scss +33 -0
- package/src/ward-view-header/ward-view-header.component.tsx +18 -0
- package/src/ward-view-header/ward-view-header.scss +8 -0
- package/src/ward-workspace/admission-request-card.component.tsx +23 -0
- package/src/ward-workspace/admission-request-card.scss +34 -0
- package/src/ward-workspace/admission-request-workspace.test.tsx +32 -0
- package/src/ward-workspace/admission-requests-workspace.component.tsx +21 -0
- package/src/ward-workspace/admission-requests-workspace.scss +13 -0
- package/translations/en.json +6 -1
- package/dist/49.js +0 -1
- package/dist/49.js.map +0 -1
- package/dist/697.js +0 -2
- package/dist/697.js.map +0 -1
|
@@ -1,68 +1,100 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
1
2
|
import { InlineNotification } from '@carbon/react';
|
|
2
|
-
import { useFeatureFlag, useLocations, useSession, type Location } from '@openmrs/esm-framework';
|
|
3
|
-
import React from 'react';
|
|
4
3
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import {
|
|
4
|
+
import { WorkspaceContainer, useFeatureFlag } from '@openmrs/esm-framework';
|
|
6
5
|
import EmptyBedSkeleton from '../beds/empty-bed-skeleton';
|
|
7
6
|
import { useAdmissionLocation } from '../hooks/useAdmissionLocation';
|
|
8
7
|
import WardBed from './ward-bed.component';
|
|
9
8
|
import { bedLayoutToBed, filterBeds } from './ward-view.resource';
|
|
10
9
|
import styles from './ward-view.scss';
|
|
10
|
+
import WardViewHeader from '../ward-view-header/ward-view-header.component';
|
|
11
|
+
import { type AdmittedPatient, type WardPatient } from '../types';
|
|
12
|
+
import { useAdmittedPatients } from '../hooks/useAdmittedPatients';
|
|
13
|
+
import useWardLocation from '../hooks/useWardLocation';
|
|
11
14
|
|
|
12
15
|
const WardView = () => {
|
|
13
|
-
const
|
|
14
|
-
const {
|
|
15
|
-
|
|
16
|
+
const response = useWardLocation();
|
|
17
|
+
const { isLoadingLocation, errorFetchingLocation, invalidLocation } = response;
|
|
18
|
+
|
|
16
19
|
const { t } = useTranslation();
|
|
17
20
|
const isBedManagementModuleInstalled = useFeatureFlag('bedmanagement-module');
|
|
18
|
-
|
|
19
|
-
const invalidLocation = Boolean(locationUuidFromUrl && !locationFromUrl);
|
|
20
|
-
const location = (locationFromUrl ?? sessionLocation) as any as Location;
|
|
21
|
+
|
|
21
22
|
//TODO:Display patients with admitted status (based on their observations) that have no beds assigned
|
|
22
|
-
if (!isBedManagementModuleInstalled) {
|
|
23
|
+
if (!isBedManagementModuleInstalled || isLoadingLocation) {
|
|
23
24
|
return <></>;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
if (invalidLocation) {
|
|
28
|
+
return <InlineNotification kind="error" title={t('invalidLocationSpecified', 'Invalid location specified')} />;
|
|
29
|
+
}
|
|
30
|
+
|
|
26
31
|
return (
|
|
27
32
|
<div className={styles.wardView}>
|
|
28
|
-
<
|
|
29
|
-
<div className={styles.wardViewHeaderLocationDisplay}>
|
|
30
|
-
<h4>{location?.display}</h4>
|
|
31
|
-
</div>
|
|
32
|
-
<div className={styles.wardViewHeaderAdmissionRequestMenuBar}>{/* TODO: Admission Request bar */}</div>
|
|
33
|
-
</div>
|
|
33
|
+
<WardViewHeader />
|
|
34
34
|
<div className={styles.wardViewMain}>
|
|
35
|
-
|
|
36
|
-
<InlineNotification
|
|
37
|
-
kind="error"
|
|
38
|
-
lowContrast={true}
|
|
39
|
-
title={t('invalidLocationSpecified', 'Invalid location specified')}
|
|
40
|
-
subtitle={t('unknownLocationUuid', 'Unknown location uuid: {{locationUuidFromUrl}}', {
|
|
41
|
-
locationUuidFromUrl,
|
|
42
|
-
})}
|
|
43
|
-
/>
|
|
44
|
-
) : (
|
|
45
|
-
<WardViewByLocation location={location} />
|
|
46
|
-
)}
|
|
35
|
+
<WardViewByLocation />
|
|
47
36
|
</div>
|
|
37
|
+
<WorkspaceContainer overlay contextKey="ward" />
|
|
48
38
|
</div>
|
|
49
39
|
);
|
|
50
40
|
};
|
|
51
41
|
|
|
52
|
-
const WardViewByLocation = (
|
|
53
|
-
const {
|
|
42
|
+
const WardViewByLocation = () => {
|
|
43
|
+
const { location } = useWardLocation();
|
|
44
|
+
const {
|
|
45
|
+
admissionLocation,
|
|
46
|
+
isLoading: isLoadingLocation,
|
|
47
|
+
error: errorLoadingLocation,
|
|
48
|
+
} = useAdmissionLocation(location?.uuid);
|
|
49
|
+
const {
|
|
50
|
+
admittedPatients,
|
|
51
|
+
isLoading: isLoadingPatients,
|
|
52
|
+
error: errorLoadingPatients,
|
|
53
|
+
} = useAdmittedPatients(location.uuid);
|
|
54
54
|
const { t } = useTranslation();
|
|
55
|
+
const admittedPatientsByUuid = useMemo(() => {
|
|
56
|
+
const map = new Map<string, AdmittedPatient>();
|
|
57
|
+
for (const admittedPatient of admittedPatients ?? []) {
|
|
58
|
+
map.set(admittedPatient.patient.uuid, admittedPatient);
|
|
59
|
+
}
|
|
60
|
+
return map;
|
|
61
|
+
}, [admittedPatients]);
|
|
55
62
|
|
|
56
|
-
if (admissionLocation) {
|
|
63
|
+
if (admissionLocation != null && admittedPatients != null) {
|
|
57
64
|
const bedLayouts = filterBeds(admissionLocation);
|
|
58
65
|
|
|
66
|
+
const wardBeds = bedLayouts.map((bedLayout, i) => {
|
|
67
|
+
const { patients } = bedLayout;
|
|
68
|
+
const bed = bedLayoutToBed(bedLayout);
|
|
69
|
+
const wardPatients: WardPatient[] = patients.map((patient) => {
|
|
70
|
+
const admittedPatient = admittedPatientsByUuid.get(patient.uuid);
|
|
71
|
+
|
|
72
|
+
if (admittedPatient) {
|
|
73
|
+
// ideally, we can just use the patient object within admittedPatient
|
|
74
|
+
// and not need the one from bedLayouts, however, the emr api
|
|
75
|
+
// does not respect custom representation right now and does not return
|
|
76
|
+
// all required fields for the patient object
|
|
77
|
+
// TODO: change after this is done. https://openmrs.atlassian.net/browse/EA-192
|
|
78
|
+
return { ...admittedPatient, patient, admitted: true };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// patient assigned a bed but *not* admitted
|
|
82
|
+
// TODO: get the patient's visit and current location
|
|
83
|
+
return {
|
|
84
|
+
patient,
|
|
85
|
+
visit: null,
|
|
86
|
+
admitted: true,
|
|
87
|
+
currentLocation: null,
|
|
88
|
+
timeSinceAdmissionInMinutes: null,
|
|
89
|
+
timeAtInpatientLocationInMinutes: null,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
return <WardBed key={bed.uuid} bed={bed} wardPatients={wardPatients} />;
|
|
93
|
+
});
|
|
94
|
+
|
|
59
95
|
return (
|
|
60
96
|
<>
|
|
61
|
-
{
|
|
62
|
-
const { patient } = bedLayout;
|
|
63
|
-
const bed = bedLayoutToBed(bedLayout);
|
|
64
|
-
return <WardBed key={bed.uuid} bed={bed} patients={patient ? [patient] : null} />;
|
|
65
|
-
})}
|
|
97
|
+
{wardBeds}
|
|
66
98
|
{bedLayouts.length == 0 && (
|
|
67
99
|
<InlineNotification
|
|
68
100
|
kind="warning"
|
|
@@ -72,7 +104,7 @@ const WardViewByLocation = ({ location }: { location: Location }) => {
|
|
|
72
104
|
)}
|
|
73
105
|
</>
|
|
74
106
|
);
|
|
75
|
-
} else if (
|
|
107
|
+
} else if (isLoadingLocation || isLoadingPatients) {
|
|
76
108
|
return (
|
|
77
109
|
<>
|
|
78
110
|
{Array(20)
|
|
@@ -82,18 +114,27 @@ const WardViewByLocation = ({ location }: { location: Location }) => {
|
|
|
82
114
|
))}
|
|
83
115
|
</>
|
|
84
116
|
);
|
|
85
|
-
} else {
|
|
117
|
+
} else if (errorLoadingLocation) {
|
|
86
118
|
return (
|
|
87
119
|
<InlineNotification
|
|
88
120
|
kind="error"
|
|
89
121
|
lowContrast={true}
|
|
90
122
|
title={t('errorLoadingWardLocation', 'Error loading ward location')}
|
|
91
123
|
subtitle={
|
|
92
|
-
|
|
124
|
+
errorLoadingLocation?.message ??
|
|
93
125
|
t('invalidWardLocation', 'Invalid ward location: {{location}}', { location: location.display })
|
|
94
126
|
}
|
|
95
127
|
/>
|
|
96
128
|
);
|
|
129
|
+
} else {
|
|
130
|
+
return (
|
|
131
|
+
<InlineNotification
|
|
132
|
+
kind="error"
|
|
133
|
+
lowContrast={true}
|
|
134
|
+
title={t('errorLoadingPatients', 'Error loading admitted patients')}
|
|
135
|
+
subtitle={errorLoadingPatients?.message}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
97
138
|
}
|
|
98
139
|
};
|
|
99
140
|
|
|
@@ -12,21 +12,6 @@
|
|
|
12
12
|
padding: 0 spacing.$spacing-05;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
.wardViewHeader {
|
|
16
|
-
display: flex;
|
|
17
|
-
margin: spacing.$spacing-05 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.wardViewHeaderLocationDisplay {
|
|
21
|
-
flex: 1;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.wardViewHeaderAdmissionRequestMenuBar {
|
|
25
|
-
background-color: black;
|
|
26
|
-
color: white;
|
|
27
|
-
padding: 4px 0 4px 12px;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
15
|
.wardViewMain {
|
|
31
16
|
background-color: #e4e4e4;
|
|
32
17
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
1
3
|
import {
|
|
2
4
|
type Person,
|
|
3
5
|
type ConfigSchema,
|
|
4
6
|
getDefaultsFromConfigSchema,
|
|
5
7
|
useConfig,
|
|
6
|
-
useSession,
|
|
7
8
|
useFeatureFlag,
|
|
8
9
|
} from '@openmrs/esm-framework';
|
|
9
|
-
import { screen } from '@testing-library/react';
|
|
10
|
-
import React from 'react';
|
|
11
10
|
import { useParams } from 'react-router-dom';
|
|
12
|
-
import { mockLocations } from '../../../../__mocks__/locations.mock';
|
|
13
11
|
import { mockAdmissionLocation } from '../../../../__mocks__/wards.mock';
|
|
14
12
|
import { renderWithSwr } from '../../../../tools/test-utils';
|
|
15
13
|
import { configSchema } from '../config-schema';
|
|
16
14
|
import { useAdmissionLocation } from '../hooks/useAdmissionLocation';
|
|
17
|
-
import WardView from './ward-view.component';
|
|
18
15
|
import { mockPatientAlice } from '../../../../__mocks__/patient.mock';
|
|
16
|
+
import { useAdmittedPatients } from '../hooks/useAdmittedPatients';
|
|
17
|
+
import useWardLocation from '../hooks/useWardLocation';
|
|
18
|
+
import WardView from './ward-view.component';
|
|
19
19
|
|
|
20
20
|
jest.replaceProperty(mockPatientAlice.person as Person, 'preferredName', {
|
|
21
21
|
uuid: '',
|
|
@@ -27,21 +27,18 @@ jest.mocked(useConfig).mockReturnValue({
|
|
|
27
27
|
...getDefaultsFromConfigSchema<ConfigSchema>(configSchema),
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
const mockedSessionLocation = { uuid: 'abcd', display: 'mock location', links: [] };
|
|
31
|
-
jest.mocked(useSession).mockReturnValue({
|
|
32
|
-
sessionLocation: mockedSessionLocation,
|
|
33
|
-
authenticated: true,
|
|
34
|
-
sessionId: 'sessionId',
|
|
35
|
-
});
|
|
36
|
-
|
|
37
30
|
const mockedUseFeatureFlag = useFeatureFlag as jest.Mock;
|
|
38
31
|
|
|
39
|
-
jest.mock('
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
jest.mock('../hooks/useWardLocation', () =>
|
|
33
|
+
jest.fn().mockReturnValue({
|
|
34
|
+
location: { uuid: 'abcd', display: 'mock location' },
|
|
35
|
+
isLoadingLocation: false,
|
|
36
|
+
errorFetchingLocation: null,
|
|
37
|
+
invalidLocation: false,
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const mockedUseWardLocation = useWardLocation as jest.Mock;
|
|
45
42
|
|
|
46
43
|
jest.mock('react-router-dom', () => ({
|
|
47
44
|
...jest.requireActual('react-router-dom'),
|
|
@@ -52,6 +49,10 @@ const mockedUseParams = useParams as jest.Mock;
|
|
|
52
49
|
jest.mock('../hooks/useAdmissionLocation', () => ({
|
|
53
50
|
useAdmissionLocation: jest.fn(),
|
|
54
51
|
}));
|
|
52
|
+
jest.mock('../hooks/useAdmittedPatients', () => ({
|
|
53
|
+
useAdmittedPatients: jest.fn(),
|
|
54
|
+
}));
|
|
55
|
+
|
|
55
56
|
jest.mocked(useAdmissionLocation).mockReturnValue({
|
|
56
57
|
error: undefined,
|
|
57
58
|
mutate: jest.fn(),
|
|
@@ -59,19 +60,25 @@ jest.mocked(useAdmissionLocation).mockReturnValue({
|
|
|
59
60
|
isLoading: false,
|
|
60
61
|
admissionLocation: mockAdmissionLocation,
|
|
61
62
|
});
|
|
63
|
+
jest.mocked(useAdmittedPatients).mockReturnValue({
|
|
64
|
+
error: undefined,
|
|
65
|
+
mutate: jest.fn(),
|
|
66
|
+
isValidating: false,
|
|
67
|
+
isLoading: false,
|
|
68
|
+
admittedPatients: [],
|
|
69
|
+
});
|
|
62
70
|
|
|
63
71
|
describe('WardView:', () => {
|
|
64
72
|
it('renders the session location when no location provided in URL', () => {
|
|
65
73
|
renderWithSwr(<WardView />);
|
|
66
|
-
const header = screen.getByRole('heading', { name:
|
|
74
|
+
const header = screen.getByRole('heading', { name: 'mock location' });
|
|
67
75
|
expect(header).toBeInTheDocument();
|
|
68
76
|
});
|
|
69
77
|
|
|
70
78
|
it('renders the location provided in URL', () => {
|
|
71
|
-
|
|
72
|
-
mockedUseParams.mockReturnValueOnce({ locationUuid: locationToUse.uuid });
|
|
79
|
+
mockedUseParams.mockReturnValueOnce({ locationUuid: 'abcd' });
|
|
73
80
|
renderWithSwr(<WardView />);
|
|
74
|
-
const header = screen.getByRole('heading', { name:
|
|
81
|
+
const header = screen.getByRole('heading', { name: 'mock location' });
|
|
75
82
|
expect(header).toBeInTheDocument();
|
|
76
83
|
});
|
|
77
84
|
|
|
@@ -82,17 +89,23 @@ describe('WardView:', () => {
|
|
|
82
89
|
});
|
|
83
90
|
|
|
84
91
|
it('renders notification for invalid location uuid', () => {
|
|
85
|
-
|
|
92
|
+
mockedUseWardLocation.mockReturnValueOnce({
|
|
93
|
+
location: null,
|
|
94
|
+
isLoadingLocation: false,
|
|
95
|
+
errorFetchingLocation: null,
|
|
96
|
+
invalidLocation: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
86
99
|
renderWithSwr(<WardView />);
|
|
87
100
|
const notification = screen.getByRole('status');
|
|
88
101
|
expect(notification).toBeInTheDocument();
|
|
89
|
-
const invalidText = screen.getByText('
|
|
102
|
+
const invalidText = screen.getByText('Invalid location specified');
|
|
90
103
|
expect(invalidText).toBeInTheDocument();
|
|
91
104
|
});
|
|
92
105
|
|
|
93
106
|
it('screen should be empty if backend module is not installed', () => {
|
|
94
107
|
mockedUseFeatureFlag.mockReturnValueOnce(false);
|
|
95
108
|
const { container } = renderWithSwr(<WardView />);
|
|
96
|
-
expect(container
|
|
109
|
+
expect(container).toBeEmptyDOMElement();
|
|
97
110
|
});
|
|
98
111
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Movement } from '@carbon/react/icons';
|
|
3
|
+
import { Button, InlineNotification } from '@carbon/react';
|
|
4
|
+
import { ArrowRightIcon, isDesktop, launchWorkspace, useLayoutType } from '@openmrs/esm-framework';
|
|
5
|
+
import { useInpatientRequest } from '../hooks/useInpatientRequest';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import useWardLocation from '../hooks/useWardLocation';
|
|
8
|
+
import styles from './admission-requests.scss';
|
|
9
|
+
|
|
10
|
+
const AdmissionRequestsBar = () => {
|
|
11
|
+
const { location } = useWardLocation();
|
|
12
|
+
const { inpatientRequests, isLoading, error } = useInpatientRequest(location?.uuid);
|
|
13
|
+
const admissionRequests = inpatientRequests?.filter((request) => request.type == 'ADMISSION');
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const layout = useLayoutType();
|
|
16
|
+
|
|
17
|
+
if (isLoading || !admissionRequests?.length) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (error) {
|
|
22
|
+
console.error(error);
|
|
23
|
+
return (
|
|
24
|
+
<InlineNotification
|
|
25
|
+
kind="error"
|
|
26
|
+
title={t('errorLoadingPatientAdmissionRequests', 'Error Loading patient admission requests')}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className={styles.admissionRequestsContainer}>
|
|
33
|
+
<Movement className={styles.movementIcon} size="24" />
|
|
34
|
+
<span className={styles.content}>
|
|
35
|
+
{t('admissionRequestsCount', '{{count}} admission requests', {
|
|
36
|
+
count: admissionRequests.length,
|
|
37
|
+
})}
|
|
38
|
+
</span>
|
|
39
|
+
<Button
|
|
40
|
+
onClick={() => launchWorkspace('admission-requests-workspace', { admissionRequests })}
|
|
41
|
+
renderIcon={ArrowRightIcon}
|
|
42
|
+
kind="ghost"
|
|
43
|
+
size={isDesktop(layout) ? 'sm' : 'lg'}>
|
|
44
|
+
{t('manage', 'Manage')}
|
|
45
|
+
</Button>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default AdmissionRequestsBar;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { renderWithSwr } from '../../../../tools/test-utils';
|
|
4
|
+
import { screen } from '@testing-library/react';
|
|
5
|
+
import { launchWorkspace } from '@openmrs/esm-framework';
|
|
6
|
+
import AdmissionRequestsBar from './admission-requests-bar.component';
|
|
7
|
+
import { mockInpatientRequest } from '../../../../__mocks__/ward-patient';
|
|
8
|
+
import { useInpatientRequest } from '../hooks/useInpatientRequest';
|
|
9
|
+
|
|
10
|
+
jest.mock('@openmrs/esm-framework', () => {
|
|
11
|
+
return {
|
|
12
|
+
...jest.requireActual('@openmrs/esm-framework'),
|
|
13
|
+
launchWorkspace: jest.fn(),
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
jest.mock('../hooks/useInpatientRequest', () => ({
|
|
18
|
+
useInpatientRequest: jest.fn(),
|
|
19
|
+
}));
|
|
20
|
+
const mockInpatientRequestResponse = {
|
|
21
|
+
error: undefined,
|
|
22
|
+
mutate: jest.fn(),
|
|
23
|
+
isValidating: false,
|
|
24
|
+
isLoading: false,
|
|
25
|
+
inpatientRequests: [mockInpatientRequest],
|
|
26
|
+
};
|
|
27
|
+
jest.mocked(useInpatientRequest).mockReturnValue(mockInpatientRequestResponse);
|
|
28
|
+
|
|
29
|
+
describe('Admission Requests Button', () => {
|
|
30
|
+
it('call launch workspace when clicked on manage button', async () => {
|
|
31
|
+
const user = userEvent.setup();
|
|
32
|
+
renderWithSwr(<AdmissionRequestsBar />);
|
|
33
|
+
|
|
34
|
+
await user.click(screen.getByRole('button', { name: /manage/i }));
|
|
35
|
+
expect(launchWorkspace).toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('there should be one admission request', () => {
|
|
39
|
+
renderWithSwr(<AdmissionRequestsBar />);
|
|
40
|
+
|
|
41
|
+
expect(screen.getByText('1 admission requests')).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/type';
|
|
2
|
+
@use '@carbon/styles/scss/spacing';
|
|
3
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
|
4
|
+
|
|
5
|
+
.admissionRequestsContainer {
|
|
6
|
+
background-color: $color-gray-70;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
padding: spacing.$spacing-02 0 spacing.$spacing-02 spacing.$spacing-04;
|
|
10
|
+
background-color: #393939;
|
|
11
|
+
|
|
12
|
+
& > button {
|
|
13
|
+
color: #78a9ff;
|
|
14
|
+
|
|
15
|
+
svg {
|
|
16
|
+
fill: #78a9ff !important;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.movementIcon {
|
|
22
|
+
padding: spacing.$spacing-02;
|
|
23
|
+
border-radius: 50%;
|
|
24
|
+
fill: $ui-03;
|
|
25
|
+
background-color: $color-blue-60-2;
|
|
26
|
+
margin-right: spacing.$spacing-03;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.content {
|
|
30
|
+
@include type.type-style('heading-compact-01');
|
|
31
|
+
color: $ui-02;
|
|
32
|
+
margin-right: spacing.$spacing-03;
|
|
33
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './ward-view-header.scss';
|
|
3
|
+
import AdmissionRequestsBar from './admission-requests-bar.component';
|
|
4
|
+
import useWardLocation from '../hooks/useWardLocation';
|
|
5
|
+
|
|
6
|
+
interface WardViewHeaderProps {}
|
|
7
|
+
|
|
8
|
+
const WardViewHeader: React.FC<WardViewHeaderProps> = () => {
|
|
9
|
+
const { location } = useWardLocation();
|
|
10
|
+
return (
|
|
11
|
+
<div className={styles.wardViewHeader}>
|
|
12
|
+
<h4>{location?.display}</h4>
|
|
13
|
+
<AdmissionRequestsBar />
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default WardViewHeader;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './admission-request-card.scss';
|
|
3
|
+
import { useParams } from 'react-router-dom';
|
|
4
|
+
import { type Patient } from '@openmrs/esm-framework';
|
|
5
|
+
import { usePatientCardRows } from '../ward-patient-card/ward-patient-card-row.resources';
|
|
6
|
+
|
|
7
|
+
interface AdmissionRequestCardProps {
|
|
8
|
+
patient: Patient;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const AdmissionRequestCard: React.FC<AdmissionRequestCardProps> = ({ patient }) => {
|
|
12
|
+
const { locationUuid } = useParams();
|
|
13
|
+
const admissionPatientCardSlots = usePatientCardRows(locationUuid);
|
|
14
|
+
return (
|
|
15
|
+
<div className={styles.admissionRequestCardHeader}>
|
|
16
|
+
{admissionPatientCardSlots.map((AdmissionPatientCard, i) => (
|
|
17
|
+
<AdmissionPatientCard key={i} patient={patient} bed={null} visit={null} />
|
|
18
|
+
))}
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default AdmissionRequestCard;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/type';
|
|
2
|
+
@use '@carbon/styles/scss/spacing';
|
|
3
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
|
4
|
+
|
|
5
|
+
.admissionRequestCardHeader {
|
|
6
|
+
background-color: $ui-03;
|
|
7
|
+
padding: spacing.$spacing-03;
|
|
8
|
+
border: 1px solid $color-gray-30;
|
|
9
|
+
height: fit-content;
|
|
10
|
+
color: $color-gray-70;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
flex-wrap: wrap;
|
|
14
|
+
> div:not(:first-of-type)::before {
|
|
15
|
+
content: '·';
|
|
16
|
+
padding: spacing.$spacing-02;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.patientName,
|
|
21
|
+
.patientGender,
|
|
22
|
+
.patientAge {
|
|
23
|
+
@include type.type-style('heading-01');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.patientName {
|
|
27
|
+
color: $color-gray-100;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.time,
|
|
31
|
+
.city,
|
|
32
|
+
.ward {
|
|
33
|
+
@include type.type-style('body-compact-01');
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
3
|
+
import AdmissionRequestsWorkspace from './admission-requests-workspace.component';
|
|
4
|
+
import { type ConfigSchema, type Person, getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
|
|
5
|
+
import { renderWithSwr } from 'tools';
|
|
6
|
+
import { mockInpatientRequest } from '__mocks__';
|
|
7
|
+
import { configSchema } from '../config-schema';
|
|
8
|
+
|
|
9
|
+
jest.replaceProperty(mockInpatientRequest.patient.person as Person, 'preferredName', {
|
|
10
|
+
uuid: '',
|
|
11
|
+
givenName: 'Alice',
|
|
12
|
+
familyName: 'Johnson',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
jest.mocked(useConfig).mockReturnValue({
|
|
16
|
+
...getDefaultsFromConfigSchema<ConfigSchema>(configSchema),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
jest.mock('@openmrs/esm-framework', () => {
|
|
20
|
+
return {
|
|
21
|
+
...jest.requireActual('@openmrs/esm-framework'),
|
|
22
|
+
closeWorkspace: jest.fn(),
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('Admission Requests Workspace', () => {
|
|
27
|
+
it('should render a admission request card', () => {
|
|
28
|
+
renderWithSwr(<AdmissionRequestsWorkspace admissionRequests={[mockInpatientRequest]} />);
|
|
29
|
+
const { givenName, familyName } = mockInpatientRequest.patient.person!.preferredName!;
|
|
30
|
+
expect(screen.getByText(givenName + ' ' + familyName)).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styles from './admission-requests-workspace.scss';
|
|
3
|
+
import AdmissionRequestCard from './admission-request-card.component';
|
|
4
|
+
import { type InpatientRequest } from '../types';
|
|
5
|
+
|
|
6
|
+
interface AdmissionRequestsWorkspaceProps {
|
|
7
|
+
admissionRequests: InpatientRequest[];
|
|
8
|
+
}
|
|
9
|
+
const AdmissionRequestsWorkspace: React.FC<AdmissionRequestsWorkspaceProps> = ({ admissionRequests }) => {
|
|
10
|
+
return (
|
|
11
|
+
<div className={styles.admissionRequestsWorkspaceContainer}>
|
|
12
|
+
<div className={styles.admissionRequestsWorkspace}>
|
|
13
|
+
{admissionRequests.map((admissionRequest) => (
|
|
14
|
+
<AdmissionRequestCard patient={admissionRequest.patient} />
|
|
15
|
+
))}
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default AdmissionRequestsWorkspace;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/type';
|
|
2
|
+
@use '@carbon/styles/scss/spacing';
|
|
3
|
+
@import '~@openmrs/esm-styleguide/src/vars';
|
|
4
|
+
|
|
5
|
+
.admissionRequestsWorkspaceContainer {
|
|
6
|
+
min-height: var(--desktop-workspace-window-height);
|
|
7
|
+
}
|
|
8
|
+
.admissionRequestsWorkspace {
|
|
9
|
+
padding: spacing.$spacing-04;
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
12
|
+
gap: 1rem;
|
|
13
|
+
}
|
package/translations/en.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
+
"admissionRequestsCount_one": "{{count}} admission request",
|
|
3
|
+
"admissionRequestsCount_other": "{{count}} admission requests",
|
|
2
4
|
"bedShare": "Bed share",
|
|
3
5
|
"emptyBed": "Empty bed",
|
|
6
|
+
"errorLoadingPatientAdmissionRequests": "Error Loading patient admission requests",
|
|
7
|
+
"errorLoadingPatients": "Error loading admitted patients",
|
|
4
8
|
"errorLoadingWardLocation": "Error loading ward location",
|
|
5
9
|
"invalidLocationSpecified": "Invalid location specified",
|
|
6
10
|
"invalidWardLocation": "Invalid ward location: {{location}}",
|
|
11
|
+
"manage": "Manage",
|
|
7
12
|
"noBedsConfigured": "No beds configured for this location",
|
|
8
|
-
"
|
|
13
|
+
"wards": "Wards"
|
|
9
14
|
}
|