@kenyaemr/esm-ward-app 8.1.1-pre.129 → 8.1.2-pre.154
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 +25 -22
- package/dist/109.js +1 -1
- package/dist/109.js.map +1 -1
- 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/126.js +1 -0
- package/dist/126.js.map +1 -0
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/146.js +1 -1
- package/dist/146.js.map +1 -1
- package/dist/15.js +1 -1
- package/dist/15.js.map +1 -1
- package/dist/348.js +1 -1
- package/dist/362.js +1 -0
- package/dist/362.js.map +1 -0
- package/dist/443.js +1 -0
- package/dist/443.js.map +1 -0
- package/dist/471.js +1 -1
- package/dist/471.js.map +1 -1
- package/dist/481.js +1 -1
- package/dist/481.js.map +1 -1
- package/dist/53.js +1 -1
- package/dist/53.js.map +1 -1
- package/dist/534.js +1 -0
- package/dist/534.js.map +1 -0
- package/dist/559.js +1 -1
- package/dist/559.js.map +1 -1
- package/dist/574.js +1 -1
- package/dist/576.js +1 -1
- package/dist/576.js.map +1 -1
- package/dist/577.js +1 -1
- package/dist/577.js.map +1 -1
- package/dist/598.js +1 -0
- package/dist/598.js.map +1 -0
- package/dist/662.js +1 -1
- package/dist/662.js.map +1 -1
- package/dist/767.js +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/925.js +2 -0
- package/dist/925.js.LICENSE.txt +40 -0
- package/dist/925.js.map +1 -0
- package/dist/kenyaemr-esm-ward-app.js +1 -1
- package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +169 -139
- package/dist/kenyaemr-esm-ward-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +35 -0
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/mock.tsx +5 -4
- package/package-lock.json +2 -2
- package/package.json +4 -4
- package/src/action-menu-buttons/clinical-forms-workspace-siderail.component.tsx +1 -1
- package/src/action-menu-buttons/discharge-workspace-siderail.component.tsx +1 -1
- package/src/beds/bed-share-divider.component.tsx +21 -0
- package/src/beds/bed-share-divider.scss +18 -0
- package/src/beds/ward-bed.component.tsx +7 -17
- package/src/beds/ward-bed.scss +1 -15
- package/src/beds/ward-bed.test.tsx +3 -3
- package/src/config-schema.ts +6 -0
- package/src/hooks/useEmrConfiguration.ts +8 -0
- package/src/index.ts +6 -6
- package/src/location-selector/location-selector.component.tsx +8 -7
- package/src/root.component.tsx +0 -2
- package/src/routes.json +20 -6
- package/src/types/index.ts +9 -4
- package/src/ward-patient-card/row-elements/ward-patient-identifier.tsx +2 -2
- package/src/ward-patient-card/row-elements/ward-patient-pending-transfer.tsx +22 -4
- package/src/ward-patient-card/ward-patient-card.component.tsx +24 -6
- package/src/ward-patient-card/ward-patient-card.scss +6 -0
- package/src/ward-view/default-ward/default-ward-beds.component.tsx +3 -5
- package/src/ward-view/default-ward/default-ward-patient-card-header.component.tsx +1 -1
- package/src/ward-view/default-ward/default-ward-patient-card.component.tsx +2 -2
- package/src/ward-view/default-ward/default-ward-unassigned-patients.component.tsx +2 -2
- package/src/ward-view/materal-ward/maternal-ward-beds.component.tsx +9 -2
- package/src/ward-view/materal-ward/maternal-ward-patient-card-header.component.tsx +1 -1
- package/src/ward-view/materal-ward/maternal-ward-patient-card.component.tsx +2 -2
- package/src/ward-view/materal-ward/maternal-ward-view.resource.ts +7 -7
- package/src/ward-view/ward-view.component.tsx +1 -2
- package/src/ward-view/ward.component.tsx +19 -11
- package/src/ward-view-header/admission-requests-bar.component.tsx +4 -2
- package/src/ward-view-header/admission-requests.scss +11 -1
- package/src/ward-view-header/ward-metrics.test.tsx +5 -13
- package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +17 -62
- package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +1 -1
- package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +2 -2
- package/src/ward-workspace/admit-patient-button.component.tsx +82 -0
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss +7 -0
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +28 -13
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +72 -66
- package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.scss +55 -0
- package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.test.tsx +99 -0
- package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.workspace.tsx +174 -0
- package/src/ward-workspace/patient-banner/patient-banner.component.tsx +9 -7
- package/src/ward-workspace/patient-banner/style.scss +3 -19
- package/src/ward-workspace/patient-clinical-forms-workspace/patient-clinical-forms.workspace.tsx +8 -2
- package/src/ward-workspace/patient-details/ward-patient-action-button.extension.tsx +2 -3
- package/src/ward-workspace/patient-details/ward-patient.style.scss +12 -0
- package/src/ward-workspace/patient-details/ward-patient.workspace.tsx +18 -47
- package/src/ward-workspace/patient-discharge/patient-discharge.workspace.tsx +14 -24
- package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +12 -34
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +48 -35
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.scss +4 -0
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +1 -1
- package/src/ward-workspace/ward-patient-notes/form/notes-form.component.tsx +30 -25
- package/src/ward-workspace/ward-patient-notes/notes.workspace.tsx +1 -1
- package/src/ward.resource.ts +32 -22
- package/translations/en.json +7 -1
- package/dist/153.js +0 -1
- package/dist/153.js.map +0 -1
- package/dist/169.js +0 -1
- package/dist/169.js.map +0 -1
- package/dist/303.js +0 -2
- package/dist/303.js.LICENSE.txt +0 -5
- package/dist/303.js.map +0 -1
- package/dist/501.js +0 -1
- package/dist/501.js.map +0 -1
- package/dist/920.js +0 -1
- package/dist/920.js.map +0 -1
- package/src/ward-workspace/admit-patient-form-workspace/types.ts +0 -7
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
1
2
|
import { Button, ButtonSet, Column, Form, InlineNotification, Row } from '@carbon/react';
|
|
2
3
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
3
|
-
import { showSnackbar, useAppContext } from '@openmrs/esm-framework';
|
|
4
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
4
|
import { Controller, useForm } from 'react-hook-form';
|
|
6
5
|
import { useTranslation } from 'react-i18next';
|
|
7
6
|
import { z } from 'zod';
|
|
7
|
+
import { showSnackbar, useAppContext } from '@openmrs/esm-framework';
|
|
8
|
+
import type { WardPatientWorkspaceProps, WardViewContext } from '../../types';
|
|
8
9
|
import { useAssignedBedByPatient } from '../../hooks/useAssignedBedByPatient';
|
|
9
|
-
import useWardLocation from '../../hooks/useWardLocation';
|
|
10
|
-
import type { WardViewContext } from '../../types';
|
|
11
10
|
import { assignPatientToBed, removePatientFromBed, useAdmitPatient } from '../../ward.resource';
|
|
11
|
+
import useWardLocation from '../../hooks/useWardLocation';
|
|
12
12
|
import BedSelector from '../bed-selector.component';
|
|
13
|
+
import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
|
|
13
14
|
import styles from './admit-patient-form.scss';
|
|
14
|
-
import type { AdmitPatientFormWorkspaceProps } from './types';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* This form gets rendered when the user clicks "admit patient" in
|
|
@@ -19,13 +19,15 @@ import type { AdmitPatientFormWorkspaceProps } from './types';
|
|
|
19
19
|
* the bed management module is installed. It asks to (optionally) select
|
|
20
20
|
* a bed to assign to patient
|
|
21
21
|
*/
|
|
22
|
-
const AdmitPatientFormWorkspace: React.FC<
|
|
23
|
-
|
|
24
|
-
dispositionType,
|
|
22
|
+
const AdmitPatientFormWorkspace: React.FC<WardPatientWorkspaceProps> = ({
|
|
23
|
+
wardPatient,
|
|
25
24
|
closeWorkspace,
|
|
26
25
|
closeWorkspaceWithSavedChanges,
|
|
27
26
|
promptBeforeClosing,
|
|
28
27
|
}) => {
|
|
28
|
+
const { patient, inpatientRequest } = wardPatient ?? {};
|
|
29
|
+
const dispositionType = inpatientRequest?.dispositionType ?? 'ADMIT';
|
|
30
|
+
|
|
29
31
|
const { t } = useTranslation();
|
|
30
32
|
const { location } = useWardLocation();
|
|
31
33
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -44,7 +46,7 @@ const AdmitPatientFormWorkspace: React.FC<AdmitPatientFormWorkspaceProps> = ({
|
|
|
44
46
|
z.object({
|
|
45
47
|
bedId: z.number().optional(),
|
|
46
48
|
}),
|
|
47
|
-
[
|
|
49
|
+
[],
|
|
48
50
|
);
|
|
49
51
|
|
|
50
52
|
type FormValues = z.infer<typeof zodSchema>;
|
|
@@ -59,7 +61,7 @@ const AdmitPatientFormWorkspace: React.FC<AdmitPatientFormWorkspaceProps> = ({
|
|
|
59
61
|
|
|
60
62
|
useEffect(() => {
|
|
61
63
|
promptBeforeClosing(() => isDirty);
|
|
62
|
-
}, [isDirty]);
|
|
64
|
+
}, [isDirty, promptBeforeClosing]);
|
|
63
65
|
|
|
64
66
|
const onSubmit = (values: FormValues) => {
|
|
65
67
|
setShowErrorNotifications(false);
|
|
@@ -139,64 +141,68 @@ const AdmitPatientFormWorkspace: React.FC<AdmitPatientFormWorkspaceProps> = ({
|
|
|
139
141
|
}, []);
|
|
140
142
|
|
|
141
143
|
if (!wardPatientGroupDetails) return <></>;
|
|
144
|
+
|
|
142
145
|
return (
|
|
143
|
-
<
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
146
|
+
<div className={styles.flexWrapper}>
|
|
147
|
+
<WardPatientWorkspaceBanner {...{ wardPatient }} />
|
|
148
|
+
<Form control={control} className={styles.form} onSubmit={handleSubmit(onSubmit, onError)}>
|
|
149
|
+
<div className={styles.formContent}>
|
|
150
|
+
<Row>
|
|
151
|
+
<Column>
|
|
152
|
+
<h2 className={styles.productiveHeading02}>{t('selectABed', 'Select a bed')}</h2>
|
|
153
|
+
<div className={styles.bedSelectionDropdown}>
|
|
154
|
+
<Controller
|
|
155
|
+
control={control}
|
|
156
|
+
name="bedId"
|
|
157
|
+
render={({ field: { onChange, value }, fieldState: { error } }) => {
|
|
158
|
+
return (
|
|
159
|
+
<BedSelector
|
|
160
|
+
beds={beds}
|
|
161
|
+
isLoadingBeds={isLoading}
|
|
162
|
+
currentPatient={patient}
|
|
163
|
+
selectedBedId={value}
|
|
164
|
+
error={error}
|
|
165
|
+
control={control}
|
|
166
|
+
onChange={onChange}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
</Column>
|
|
173
|
+
</Row>
|
|
174
|
+
<div className={styles.errorNotifications}>
|
|
175
|
+
{showErrorNotifications &&
|
|
176
|
+
Object.entries(errors).map(([key, value]) => {
|
|
177
|
+
return (
|
|
178
|
+
<Row key={key}>
|
|
179
|
+
<Column>
|
|
180
|
+
<InlineNotification kind="error" subtitle={value.message} lowContrast />
|
|
181
|
+
</Column>
|
|
182
|
+
</Row>
|
|
183
|
+
);
|
|
184
|
+
})}
|
|
185
|
+
</div>
|
|
180
186
|
</div>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
</
|
|
198
|
-
</
|
|
199
|
-
</
|
|
187
|
+
<ButtonSet className={styles.buttonSet}>
|
|
188
|
+
<Button size="xl" kind="secondary" onClick={() => closeWorkspace({ ignoreChanges: true })}>
|
|
189
|
+
{t('cancel', 'Cancel')}
|
|
190
|
+
</Button>
|
|
191
|
+
<Button
|
|
192
|
+
type="submit"
|
|
193
|
+
size="xl"
|
|
194
|
+
disabled={
|
|
195
|
+
isSubmitting ||
|
|
196
|
+
isLoadingEmrConfiguration ||
|
|
197
|
+
errorFetchingEmrConfiguration ||
|
|
198
|
+
isLoading ||
|
|
199
|
+
isLoadingBedsAssignedToPatient
|
|
200
|
+
}>
|
|
201
|
+
{!isSubmitting ? t('admit', 'Admit') : t('admitting', 'Admitting...')}
|
|
202
|
+
</Button>
|
|
203
|
+
</ButtonSet>
|
|
204
|
+
</Form>
|
|
205
|
+
</div>
|
|
200
206
|
);
|
|
201
207
|
};
|
|
202
208
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
@use '@carbon/type';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
|
+
|
|
5
|
+
.flexWrapper {
|
|
6
|
+
height: 100%;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
color: #393939;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.workspaceContent {
|
|
13
|
+
padding: layout.$spacing-05;
|
|
14
|
+
height: 100%;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
color: #393939;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.field {
|
|
21
|
+
margin-bottom: layout.$spacing-05;
|
|
22
|
+
& > h2 {
|
|
23
|
+
margin-bottom: layout.$spacing-03;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.productiveHeading02 {
|
|
28
|
+
@include type.type-style('heading-compact-02');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.formContainer {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
justify-content: space-between;
|
|
35
|
+
height: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.buttonSet {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
margin: 0 (-(layout.$spacing-05)) (-(layout.$spacing-05)) (-(layout.$spacing-05));
|
|
42
|
+
|
|
43
|
+
button {
|
|
44
|
+
max-width: unset !important;
|
|
45
|
+
width: 50% !important;
|
|
46
|
+
|
|
47
|
+
> svg {
|
|
48
|
+
fill: currentColor !important;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.notifications {
|
|
54
|
+
margin-top: layout.$spacing-05;
|
|
55
|
+
}
|
package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.test.tsx
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { useAppContext, type DefaultWorkspaceProps } from '@openmrs/esm-framework';
|
|
5
|
+
import { mockInpatientRequestAlice, mockLocationInpatientWard, mockPatientAlice } from '__mocks__';
|
|
6
|
+
import { renderWithSwr } from 'tools';
|
|
7
|
+
import useWardLocation from '../../hooks/useWardLocation';
|
|
8
|
+
import type { WardPatient, WardViewContext } from '../../types';
|
|
9
|
+
import { useCreateEncounter } from '../../ward.resource';
|
|
10
|
+
import CancelAdmissionRequestWorkspace from './cancel-admission-request.workspace';
|
|
11
|
+
import { mockWardViewContext } from '../../../mock';
|
|
12
|
+
|
|
13
|
+
jest.mock('../../hooks/useWardLocation', () => jest.fn());
|
|
14
|
+
|
|
15
|
+
jest.mock('../../hooks/useInpatientRequest', () => ({
|
|
16
|
+
useInpatientRequest: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
jest.mock('../../hooks/useWardPatientGrouping', () => ({
|
|
20
|
+
useWardPatientGrouping: jest.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock('../../hooks/useInpatientAdmission', () => ({
|
|
24
|
+
useInpatientAdmission: jest.fn(),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock('../../ward.resource', () => ({
|
|
28
|
+
useCreateEncounter: jest.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const mockedUseWardLocation = jest.mocked(useWardLocation);
|
|
32
|
+
const mockedCreateEncounter = jest.fn().mockResolvedValue({
|
|
33
|
+
ok: true,
|
|
34
|
+
data: {
|
|
35
|
+
uuid: 'encounter-uuid',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const mockedUseCreateEncounter = jest.mocked(useCreateEncounter);
|
|
39
|
+
mockedUseCreateEncounter.mockReturnValue({
|
|
40
|
+
createEncounter: mockedCreateEncounter,
|
|
41
|
+
isLoadingEmrConfiguration: false,
|
|
42
|
+
errorFetchingEmrConfiguration: false,
|
|
43
|
+
emrConfiguration: null,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
mockedUseWardLocation.mockReturnValue({
|
|
47
|
+
location: mockLocationInpatientWard,
|
|
48
|
+
invalidLocation: false,
|
|
49
|
+
isLoadingLocation: false,
|
|
50
|
+
errorFetchingLocation: null,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const mockWorkspaceProps: DefaultWorkspaceProps = {
|
|
54
|
+
closeWorkspaceWithSavedChanges: jest.fn(),
|
|
55
|
+
promptBeforeClosing: jest.fn(),
|
|
56
|
+
setTitle: jest.fn(),
|
|
57
|
+
closeWorkspace: jest.fn(),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const mockWardPatientAliceProps: WardPatient = {
|
|
61
|
+
visit: mockInpatientRequestAlice.visit,
|
|
62
|
+
patient: mockPatientAlice,
|
|
63
|
+
bed: null,
|
|
64
|
+
inpatientAdmission: null,
|
|
65
|
+
inpatientRequest: mockInpatientRequestAlice,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
|
|
69
|
+
|
|
70
|
+
function renderCancelAdmissionRequestWorkspace() {
|
|
71
|
+
renderWithSwr(
|
|
72
|
+
<CancelAdmissionRequestWorkspace
|
|
73
|
+
{...{ ...mockWorkspaceProps, wardPatient: mockWardPatientAliceProps, WardPatientHeader: jest.fn() }}
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe('CancelAdmissionRequestWorkspace', () => {
|
|
79
|
+
it('should cancel admission request form creates encounter when form is filled out and submitted ', async () => {
|
|
80
|
+
const user = userEvent.setup();
|
|
81
|
+
renderCancelAdmissionRequestWorkspace();
|
|
82
|
+
|
|
83
|
+
const textbox = screen.getByRole('textbox');
|
|
84
|
+
expect(textbox).toBeInTheDocument();
|
|
85
|
+
const submit = screen.getByRole('button', { name: /save/i });
|
|
86
|
+
await user.click(submit);
|
|
87
|
+
|
|
88
|
+
const warningText = /notes required for cancelling admission or transfer request/i;
|
|
89
|
+
const warning = screen.getByText(warningText);
|
|
90
|
+
expect(warning).toBeInTheDocument();
|
|
91
|
+
|
|
92
|
+
await user.type(textbox, 'Test note');
|
|
93
|
+
expect(screen.queryByText(warningText)).not.toBeInTheDocument();
|
|
94
|
+
|
|
95
|
+
await user.click(submit);
|
|
96
|
+
|
|
97
|
+
expect(mockedCreateEncounter).toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
});
|
package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.workspace.tsx
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Button, ButtonSet, Form, InlineNotification, TextArea } from '@carbon/react';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
5
|
+
import { Controller, useForm } from 'react-hook-form';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { ResponsiveWrapper, showSnackbar, useAppContext, useSession } from '@openmrs/esm-framework';
|
|
9
|
+
import type { ObsPayload, WardPatientWorkspaceProps, WardViewContext } from '../../types';
|
|
10
|
+
import { useCreateEncounter } from '../../ward.resource';
|
|
11
|
+
import useWardLocation from '../../hooks/useWardLocation';
|
|
12
|
+
import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
|
|
13
|
+
import styles from './cancel-admission-request.scss';
|
|
14
|
+
|
|
15
|
+
export default function CancelAdmissionRequestWorkspace({
|
|
16
|
+
closeWorkspaceWithSavedChanges,
|
|
17
|
+
wardPatient,
|
|
18
|
+
promptBeforeClosing,
|
|
19
|
+
}: WardPatientWorkspaceProps) {
|
|
20
|
+
const { patient } = wardPatient ?? {};
|
|
21
|
+
const { t } = useTranslation();
|
|
22
|
+
const [showErrorNotifications, setShowErrorNotifications] = useState(false);
|
|
23
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
24
|
+
const { createEncounter, emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } =
|
|
25
|
+
useCreateEncounter();
|
|
26
|
+
const { currentProvider } = useSession();
|
|
27
|
+
const { location } = useWardLocation();
|
|
28
|
+
const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
|
|
29
|
+
|
|
30
|
+
const zodSchema = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
z.object({
|
|
33
|
+
note: z
|
|
34
|
+
.string()
|
|
35
|
+
.trim()
|
|
36
|
+
.min(1, {
|
|
37
|
+
message: t(
|
|
38
|
+
'notesRequiredForCancellingRequest',
|
|
39
|
+
'Notes required for cancelling admission or transfer request',
|
|
40
|
+
),
|
|
41
|
+
}),
|
|
42
|
+
}),
|
|
43
|
+
[t],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
type FormValues = z.infer<typeof zodSchema>;
|
|
47
|
+
|
|
48
|
+
const formDefaultValues: Partial<FormValues> = {
|
|
49
|
+
note: '',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
formState: { errors, isDirty },
|
|
54
|
+
control,
|
|
55
|
+
handleSubmit,
|
|
56
|
+
} = useForm<FormValues>({ resolver: zodResolver(zodSchema), defaultValues: formDefaultValues });
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
promptBeforeClosing(() => isDirty);
|
|
60
|
+
return () => promptBeforeClosing(null);
|
|
61
|
+
}, [isDirty, promptBeforeClosing]);
|
|
62
|
+
|
|
63
|
+
const onSubmit = useCallback(
|
|
64
|
+
(values: FormValues) => {
|
|
65
|
+
setIsSubmitting(true);
|
|
66
|
+
setShowErrorNotifications(false);
|
|
67
|
+
const obs: Array<ObsPayload> = [
|
|
68
|
+
{
|
|
69
|
+
concept: emrConfiguration?.consultFreeTextCommentsConcept?.uuid,
|
|
70
|
+
value: values.note,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
concept: emrConfiguration?.admissionDecisionConcept?.uuid,
|
|
74
|
+
value: {
|
|
75
|
+
uuid: emrConfiguration?.denyAdmissionConcept?.uuid,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
createEncounter(patient, emrConfiguration?.cancelADTRequestEncounterType, obs)
|
|
81
|
+
.then(() => {
|
|
82
|
+
showSnackbar({
|
|
83
|
+
title: t('admissionRequestCancelled', 'Admission request cancelled.'),
|
|
84
|
+
kind: 'success',
|
|
85
|
+
});
|
|
86
|
+
})
|
|
87
|
+
.catch((err: Error) => {
|
|
88
|
+
showSnackbar({
|
|
89
|
+
title: t('Error cancelling admission request', 'Error cancelling admission request'),
|
|
90
|
+
subtitle: err.message,
|
|
91
|
+
kind: 'error',
|
|
92
|
+
});
|
|
93
|
+
})
|
|
94
|
+
.finally(() => {
|
|
95
|
+
setIsSubmitting(false);
|
|
96
|
+
closeWorkspaceWithSavedChanges();
|
|
97
|
+
wardPatientGroupDetails.mutate();
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
[
|
|
101
|
+
emrConfiguration?.consultFreeTextCommentsConcept?.uuid,
|
|
102
|
+
emrConfiguration?.admissionDecisionConcept?.uuid,
|
|
103
|
+
emrConfiguration?.denyAdmissionConcept?.uuid,
|
|
104
|
+
emrConfiguration?.cancelADTRequestEncounterType,
|
|
105
|
+
createEncounter,
|
|
106
|
+
patient,
|
|
107
|
+
t,
|
|
108
|
+
closeWorkspaceWithSavedChanges,
|
|
109
|
+
wardPatientGroupDetails,
|
|
110
|
+
],
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const onError = useCallback(() => {
|
|
114
|
+
setIsSubmitting(false);
|
|
115
|
+
setShowErrorNotifications(true);
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
if (!wardPatientGroupDetails) return <></>;
|
|
119
|
+
return (
|
|
120
|
+
<div className={styles.flexWrapper}>
|
|
121
|
+
<WardPatientWorkspaceBanner wardPatient={wardPatient} />
|
|
122
|
+
<Form
|
|
123
|
+
onSubmit={handleSubmit(onSubmit, onError)}
|
|
124
|
+
className={classNames(styles.formContainer, styles.workspaceContent)}>
|
|
125
|
+
<div>
|
|
126
|
+
{errorFetchingEmrConfiguration && (
|
|
127
|
+
<div className={styles.formError}>
|
|
128
|
+
<InlineNotification
|
|
129
|
+
kind="error"
|
|
130
|
+
title={t('somePartsOfTheFormDidntLoad', "Some parts of the form didn't load")}
|
|
131
|
+
subtitle={t(
|
|
132
|
+
'fetchingEmrConfigurationFailed',
|
|
133
|
+
'Fetching EMR configuration failed. Try refreshing the page or contact your system administrator.',
|
|
134
|
+
)}
|
|
135
|
+
lowContrast
|
|
136
|
+
hideCloseButton
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
<div className={styles.field}>
|
|
141
|
+
<h2 className={styles.productiveHeading02}>{t('clinicalNotes', 'Clinical notes')}</h2>
|
|
142
|
+
<Controller
|
|
143
|
+
name="note"
|
|
144
|
+
control={control}
|
|
145
|
+
render={({ field, fieldState: { error } }) => (
|
|
146
|
+
<ResponsiveWrapper>
|
|
147
|
+
<TextArea {...field} labelText={''} />
|
|
148
|
+
</ResponsiveWrapper>
|
|
149
|
+
)}
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
{showErrorNotifications && (
|
|
153
|
+
<div className={styles.notifications}>
|
|
154
|
+
{Object.values(errors).map((error) => (
|
|
155
|
+
<InlineNotification key={error.message} lowContrast subtitle={error?.message} hideCloseButton />
|
|
156
|
+
))}
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
<ButtonSet className={styles.buttonSet}>
|
|
161
|
+
<Button size="xl" kind="secondary" onClick={closeWorkspaceWithSavedChanges}>
|
|
162
|
+
{t('cancel', 'Cancel')}
|
|
163
|
+
</Button>
|
|
164
|
+
<Button
|
|
165
|
+
type="submit"
|
|
166
|
+
size="xl"
|
|
167
|
+
disabled={isLoadingEmrConfiguration || isSubmitting || errorFetchingEmrConfiguration || !patient}>
|
|
168
|
+
{t('save', 'Save')}
|
|
169
|
+
</Button>
|
|
170
|
+
</ButtonSet>
|
|
171
|
+
</Form>
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { useAppContext } from '@openmrs/esm-framework';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import type {
|
|
3
|
+
import type { WardPatientCardType, WardViewContext } from '../../types';
|
|
4
4
|
import styles from './style.scss';
|
|
5
5
|
|
|
6
|
-
const WardPatientWorkspaceBanner = (wardPatient
|
|
7
|
-
const { patient } = wardPatient;
|
|
8
|
-
const {WardPatientHeader} = useAppContext<WardViewContext>('ward-view-context') ?? {};
|
|
6
|
+
const WardPatientWorkspaceBanner: WardPatientCardType = ({ wardPatient }) => {
|
|
7
|
+
const { patient } = wardPatient ?? {};
|
|
8
|
+
const { WardPatientHeader } = useAppContext<WardViewContext>('ward-view-context') ?? {};
|
|
9
9
|
|
|
10
10
|
if (!patient) {
|
|
11
11
|
console.warn('Patient details were not received by the ward workspace');
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
return (
|
|
16
|
-
<div className={styles.
|
|
17
|
-
|
|
15
|
+
return WardPatientHeader ? (
|
|
16
|
+
<div className={styles.wardWorkspacePatientBanner}>
|
|
17
|
+
<WardPatientHeader wardPatient={wardPatient} />
|
|
18
18
|
</div>
|
|
19
|
+
) : (
|
|
20
|
+
<></>
|
|
19
21
|
);
|
|
20
22
|
};
|
|
21
23
|
|
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
@use '@carbon/layout';
|
|
2
1
|
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
3
2
|
|
|
4
|
-
.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
flex-wrap: wrap;
|
|
8
|
-
width: 100%;
|
|
9
|
-
padding: layout.$spacing-04;
|
|
10
|
-
background: $ui-01;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.dotSeparatedChildren {
|
|
14
|
-
> div:not(div:first-of-type):not(:empty) {
|
|
15
|
-
display: flex;
|
|
16
|
-
align-items: center;
|
|
17
|
-
|
|
18
|
-
&::before {
|
|
19
|
-
content: '·';
|
|
20
|
-
padding: 0 layout.$spacing-02;
|
|
21
|
-
}
|
|
3
|
+
.wardWorkspacePatientBanner {
|
|
4
|
+
> div {
|
|
5
|
+
background: $ui-01;
|
|
22
6
|
}
|
|
23
7
|
}
|
package/src/ward-workspace/patient-clinical-forms-workspace/patient-clinical-forms.workspace.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
1
|
import { ExtensionSlot } from '@openmrs/esm-framework';
|
|
2
|
+
import React, { useMemo } from 'react';
|
|
3
3
|
import type { WardPatientWorkspaceProps } from '../../types';
|
|
4
|
+
import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
|
|
4
5
|
|
|
5
6
|
const WardPatientClinicalFormsWorkspace: React.FC<WardPatientWorkspaceProps> = (props) => {
|
|
6
7
|
const { wardPatient, ...restWorkspaceProps } = props;
|
|
@@ -17,7 +18,12 @@ const WardPatientClinicalFormsWorkspace: React.FC<WardPatientWorkspaceProps> = (
|
|
|
17
18
|
[patientUuid, restWorkspaceProps],
|
|
18
19
|
);
|
|
19
20
|
|
|
20
|
-
return
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
<WardPatientWorkspaceBanner {...{ wardPatient }} />
|
|
24
|
+
<ExtensionSlot name="ward-patient-clinical-forms-workspace-slot" state={clinicalFormsExtensionState} />
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
21
27
|
};
|
|
22
28
|
|
|
23
29
|
export default WardPatientClinicalFormsWorkspace;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { UserAvatarIcon } from '@openmrs/esm-framework';
|
|
4
|
-
import { ActionMenuButton, launchWorkspace } from '@openmrs/esm-framework';
|
|
3
|
+
import { ActionMenuButton, launchWorkspace, UserAvatarIcon } from '@openmrs/esm-framework';
|
|
5
4
|
import type { WardPatientWorkspaceProps } from '../../types';
|
|
6
5
|
|
|
7
6
|
export default function WardPatientActionButton() {
|
|
@@ -12,7 +11,7 @@ export default function WardPatientActionButton() {
|
|
|
12
11
|
getIcon={(props) => <UserAvatarIcon {...props} />}
|
|
13
12
|
label={t('Patient', 'patient')}
|
|
14
13
|
iconDescription={t('Patient', 'patient')}
|
|
15
|
-
handler={() =>
|
|
14
|
+
handler={() => launchWorkspace<WardPatientWorkspaceProps>('ward-patient-workspace')}
|
|
16
15
|
type={'ward'}
|
|
17
16
|
/>
|
|
18
17
|
);
|
|
@@ -5,7 +5,19 @@
|
|
|
5
5
|
min-height: var(--desktop-workspace-window-height);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
.patientWorkspace {
|
|
9
|
+
display: flex;
|
|
10
|
+
height: 100%;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
.headerPatientDetail {
|
|
9
15
|
@include type.type-style('body-compact-02');
|
|
10
16
|
margin: 0 layout.$spacing-02;
|
|
11
17
|
}
|
|
18
|
+
|
|
19
|
+
.patientWorkspaceContentSlot {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex: 1;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
}
|