@kenyaemr/esm-ward-app 8.1.2-pre.214 → 8.5.1-pre.18
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 -35
- package/README.md +38 -0
- package/dist/1119.js +1 -0
- package/dist/1197.js +1 -0
- package/dist/1663.js +1 -0
- package/dist/1663.js.map +1 -0
- package/dist/1741.js +1 -0
- package/dist/1741.js.map +1 -0
- package/dist/1776.js +1 -0
- package/dist/1776.js.map +1 -0
- package/dist/1879.js +1 -0
- package/dist/1879.js.map +1 -0
- package/dist/1899.js +1 -0
- package/dist/1899.js.map +1 -0
- package/dist/1917.js +1 -0
- package/dist/1917.js.map +1 -0
- package/dist/2146.js +1 -0
- package/dist/2372.js +1 -0
- package/dist/2372.js.map +1 -0
- package/dist/2470.js +1 -0
- package/dist/2470.js.map +1 -0
- package/dist/2537.js +1 -0
- package/dist/2537.js.map +1 -0
- package/dist/2557.js +1 -0
- package/dist/2557.js.map +1 -0
- package/dist/2690.js +1 -0
- package/dist/2728.js +2 -0
- package/dist/{372.js.LICENSE.txt → 2728.js.LICENSE.txt} +2 -2
- package/dist/2728.js.map +1 -0
- package/dist/2775.js +2 -0
- package/dist/2775.js.LICENSE.txt +59 -0
- package/dist/2775.js.map +1 -0
- package/dist/2913.js +2 -0
- package/dist/2913.js.map +1 -0
- package/dist/2932.js +1 -0
- package/dist/2932.js.map +1 -0
- package/dist/2948.js +1 -0
- package/dist/2948.js.map +1 -0
- package/dist/3099.js +1 -0
- package/dist/3161.js +2 -0
- package/dist/{649.js.LICENSE.txt → 3161.js.LICENSE.txt} +2 -2
- package/dist/3161.js.map +1 -0
- package/dist/3365.js +1 -0
- package/dist/3365.js.map +1 -0
- package/dist/3373.js +2 -0
- package/dist/3373.js.LICENSE.txt +5 -0
- package/dist/3373.js.map +1 -0
- package/dist/3399.js +1 -0
- package/dist/3399.js.map +1 -0
- package/dist/3413.js +1 -0
- package/dist/3413.js.map +1 -0
- package/dist/3423.js +1 -0
- package/dist/3423.js.map +1 -0
- package/dist/3584.js +1 -0
- package/dist/3737.js +1 -0
- package/dist/3737.js.map +1 -0
- package/dist/3982.js +1 -0
- package/dist/3982.js.map +1 -0
- package/dist/4041.js +2 -0
- package/dist/4041.js.map +1 -0
- package/dist/4055.js +1 -0
- package/dist/4132.js +1 -0
- package/dist/4300.js +1 -0
- package/dist/4335.js +1 -0
- package/dist/4430.js +2 -0
- package/dist/4430.js.LICENSE.txt +29 -0
- package/dist/4430.js.map +1 -0
- package/dist/4618.js +1 -0
- package/dist/465.js +1 -0
- package/dist/465.js.map +1 -0
- package/dist/4652.js +1 -0
- package/dist/4701.js +2 -0
- package/dist/4701.js.LICENSE.txt +9 -0
- package/dist/4701.js.map +1 -0
- package/dist/4743.js +2 -0
- package/dist/4743.js.LICENSE.txt +9 -0
- package/dist/4743.js.map +1 -0
- package/dist/4944.js +1 -0
- package/dist/5173.js +1 -0
- package/dist/5241.js +1 -0
- package/dist/5442.js +1 -0
- package/dist/5661.js +1 -0
- package/dist/6012.js +2 -0
- package/dist/6012.js.LICENSE.txt +5 -0
- package/dist/6012.js.map +1 -0
- package/dist/6022.js +1 -0
- package/dist/6468.js +1 -0
- package/dist/6679.js +1 -0
- package/dist/6840.js +1 -0
- package/dist/6859.js +1 -0
- package/dist/7097.js +1 -0
- package/dist/7159.js +1 -0
- package/dist/7179.js +2 -0
- package/dist/7179.js.LICENSE.txt +9 -0
- package/dist/7179.js.map +1 -0
- package/dist/723.js +1 -0
- package/dist/7232.js +2 -0
- package/dist/7232.js.LICENSE.txt +9 -0
- package/dist/7232.js.map +1 -0
- package/dist/7524.js +1 -0
- package/dist/7524.js.map +1 -0
- package/dist/7617.js +1 -0
- package/dist/7661.js +1 -0
- package/dist/7661.js.map +1 -0
- package/dist/7886.js +1 -0
- package/dist/7886.js.map +1 -0
- package/dist/795.js +1 -0
- package/dist/8163.js +1 -0
- package/dist/8205.js +1 -0
- package/dist/8205.js.map +1 -0
- package/dist/8317.js +2 -0
- package/dist/8317.js.LICENSE.txt +15 -0
- package/dist/8317.js.map +1 -0
- package/dist/8349.js +1 -0
- package/dist/8501.js +1 -0
- package/dist/8501.js.map +1 -0
- package/dist/8522.js +1 -0
- package/dist/8522.js.map +1 -0
- package/dist/8618.js +1 -0
- package/dist/8622.js +1 -0
- package/dist/8622.js.map +1 -0
- package/dist/89.js +1 -0
- package/dist/89.js.map +1 -0
- package/dist/890.js +1 -0
- package/dist/9045.js +1 -0
- package/dist/9045.js.map +1 -0
- package/dist/9117.js +1 -0
- package/dist/9117.js.map +1 -0
- package/dist/9214.js +1 -0
- package/dist/9538.js +1 -0
- package/dist/9569.js +1 -0
- package/dist/986.js +1 -0
- package/dist/9876.js +2 -0
- package/dist/9876.js.LICENSE.txt +9 -0
- package/dist/9876.js.map +1 -0
- package/dist/9879.js +1 -0
- package/dist/9880.js +1 -0
- package/dist/9880.js.map +1 -0
- package/dist/9895.js +1 -0
- package/dist/9900.js +1 -0
- package/dist/9913.js +1 -0
- package/dist/kenyaemr-esm-ward-app.js +1 -1
- package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +1479 -235
- package/dist/kenyaemr-esm-ward-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +0 -35
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/mock.tsx +15 -2
- package/package.json +10 -5
- package/src/beds/{empty-bed-skeleton.tsx → empty-bed-skeleton.component.tsx} +2 -2
- package/src/config-schema.ts +48 -0
- package/src/declarations.d.ts +8 -0
- package/src/hooks/useAdmissionLocation.ts +24 -4
- package/src/hooks/useInpatientAdmission.ts +14 -10
- package/src/hooks/useInpatientAdmissionByPatients.ts +29 -0
- package/src/hooks/useInpatientRequest.ts +5 -1
- package/src/hooks/useInpatientRequestByPatients.ts +34 -0
- package/src/hooks/useIpdDischargeEncounter.ts +137 -0
- package/src/hooks/usePatientPendingOrders.ts +1 -1
- package/src/hooks/useRestPatient.ts +11 -3
- package/src/hooks/useSummaryMetrics.ts +103 -0
- package/src/hooks/useWardLocation.test.ts +1 -1
- package/src/hooks/useWardLocation.ts +18 -5
- package/src/hooks/useWardPatientGrouping.ts +63 -10
- package/src/index.ts +24 -4
- package/src/location-selector/location-selector.component.tsx +38 -5
- package/src/root.component.tsx +2 -2
- package/src/routes.json +60 -26
- package/src/types/index.ts +17 -6
- package/src/ward-patient-card/card-rows/admission-request-note-row.component.tsx +1 -1
- package/src/ward-patient-card/card-rows/coded-obs-tags-row.component.tsx +4 -4
- package/src/ward-patient-card/card-rows/mother-child-row.component.tsx +6 -6
- package/src/ward-patient-card/card-rows/pending-items-row.component.tsx +2 -3
- package/src/ward-patient-card/row-elements/{ward-patient-coded-obs-tags.tsx → ward-patient-coded-obs-tags.component.tsx} +5 -5
- package/src/ward-patient-card/row-elements/{ward-patient-header-address.tsx → ward-patient-header-address.component.tsx} +2 -2
- package/src/ward-patient-card/row-elements/{ward-patient-identifier.tsx → ward-patient-identifier.component.tsx} +7 -1
- package/src/ward-patient-card/row-elements/ward-patient-identifier.scss +3 -0
- package/src/ward-patient-card/row-elements/{ward-patient-obs.tsx → ward-patient-obs.component.tsx} +5 -5
- package/src/ward-patient-card/row-elements/{ward-patient-pending-transfer.tsx → ward-patient-pending-transfer.component.tsx} +3 -5
- package/src/ward-patient-card/row-elements/ward-patient-time-on-ward.component.tsx +73 -0
- package/src/ward-patient-card/row-elements/ward-patient-time-since-admission.component.tsx +29 -0
- package/src/ward-patient-card/ward-patient-card.component.tsx +5 -5
- package/src/ward-patient-card/ward-patient-card.scss +2 -6
- package/src/ward-patients/admitted-patients.tsx +218 -0
- package/src/ward-patients/awaiting-admission-patients.tsx +158 -0
- package/src/ward-patients/discharge-in-patients.tsx +183 -0
- package/src/ward-patients/discharge-patients.tsx +129 -0
- package/src/ward-patients/patient-cells.tsx +75 -0
- package/src/ward-patients/table-state-components.tsx +40 -0
- package/src/ward-patients/ward-patient.scss +24 -0
- package/src/ward-patients/ward-patients-table.tsx +38 -0
- package/src/ward-view/default-ward/default-ward-patient-card-header.component.tsx +8 -8
- package/src/ward-view/default-ward/default-ward-pending-patients.component.tsx +3 -3
- package/src/ward-view/default-ward/default-ward-view.component.tsx +33 -6
- package/src/ward-view/linelist-wards/Filters.tsx +25 -0
- package/src/ward-view/linelist-wards/Header.tsx +27 -0
- package/src/ward-view/linelist-wards/LineListTable.tsx +145 -0
- package/src/ward-view/linelist-wards/Metrics.tsx +21 -0
- package/src/ward-view/linelist-wards/WardPendingOutCell.tsx +15 -0
- package/src/ward-view/linelist-wards/WardsLineList.tsx +30 -0
- package/src/ward-view/linelist-wards/linelist-wards.scss +100 -0
- package/src/ward-view/materal-ward/maternal-ward-patient-card-header.component.tsx +8 -8
- package/src/ward-view/materal-ward/maternal-ward-patient-card.component.tsx +2 -2
- package/src/ward-view/materal-ward/maternal-ward-patient-card.test.tsx +1 -1
- package/src/ward-view/materal-ward/maternal-ward-pending-patients.component.tsx +1 -1
- package/src/ward-view/materal-ward/maternal-ward-view.component.tsx +4 -4
- package/src/ward-view/ward-view-content-wrapper.tsx +36 -0
- package/src/ward-view/ward-view.resource.ts +26 -8
- package/src/ward-view/ward-view.scss +15 -8
- package/src/ward-view/ward-view.test.tsx +8 -8
- package/src/ward-view/ward.component.tsx +2 -2
- package/src/ward-view-header/admission-requests-bar.component.tsx +18 -8
- package/src/ward-view-header/admission-requests-bar.test.tsx +3 -3
- package/src/ward-view-header/ward-metric.component.tsx +2 -3
- package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +12 -14
- package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +5 -5
- package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +1 -1
- package/src/ward-workspace/admission-request-workspace/admission-requests-action-button.extension.tsx +18 -0
- package/src/ward-workspace/admission-request-workspace/admission-requests-context.ts +20 -0
- package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +7 -6
- package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +15 -25
- package/src/ward-workspace/admit-patient-button.component.tsx +41 -18
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +14 -16
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +2 -2
- package/src/ward-workspace/bed-selector.component.tsx +1 -1
- package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.test.tsx +1 -1
- package/src/ward-workspace/cancel-admission-request-workspace/cancel-admission-request.workspace.tsx +4 -6
- package/src/ward-workspace/create-admission-encounter/create-admission-encounter-action-button.extension.tsx +46 -0
- package/src/ward-workspace/create-admission-encounter/create-admission-encounter.test.tsx +241 -0
- package/src/ward-workspace/create-admission-encounter/create-admission-encounter.workspace.tsx +152 -0
- package/src/ward-workspace/kenya-emr-patient-discharge/discharge-workspace-siderail.component.tsx +23 -0
- package/src/ward-workspace/kenya-emr-patient-discharge/patient-discharge.resource.tsx +123 -0
- package/src/ward-workspace/kenya-emr-patient-discharge/patient-discharge.workspace.tsx +101 -0
- package/src/ward-workspace/patient-discharge/patient-discharge.workspace.tsx +6 -15
- package/src/ward-workspace/patient-transfer-bed-swap/{patient-transfer-request-form.component.tsx → patient-admit-or-transfer-request-form.component.tsx} +56 -37
- package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +13 -3
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +11 -5
- package/src/ward-workspace/patient-transfer-request-workspace/patient-transfer-request.scss +5 -0
- package/src/ward-workspace/patient-transfer-request-workspace/patient-transfer-request.workspace.tsx +20 -6
- package/src/ward-workspace/ward-patient-notes/form/notes-form.component.tsx +2 -1
- package/src/ward-workspace/ward-patient-notes/history/note.component.tsx +3 -3
- package/src/ward.resource.ts +9 -3
- package/translations/am.json +123 -0
- package/translations/ar.json +123 -0
- package/translations/ar_SY.json +123 -0
- package/translations/bn.json +123 -0
- package/translations/de.json +123 -0
- package/translations/en.json +31 -3
- package/translations/en_US.json +123 -0
- package/translations/es.json +123 -0
- package/translations/es_MX.json +123 -0
- package/translations/fr.json +123 -0
- package/translations/he.json +123 -0
- package/translations/hi.json +123 -0
- package/translations/hi_IN.json +123 -0
- package/translations/id.json +123 -0
- package/translations/it.json +123 -0
- package/translations/ka.json +123 -0
- package/translations/km.json +123 -0
- package/translations/ku.json +123 -0
- package/translations/ky.json +123 -0
- package/translations/lg.json +123 -0
- package/translations/ne.json +123 -0
- package/translations/pl.json +123 -0
- package/translations/pt.json +123 -0
- package/translations/pt_BR.json +123 -0
- package/translations/qu.json +123 -0
- package/translations/ro_RO.json +123 -0
- package/translations/ru_RU.json +123 -0
- package/translations/si.json +123 -0
- package/translations/sw.json +123 -0
- package/translations/sw_KE.json +123 -0
- package/translations/tr.json +123 -0
- package/translations/tr_TR.json +123 -0
- package/translations/uk.json +123 -0
- package/translations/uz.json +123 -0
- package/translations/uz@Latn.json +123 -0
- package/translations/uz_UZ.json +123 -0
- package/translations/vi.json +123 -0
- package/translations/zh.json +123 -0
- package/translations/zh_CN.json +123 -0
- package/dist/109.js +0 -1
- package/dist/109.js.map +0 -1
- package/dist/124.js +0 -1
- package/dist/124.js.map +0 -1
- package/dist/125.js +0 -1
- package/dist/125.js.map +0 -1
- package/dist/126.js +0 -1
- package/dist/126.js.map +0 -1
- package/dist/130.js +0 -2
- package/dist/130.js.LICENSE.txt +0 -5
- package/dist/130.js.map +0 -1
- package/dist/146.js +0 -1
- package/dist/146.js.map +0 -1
- package/dist/15.js +0 -1
- package/dist/15.js.map +0 -1
- package/dist/325.js +0 -1
- package/dist/325.js.map +0 -1
- package/dist/348.js +0 -1
- package/dist/348.js.map +0 -1
- package/dist/362.js +0 -1
- package/dist/362.js.map +0 -1
- package/dist/372.js +0 -2
- package/dist/372.js.map +0 -1
- package/dist/443.js +0 -1
- package/dist/443.js.map +0 -1
- package/dist/471.js +0 -1
- package/dist/471.js.map +0 -1
- package/dist/481.js +0 -1
- package/dist/481.js.map +0 -1
- package/dist/53.js +0 -1
- package/dist/53.js.map +0 -1
- package/dist/559.js +0 -1
- package/dist/559.js.map +0 -1
- package/dist/574.js +0 -1
- package/dist/576.js +0 -1
- package/dist/576.js.map +0 -1
- package/dist/577.js +0 -1
- package/dist/577.js.map +0 -1
- package/dist/591.js +0 -2
- package/dist/591.js.map +0 -1
- package/dist/598.js +0 -1
- package/dist/598.js.map +0 -1
- package/dist/649.js +0 -2
- package/dist/649.js.map +0 -1
- package/dist/662.js +0 -1
- package/dist/662.js.map +0 -1
- package/dist/767.js +0 -1
- package/dist/767.js.map +0 -1
- package/dist/784.js +0 -2
- package/dist/784.js.map +0 -1
- package/dist/921.js +0 -1
- package/dist/921.js.map +0 -1
- package/dist/922.js +0 -1
- package/dist/922.js.map +0 -1
- package/dist/925.js +0 -2
- package/dist/925.js.LICENSE.txt +0 -40
- package/dist/925.js.map +0 -1
- package/dist/940.js +0 -1
- package/dist/940.js.map +0 -1
- package/dist/969.js +0 -1
- package/dist/969.js.map +0 -1
- package/dist/983.js +0 -1
- package/dist/983.js.map +0 -1
- package/package-lock.json +0 -5001
- package/src/ward-patient-card/row-elements/ward-patient-time-on-ward.tsx +0 -22
- package/src/ward-patient-card/row-elements/ward-patient-time-since-admission.tsx +0 -22
- /package/dist/{591.js.LICENSE.txt → 2913.js.LICENSE.txt} +0 -0
- /package/dist/{784.js.LICENSE.txt → 4041.js.LICENSE.txt} +0 -0
- /package/src/ward-patient-card/row-elements/{ward-patient-age.tsx → ward-patient-age.component.tsx} +0 -0
- /package/src/ward-patient-card/row-elements/{ward-patient-bed-number.tsx → ward-patient-bed-number.component.tsx} +0 -0
- /package/src/ward-patient-card/row-elements/{ward-patient-location.tsx → ward-patient-location.component.tsx} +0 -0
- /package/src/ward-patient-card/row-elements/{ward-patient-name.tsx → ward-patient-name.component.tsx} +0 -0
- /package/src/ward-patient-card/row-elements/{ward-patient-responsive-tooltip.tsx → ward-patient-responsive-tooltip.component.tsx} +0 -0
- /package/src/ward-patient-card/row-elements/{ward-patient-skeleton-text.tsx → ward-patient-skeleton-text.component.tsx} +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { useAppContext, useVisit } from '@openmrs/esm-framework';
|
|
2
|
+
import { screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import {
|
|
5
|
+
mockInpatientAdmissions,
|
|
6
|
+
mockInpatientRequests,
|
|
7
|
+
mockLocationInpatientWard,
|
|
8
|
+
mockLocationMosoriot,
|
|
9
|
+
mockPatientAlice,
|
|
10
|
+
} from '__mocks__';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { renderWithSwr } from '../../../../../tools';
|
|
13
|
+
import { mockWardViewContext } from '../../../mock';
|
|
14
|
+
import { useAssignedBedByPatient } from '../../hooks/useAssignedBedByPatient';
|
|
15
|
+
import useEmrConfiguration from '../../hooks/useEmrConfiguration';
|
|
16
|
+
import { useInpatientAdmissionByPatients } from '../../hooks/useInpatientAdmissionByPatients';
|
|
17
|
+
import { useInpatientRequestByPatients } from '../../hooks/useInpatientRequestByPatients';
|
|
18
|
+
import useRestPatient from '../../hooks/useRestPatient';
|
|
19
|
+
import useWardLocation from '../../hooks/useWardLocation';
|
|
20
|
+
import { type WardViewContext } from '../../types';
|
|
21
|
+
import { useAdmitPatient } from '../../ward.resource';
|
|
22
|
+
import CreateAdmissionEncounterWorkspace from './create-admission-encounter.workspace';
|
|
23
|
+
|
|
24
|
+
jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
|
|
25
|
+
|
|
26
|
+
const mockUseVisit = jest.mocked(useVisit).mockReturnValue({
|
|
27
|
+
activeVisit: {
|
|
28
|
+
encounters: [],
|
|
29
|
+
startDatetime: new Date().toISOString(),
|
|
30
|
+
uuid: 'mock-visit',
|
|
31
|
+
visitType: { display: 'Some Visit Type', uuid: 'some-visit-type-uuid' },
|
|
32
|
+
},
|
|
33
|
+
currentVisit: null,
|
|
34
|
+
currentVisitIsRetrospective: null,
|
|
35
|
+
mutate: jest.fn(),
|
|
36
|
+
error: undefined,
|
|
37
|
+
isLoading: false,
|
|
38
|
+
isValidating: false,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
jest.mock('../../hooks/useWardLocation', () => jest.fn());
|
|
42
|
+
const mockedUseWardLocation = jest.mocked(useWardLocation);
|
|
43
|
+
mockedUseWardLocation.mockReturnValue({
|
|
44
|
+
location: mockLocationInpatientWard,
|
|
45
|
+
isLoadingLocation: false,
|
|
46
|
+
errorFetchingLocation: null,
|
|
47
|
+
invalidLocation: false,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
jest.mock('../../hooks/useRestPatient', () => jest.fn());
|
|
51
|
+
const mockUseRestPatient = jest.mocked(useRestPatient).mockReturnValue({
|
|
52
|
+
patient: mockPatientAlice,
|
|
53
|
+
isLoading: false,
|
|
54
|
+
error: null,
|
|
55
|
+
isValidating: false,
|
|
56
|
+
mutate: jest.fn(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
jest.mock('../../hooks/useAssignedBedByPatient', () => ({
|
|
60
|
+
useAssignedBedByPatient: jest.fn(),
|
|
61
|
+
}));
|
|
62
|
+
// @ts-ignore - we don't need to mock the entire object
|
|
63
|
+
jest.mocked(useAssignedBedByPatient).mockReturnValue({
|
|
64
|
+
data: {
|
|
65
|
+
data: {
|
|
66
|
+
results: [
|
|
67
|
+
{
|
|
68
|
+
bedId: 1,
|
|
69
|
+
bedNumber: '1',
|
|
70
|
+
bedType: null,
|
|
71
|
+
patients: [mockPatientAlice],
|
|
72
|
+
physicalLocation: mockLocationInpatientWard,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
isLoading: false,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
jest.mock('../../hooks/useInpatientAdmissionByPatients', () => ({
|
|
81
|
+
useInpatientAdmissionByPatients: jest.fn(),
|
|
82
|
+
}));
|
|
83
|
+
const mockedUseInpatientAdmissionByPatients = jest.mocked(useInpatientAdmissionByPatients).mockReturnValue({
|
|
84
|
+
data: [],
|
|
85
|
+
hasMore: false,
|
|
86
|
+
loadMore: jest.fn(),
|
|
87
|
+
isValidating: false,
|
|
88
|
+
isLoading: false,
|
|
89
|
+
error: undefined,
|
|
90
|
+
mutate: jest.fn(),
|
|
91
|
+
totalCount: mockInpatientAdmissions.length,
|
|
92
|
+
nextUri: null,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
jest.mock('../../hooks/useInpatientRequestByPatients', () => ({
|
|
96
|
+
useInpatientRequestByPatients: jest.fn(),
|
|
97
|
+
}));
|
|
98
|
+
const mockedUseInpatientRequestByPatients = jest.mocked(useInpatientRequestByPatients).mockReturnValue({
|
|
99
|
+
inpatientRequests: [],
|
|
100
|
+
hasMore: false,
|
|
101
|
+
loadMore: jest.fn(),
|
|
102
|
+
isValidating: false,
|
|
103
|
+
isLoading: false,
|
|
104
|
+
error: undefined,
|
|
105
|
+
mutate: jest.fn(),
|
|
106
|
+
totalCount: mockInpatientAdmissions.length,
|
|
107
|
+
nextUri: null,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
jest.mock('../../hooks/useEmrConfiguration', () => jest.fn());
|
|
111
|
+
jest.mocked(useEmrConfiguration).mockReturnValue({
|
|
112
|
+
isLoadingEmrConfiguration: false,
|
|
113
|
+
errorFetchingEmrConfiguration: null,
|
|
114
|
+
// @ts-ignore - we only need these keys for now
|
|
115
|
+
emrConfiguration: {
|
|
116
|
+
admissionEncounterType: {
|
|
117
|
+
uuid: 'admission-encounter-type-uuid',
|
|
118
|
+
display: 'Admission Encounter',
|
|
119
|
+
},
|
|
120
|
+
transferWithinHospitalEncounterType: {
|
|
121
|
+
uuid: 'transfer-within-hospital-encounter-type-uuid',
|
|
122
|
+
display: 'Transfer Within Hospital Encounter Type',
|
|
123
|
+
},
|
|
124
|
+
clinicianEncounterRole: {
|
|
125
|
+
uuid: 'clinician-encounter-role-uuid',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
mutateEmrConfiguration: jest.fn(),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
jest.mock('../../ward.resource', () => ({
|
|
132
|
+
useAdmitPatient: jest.fn(),
|
|
133
|
+
assignPatientToBed: jest.fn(),
|
|
134
|
+
removePatientFromBed: jest.fn(),
|
|
135
|
+
}));
|
|
136
|
+
const mockedUseAdmitPatient: ReturnType<typeof useAdmitPatient> = {
|
|
137
|
+
admitPatient: jest.fn(),
|
|
138
|
+
isLoadingEmrConfiguration: false,
|
|
139
|
+
errorFetchingEmrConfiguration: false,
|
|
140
|
+
};
|
|
141
|
+
jest.mocked(useAdmitPatient).mockReturnValue(mockedUseAdmitPatient);
|
|
142
|
+
const mockedAdmitPatient = mockedUseAdmitPatient.admitPatient;
|
|
143
|
+
// @ts-ignore - we only need these two keys for now
|
|
144
|
+
mockedAdmitPatient.mockResolvedValue({
|
|
145
|
+
ok: true,
|
|
146
|
+
data: {
|
|
147
|
+
uuid: 'encounter-uuid',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('CreateAdmissionEncounterWorkspace', () => {
|
|
152
|
+
it('should render patient header and admit patient button', async () => {
|
|
153
|
+
const user = userEvent.setup();
|
|
154
|
+
renderCreateAdmissionEncounterWorkspace(mockPatientAlice.uuid);
|
|
155
|
+
expect(screen.getByText(mockPatientAlice.person?.preferredName?.display)).toBeInTheDocument();
|
|
156
|
+
const admitPatientButton = screen.getByRole('button', { name: /admit patient/i });
|
|
157
|
+
expect(admitPatientButton).toBeEnabled();
|
|
158
|
+
|
|
159
|
+
await user.click(admitPatientButton);
|
|
160
|
+
expect(mockedAdmitPatient).toHaveBeenCalledWith(expect.any(Object), 'ADMIT', 'mock-visit');
|
|
161
|
+
});
|
|
162
|
+
it('should have warning when patient has a pending admission request', async () => {
|
|
163
|
+
mockedUseInpatientRequestByPatients.mockReturnValueOnce({
|
|
164
|
+
inpatientRequests: mockInpatientRequests,
|
|
165
|
+
hasMore: false,
|
|
166
|
+
loadMore: jest.fn(),
|
|
167
|
+
isValidating: false,
|
|
168
|
+
isLoading: false,
|
|
169
|
+
error: undefined,
|
|
170
|
+
mutate: jest.fn(),
|
|
171
|
+
totalCount: mockInpatientAdmissions.length,
|
|
172
|
+
nextUri: null,
|
|
173
|
+
});
|
|
174
|
+
const user = userEvent.setup();
|
|
175
|
+
renderCreateAdmissionEncounterWorkspace(mockPatientAlice.uuid);
|
|
176
|
+
expect(screen.getByText(mockPatientAlice.person?.preferredName?.display)).toBeInTheDocument();
|
|
177
|
+
expect(
|
|
178
|
+
screen.getByText(
|
|
179
|
+
'Patient already has a pending admission request to location ' + mockLocationInpatientWard.display,
|
|
180
|
+
),
|
|
181
|
+
).toBeInTheDocument();
|
|
182
|
+
const admitPatientButton = screen.getByRole('button', { name: /admit patient/i });
|
|
183
|
+
expect(admitPatientButton).toBeEnabled();
|
|
184
|
+
|
|
185
|
+
await user.click(admitPatientButton);
|
|
186
|
+
expect(mockedAdmitPatient).toHaveBeenCalledWith(expect.any(Object), 'ADMIT', 'mock-visit');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should have warning when patient is already admitted elsewhere', async () => {
|
|
190
|
+
mockedUseInpatientAdmissionByPatients.mockReturnValueOnce({
|
|
191
|
+
data: [{ ...mockInpatientAdmissions[0], currentInpatientLocation: mockLocationMosoriot }],
|
|
192
|
+
hasMore: false,
|
|
193
|
+
loadMore: jest.fn(),
|
|
194
|
+
isValidating: false,
|
|
195
|
+
isLoading: false,
|
|
196
|
+
error: undefined,
|
|
197
|
+
mutate: jest.fn(),
|
|
198
|
+
totalCount: mockInpatientAdmissions.length,
|
|
199
|
+
nextUri: null,
|
|
200
|
+
});
|
|
201
|
+
const user = userEvent.setup();
|
|
202
|
+
renderCreateAdmissionEncounterWorkspace(mockPatientAlice.uuid);
|
|
203
|
+
expect(screen.getByText(mockPatientAlice.person?.preferredName?.display)).toBeInTheDocument();
|
|
204
|
+
expect(screen.getByText('Patient currently admitted to ' + mockLocationMosoriot.display)).toBeInTheDocument();
|
|
205
|
+
const admitPatientButton = screen.getByRole('button', { name: /transfer patient/i });
|
|
206
|
+
expect(admitPatientButton).toBeEnabled();
|
|
207
|
+
|
|
208
|
+
await user.click(admitPatientButton);
|
|
209
|
+
expect(mockedAdmitPatient).toHaveBeenCalledWith(expect.any(Object), 'TRANSFER', 'mock-visit');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should disable admit patient button when patient is already admitted to current location', () => {
|
|
213
|
+
mockedUseInpatientAdmissionByPatients.mockReturnValueOnce({
|
|
214
|
+
data: mockInpatientAdmissions,
|
|
215
|
+
hasMore: false,
|
|
216
|
+
loadMore: jest.fn(),
|
|
217
|
+
isValidating: false,
|
|
218
|
+
isLoading: false,
|
|
219
|
+
error: undefined,
|
|
220
|
+
mutate: jest.fn(),
|
|
221
|
+
totalCount: mockInpatientAdmissions.length,
|
|
222
|
+
nextUri: null,
|
|
223
|
+
});
|
|
224
|
+
renderCreateAdmissionEncounterWorkspace(mockPatientAlice.uuid);
|
|
225
|
+
expect(screen.getByText(mockPatientAlice.person?.preferredName?.display)).toBeInTheDocument();
|
|
226
|
+
expect(screen.getByText('Patient already admitted to current location')).toBeInTheDocument();
|
|
227
|
+
expect(screen.getByRole('button', { name: /admit patient/i })).toBeDisabled();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
function renderCreateAdmissionEncounterWorkspace(patentUuid: string) {
|
|
232
|
+
renderWithSwr(
|
|
233
|
+
<CreateAdmissionEncounterWorkspace
|
|
234
|
+
patientUuid={patentUuid}
|
|
235
|
+
closeWorkspace={jest.fn()}
|
|
236
|
+
promptBeforeClosing={jest.fn()}
|
|
237
|
+
closeWorkspaceWithSavedChanges={jest.fn()}
|
|
238
|
+
setTitle={jest.fn()}
|
|
239
|
+
/>,
|
|
240
|
+
);
|
|
241
|
+
}
|
package/src/ward-workspace/create-admission-encounter/create-admission-encounter.workspace.tsx
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button, InlineNotification, SkeletonText } from '@carbon/react';
|
|
4
|
+
import { ArrowLeftIcon, type DefaultWorkspaceProps, useVisit } from '@openmrs/esm-framework';
|
|
5
|
+
import { useAssignedBedByPatient } from '../../hooks/useAssignedBedByPatient';
|
|
6
|
+
import { useInpatientAdmissionByPatients } from '../../hooks/useInpatientAdmissionByPatients';
|
|
7
|
+
import { useInpatientRequestByPatients } from '../../hooks/useInpatientRequestByPatients';
|
|
8
|
+
import useRestPatient from '../../hooks/useRestPatient';
|
|
9
|
+
import useWardLocation from '../../hooks/useWardLocation';
|
|
10
|
+
import type { Bed, WardPatient } from '../../types';
|
|
11
|
+
import AdmitPatientButton from '../admit-patient-button.component';
|
|
12
|
+
import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
|
|
13
|
+
|
|
14
|
+
export interface CreateAdmissionEncounterWorkspaceProps {
|
|
15
|
+
patientUuid: string;
|
|
16
|
+
handleReturnToSearchList?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This is the workspace that opens when clicking on a search result
|
|
21
|
+
* from the workspace triggered by the "Add patient to ward" button.
|
|
22
|
+
* It directly admits them to the current ward locations
|
|
23
|
+
*/
|
|
24
|
+
const CreateAdmissionEncounterWorkspace: React.FC<CreateAdmissionEncounterWorkspaceProps & DefaultWorkspaceProps> = ({
|
|
25
|
+
patientUuid,
|
|
26
|
+
handleReturnToSearchList,
|
|
27
|
+
closeWorkspaceWithSavedChanges,
|
|
28
|
+
}) => {
|
|
29
|
+
const { location } = useWardLocation();
|
|
30
|
+
const { patient, isLoading: isLoadingPatient, error: errorLoadingPatient } = useRestPatient(patientUuid);
|
|
31
|
+
const { activeVisit, isLoading: isLoadingVisit, error: errorLoadingVisit } = useVisit(patientUuid);
|
|
32
|
+
const { t } = useTranslation();
|
|
33
|
+
const { data: bedData, isLoading: isLoadingBed, error: errorLoadingBed } = useAssignedBedByPatient(patientUuid);
|
|
34
|
+
const {
|
|
35
|
+
data: inpatientAdmissions,
|
|
36
|
+
isLoading: isLoadingInpatientAdmission,
|
|
37
|
+
error: errorInpatientAdmission,
|
|
38
|
+
} = useInpatientAdmissionByPatients([patientUuid]);
|
|
39
|
+
const {
|
|
40
|
+
inpatientRequests,
|
|
41
|
+
isLoading: isLoadingInpatientRequest,
|
|
42
|
+
error: errorInpatientRequests,
|
|
43
|
+
} = useInpatientRequestByPatients([patientUuid]);
|
|
44
|
+
|
|
45
|
+
const isLoading =
|
|
46
|
+
isLoadingPatient || isLoadingVisit || isLoadingBed || isLoadingInpatientAdmission || isLoadingInpatientRequest;
|
|
47
|
+
const hasError =
|
|
48
|
+
errorLoadingPatient || errorLoadingVisit || errorLoadingBed || errorInpatientAdmission || errorInpatientRequests;
|
|
49
|
+
|
|
50
|
+
if (isLoading) {
|
|
51
|
+
return <SkeletonText />;
|
|
52
|
+
} else if (hasError) {
|
|
53
|
+
return (
|
|
54
|
+
<div>
|
|
55
|
+
<InlineNotification
|
|
56
|
+
kind="error"
|
|
57
|
+
lowContrast={true}
|
|
58
|
+
title={t('errorLoadingPatientInfo', 'Error loading patient info')}
|
|
59
|
+
/>
|
|
60
|
+
<Button
|
|
61
|
+
kind="ghost"
|
|
62
|
+
renderIcon={(props) => <ArrowLeftIcon size={24} {...props} />}
|
|
63
|
+
iconDescription={t('backToSearchResults', 'Back to search results')}
|
|
64
|
+
size="sm"
|
|
65
|
+
onClick={() => handleReturnToSearchList?.()}>
|
|
66
|
+
<span>{t('backToSearchResults', 'Back to search results')}</span>
|
|
67
|
+
</Button>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
const assignedBedDetail = bedData.data.results[0];
|
|
72
|
+
const isAssignedBedAtCurrentLocation = assignedBedDetail?.physicalLocation?.uuid == location.uuid;
|
|
73
|
+
const isAdmittedToCurrentLocation = inpatientAdmissions[0]?.currentInpatientLocation?.uuid == location.uuid;
|
|
74
|
+
const isAdmittedToOtherLocation = inpatientAdmissions[0] && !isAdmittedToCurrentLocation;
|
|
75
|
+
|
|
76
|
+
const wardPatient: WardPatient = {
|
|
77
|
+
patient,
|
|
78
|
+
visit: activeVisit,
|
|
79
|
+
bed: isAssignedBedAtCurrentLocation
|
|
80
|
+
? ({
|
|
81
|
+
id: assignedBedDetail.bedId,
|
|
82
|
+
bedNumber: assignedBedDetail.bedNumber,
|
|
83
|
+
bedType: assignedBedDetail.bedType,
|
|
84
|
+
} as Bed)
|
|
85
|
+
: null,
|
|
86
|
+
inpatientAdmission: inpatientAdmissions[0],
|
|
87
|
+
inpatientRequest: null,
|
|
88
|
+
};
|
|
89
|
+
return (
|
|
90
|
+
<div>
|
|
91
|
+
<WardPatientWorkspaceBanner wardPatient={wardPatient} />
|
|
92
|
+
{activeVisit ? (
|
|
93
|
+
<div>
|
|
94
|
+
{isAdmittedToCurrentLocation && (
|
|
95
|
+
<InlineNotification
|
|
96
|
+
kind="warning"
|
|
97
|
+
lowContrast={true}
|
|
98
|
+
hideCloseButton={true}
|
|
99
|
+
title={t('patientAlreadyAdmittedToCurrentLocation', 'Patient already admitted to current location')}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
{isAdmittedToOtherLocation && (
|
|
103
|
+
<InlineNotification
|
|
104
|
+
kind="warning"
|
|
105
|
+
lowContrast={true}
|
|
106
|
+
hideCloseButton={true}
|
|
107
|
+
title={t('patientCurrentlyAdmittedToWardLocation', 'Patient currently admitted to {{wardLocation}}', {
|
|
108
|
+
wardLocation: inpatientAdmissions[0].currentInpatientLocation.display,
|
|
109
|
+
})}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
{inpatientRequests[0] && (
|
|
113
|
+
<InlineNotification
|
|
114
|
+
kind="warning"
|
|
115
|
+
lowContrast={true}
|
|
116
|
+
hideCloseButton={true}
|
|
117
|
+
title={t(
|
|
118
|
+
'patientHasPendingAdmissionRequest',
|
|
119
|
+
'Patient already has a pending admission request to location {{location}}',
|
|
120
|
+
{
|
|
121
|
+
location: inpatientRequests[0].dispositionLocation.display,
|
|
122
|
+
},
|
|
123
|
+
)}
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
<AdmitPatientButton
|
|
127
|
+
wardPatient={wardPatient}
|
|
128
|
+
dispositionType={inpatientAdmissions[0] ? 'TRANSFER' : 'ADMIT'}
|
|
129
|
+
onAdmitPatientSuccess={() => closeWorkspaceWithSavedChanges()}
|
|
130
|
+
disabled={isAdmittedToCurrentLocation}
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
) : (
|
|
134
|
+
<div>
|
|
135
|
+
{/* TODO: This is a placeholder, and will likely change with ongoing designs with RDE */}
|
|
136
|
+
{t('patienthasNoActiveVisit', 'Patient has no active visit')}
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
139
|
+
<Button
|
|
140
|
+
kind="ghost"
|
|
141
|
+
renderIcon={(props) => <ArrowLeftIcon size={24} {...props} />}
|
|
142
|
+
iconDescription={t('backToSearchResults', 'Back to search results')}
|
|
143
|
+
size="sm"
|
|
144
|
+
onClick={() => handleReturnToSearchList?.()}>
|
|
145
|
+
<span>{t('backToSearchResults', 'Back to search results')}</span>
|
|
146
|
+
</Button>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export default CreateAdmissionEncounterWorkspace;
|
package/src/ward-workspace/kenya-emr-patient-discharge/discharge-workspace-siderail.component.tsx
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ActionMenuButton, launchWorkspace } from '@openmrs/esm-framework';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Exit } from '@carbon/react/icons';
|
|
5
|
+
|
|
6
|
+
const DischargeIcon = (props) => <Exit {...props} />;
|
|
7
|
+
|
|
8
|
+
export default function PatientDischargeSideRailIcon() {
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
const handler = () => {
|
|
11
|
+
launchWorkspace('patient-care-discharge-workspace');
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ActionMenuButton
|
|
16
|
+
getIcon={DischargeIcon}
|
|
17
|
+
label={t('discharge', 'Discharge')}
|
|
18
|
+
iconDescription={t('discharge', 'Discharge')}
|
|
19
|
+
handler={handler}
|
|
20
|
+
type="ward-patient-discharge"
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Encounter,
|
|
3
|
+
openmrsFetch,
|
|
4
|
+
type OpenmrsResource,
|
|
5
|
+
restBaseUrl,
|
|
6
|
+
showSnackbar,
|
|
7
|
+
useAppContext,
|
|
8
|
+
useSession,
|
|
9
|
+
type Visit,
|
|
10
|
+
} from '@openmrs/esm-framework';
|
|
11
|
+
import { useTranslation } from 'react-i18next';
|
|
12
|
+
import { type WardPatient } from '../../types';
|
|
13
|
+
|
|
14
|
+
export function removePatientFromBed(bedId: number, patientUuid: string) {
|
|
15
|
+
return openmrsFetch(`${restBaseUrl}/beds/${bedId}?patientUuid=${patientUuid}`, {
|
|
16
|
+
method: 'DELETE',
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const createDischargeEncounterPayload = (
|
|
21
|
+
patientUuid: string,
|
|
22
|
+
encounterType: OpenmrsResource,
|
|
23
|
+
location: OpenmrsResource,
|
|
24
|
+
currentProvider: OpenmrsResource,
|
|
25
|
+
visitUuid: string,
|
|
26
|
+
clinicianEncounterRole: OpenmrsResource,
|
|
27
|
+
) => {
|
|
28
|
+
const encounterPayload = {
|
|
29
|
+
patient: patientUuid,
|
|
30
|
+
encounterType,
|
|
31
|
+
location: location?.uuid,
|
|
32
|
+
encounterProviders: [
|
|
33
|
+
{
|
|
34
|
+
provider: currentProvider?.uuid,
|
|
35
|
+
encounterRole: clinicianEncounterRole?.uuid,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
obs: [],
|
|
39
|
+
visit: visitUuid,
|
|
40
|
+
};
|
|
41
|
+
return encounterPayload;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const createDischargeEncounter = (encounterPayload: any) => {
|
|
45
|
+
return openmrsFetch(`${restBaseUrl}/encounter`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
body: encounterPayload,
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom hook for handling patient discharge operations
|
|
54
|
+
*
|
|
55
|
+
* This hook provides functionality to discharge a patient by:
|
|
56
|
+
* 1. Creating a discharge encounter
|
|
57
|
+
* 2. Removing the patient from their bed (if applicable)
|
|
58
|
+
* 3. Updating the ward patient group details
|
|
59
|
+
*
|
|
60
|
+
* @returns {Object} An object containing the handleDischarge function
|
|
61
|
+
* @property {Function} handleDischarge - Function to handle the patient discharge process
|
|
62
|
+
*/
|
|
63
|
+
export const usePatientDischarge = () => {
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
const { wardPatientGroupDetails } =
|
|
66
|
+
useAppContext<{ wardPatientGroupDetails: { mutate: () => void } }>('ward-view-context') ?? {};
|
|
67
|
+
const session = useSession();
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Handles the patient discharge process
|
|
71
|
+
*
|
|
72
|
+
* @param {Encounter} encounter - The current encounter
|
|
73
|
+
* @param {WardPatient} wardPatient - The ward patient information
|
|
74
|
+
* @param {Record<string, unknown>} emrConfiguration - EMR configuration containing encounter types and roles
|
|
75
|
+
* @param {Visit} visit - The current visit
|
|
76
|
+
* @returns {Promise<void>} A promise that resolves when the discharge process is complete
|
|
77
|
+
*/
|
|
78
|
+
const handleDischarge = async (
|
|
79
|
+
encounter: Encounter,
|
|
80
|
+
wardPatient: WardPatient,
|
|
81
|
+
emrConfiguration: Record<string, unknown>,
|
|
82
|
+
visit: Visit,
|
|
83
|
+
) => {
|
|
84
|
+
try {
|
|
85
|
+
const encounterPayload = createDischargeEncounterPayload(
|
|
86
|
+
wardPatient.patient.uuid,
|
|
87
|
+
emrConfiguration.exitFromInpatientEncounterType as OpenmrsResource,
|
|
88
|
+
session?.sessionLocation as OpenmrsResource,
|
|
89
|
+
session?.currentProvider as OpenmrsResource,
|
|
90
|
+
visit.uuid,
|
|
91
|
+
emrConfiguration.clinicianEncounterRole as OpenmrsResource,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const dischargeResponse = await createDischargeEncounter(encounterPayload);
|
|
95
|
+
|
|
96
|
+
if (!dischargeResponse?.ok) {
|
|
97
|
+
throw new Error('Failed to create discharge encounter');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (wardPatient?.bed?.id) {
|
|
101
|
+
const bedRemovalResponse = await removePatientFromBed(wardPatient.bed.id, wardPatient?.patient?.uuid);
|
|
102
|
+
if (!bedRemovalResponse?.ok) {
|
|
103
|
+
throw new Error('Failed to remove patient from bed');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
showSnackbar({
|
|
108
|
+
title: t('patientWasDischarged', 'Patient was discharged'),
|
|
109
|
+
kind: 'success',
|
|
110
|
+
});
|
|
111
|
+
} catch (err) {
|
|
112
|
+
showSnackbar({
|
|
113
|
+
title: t('errorDischargingPatient', 'Error discharging patient'),
|
|
114
|
+
subtitle: err instanceof Error ? err.message : 'Unknown error occurred',
|
|
115
|
+
kind: 'error',
|
|
116
|
+
});
|
|
117
|
+
} finally {
|
|
118
|
+
wardPatientGroupDetails?.mutate();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return { handleDischarge };
|
|
123
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { InlineLoading, InlineNotification } from '@carbon/react';
|
|
4
|
+
import {
|
|
5
|
+
type DefaultWorkspaceProps,
|
|
6
|
+
type Encounter,
|
|
7
|
+
ExtensionSlot,
|
|
8
|
+
useEmrConfiguration,
|
|
9
|
+
usePatient,
|
|
10
|
+
} from '@openmrs/esm-framework';
|
|
11
|
+
import { usePatientDischarge } from './patient-discharge.resource';
|
|
12
|
+
import { type WardPatient } from '../../types';
|
|
13
|
+
|
|
14
|
+
type PatientDischargeWorkspaceProps = DefaultWorkspaceProps & {
|
|
15
|
+
readonly patientUuid: string;
|
|
16
|
+
readonly wardPatient: WardPatient;
|
|
17
|
+
readonly formUuid: string;
|
|
18
|
+
readonly dischargePatientOnSuccesfullSubmission?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function PatientDischargeWorkspace(props: PatientDischargeWorkspaceProps) {
|
|
22
|
+
const { t } = useTranslation();
|
|
23
|
+
const {
|
|
24
|
+
patientUuid,
|
|
25
|
+
closeWorkspace,
|
|
26
|
+
closeWorkspaceWithSavedChanges,
|
|
27
|
+
wardPatient,
|
|
28
|
+
promptBeforeClosing,
|
|
29
|
+
formUuid,
|
|
30
|
+
dischargePatientOnSuccesfullSubmission = true,
|
|
31
|
+
} = props;
|
|
32
|
+
const { visit: currentVisit } = wardPatient ?? {};
|
|
33
|
+
const { patient, isLoading: isLoadingPatient, error: patientError } = usePatient(patientUuid);
|
|
34
|
+
const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
|
|
35
|
+
|
|
36
|
+
const { handleDischarge } = usePatientDischarge();
|
|
37
|
+
|
|
38
|
+
const state = useMemo<Record<string, unknown>>(
|
|
39
|
+
() => ({
|
|
40
|
+
view: 'form',
|
|
41
|
+
formUuid,
|
|
42
|
+
visitUuid: currentVisit?.uuid ?? null,
|
|
43
|
+
visitTypeUuid: currentVisit?.visitType?.uuid ?? null,
|
|
44
|
+
patientUuid: patientUuid ?? null,
|
|
45
|
+
patient,
|
|
46
|
+
encounterUuid: '',
|
|
47
|
+
closeWorkspaceWithSavedChanges,
|
|
48
|
+
closeWorkspace,
|
|
49
|
+
promptBeforeClosing,
|
|
50
|
+
handlePostResponse: (encounter: Encounter) => {
|
|
51
|
+
if (dischargePatientOnSuccesfullSubmission)
|
|
52
|
+
handleDischarge(encounter, wardPatient, emrConfiguration as Record<string, unknown>, currentVisit);
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
[
|
|
56
|
+
patientUuid,
|
|
57
|
+
currentVisit,
|
|
58
|
+
patient,
|
|
59
|
+
closeWorkspace,
|
|
60
|
+
promptBeforeClosing,
|
|
61
|
+
emrConfiguration,
|
|
62
|
+
closeWorkspaceWithSavedChanges,
|
|
63
|
+
handleDischarge,
|
|
64
|
+
dischargePatientOnSuccesfullSubmission,
|
|
65
|
+
formUuid,
|
|
66
|
+
wardPatient,
|
|
67
|
+
],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const isLoading = isLoadingPatient || isLoadingEmrConfiguration;
|
|
71
|
+
const error = patientError || errorFetchingEmrConfiguration;
|
|
72
|
+
|
|
73
|
+
if (isLoading) {
|
|
74
|
+
return <InlineLoading description={t('loading', 'Loading')} iconDescription={t('loading', 'Loading data...')} />;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (error) {
|
|
78
|
+
return (
|
|
79
|
+
<InlineNotification
|
|
80
|
+
aria-label={t('error', 'Error')}
|
|
81
|
+
kind="error"
|
|
82
|
+
onClose={() => {}}
|
|
83
|
+
onCloseButtonClick={() => {}}
|
|
84
|
+
statusIconDescription="notification"
|
|
85
|
+
subtitle={t('errorLoadingPatientWorkspace', 'Error loading patient workspace {{errorMessage}}', {
|
|
86
|
+
errorMessage: error?.message,
|
|
87
|
+
})}
|
|
88
|
+
title={t('error', 'Error')}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div>
|
|
95
|
+
{/* <pre>{JSON.stringify(currentVisit, null, 2)}</pre> */}
|
|
96
|
+
{patient && <ExtensionSlot name="form-widget-slot" state={state} />}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default PatientDischargeWorkspace;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
2
3
|
import { Button, ButtonSet, InlineNotification } from '@carbon/react';
|
|
3
4
|
import { Exit } from '@carbon/react/icons';
|
|
4
|
-
import {
|
|
5
|
-
import { ExtensionSlot, showSnackbar, useAppContext, useSession } from '@openmrs/esm-framework';
|
|
5
|
+
import { ExtensionSlot, showSnackbar, useAppContext } from '@openmrs/esm-framework';
|
|
6
6
|
import { type WardPatientWorkspaceProps, type WardViewContext } from '../../types';
|
|
7
7
|
import { removePatientFromBed, useCreateEncounter } from '../../ward.resource';
|
|
8
|
-
import useWardLocation from '../../hooks/useWardLocation';
|
|
9
8
|
import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
|
|
10
9
|
import styles from './patient-discharge.scss';
|
|
11
10
|
|
|
@@ -13,8 +12,6 @@ export default function PatientDischargeWorkspace(props: WardPatientWorkspacePro
|
|
|
13
12
|
const { wardPatient, closeWorkspaceWithSavedChanges } = props;
|
|
14
13
|
const { t } = useTranslation();
|
|
15
14
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
16
|
-
const { currentProvider } = useSession();
|
|
17
|
-
const { location } = useWardLocation();
|
|
18
15
|
const { createEncounter, emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } =
|
|
19
16
|
useCreateEncounter();
|
|
20
17
|
const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
|
|
@@ -22,7 +19,7 @@ export default function PatientDischargeWorkspace(props: WardPatientWorkspacePro
|
|
|
22
19
|
const submitDischarge = useCallback(() => {
|
|
23
20
|
setIsSubmitting(true);
|
|
24
21
|
|
|
25
|
-
createEncounter(wardPatient?.patient, emrConfiguration.exitFromInpatientEncounterType)
|
|
22
|
+
createEncounter(wardPatient?.patient, emrConfiguration.exitFromInpatientEncounterType, wardPatient?.visit.uuid)
|
|
26
23
|
.then((response) => {
|
|
27
24
|
if (response?.ok) {
|
|
28
25
|
if (wardPatient?.bed?.id) {
|
|
@@ -51,17 +48,11 @@ export default function PatientDischargeWorkspace(props: WardPatientWorkspacePro
|
|
|
51
48
|
closeWorkspaceWithSavedChanges();
|
|
52
49
|
wardPatientGroupDetails.mutate();
|
|
53
50
|
});
|
|
54
|
-
}, [
|
|
55
|
-
createEncounter,
|
|
56
|
-
wardPatient?.patient,
|
|
57
|
-
wardPatient.bed.id,
|
|
58
|
-
emrConfiguration,
|
|
59
|
-
t,
|
|
60
|
-
closeWorkspaceWithSavedChanges,
|
|
61
|
-
wardPatientGroupDetails,
|
|
62
|
-
]);
|
|
51
|
+
}, [createEncounter, wardPatient, emrConfiguration, t, closeWorkspaceWithSavedChanges, wardPatientGroupDetails]);
|
|
63
52
|
|
|
64
53
|
if (!wardPatientGroupDetails) return <></>;
|
|
54
|
+
if (!wardPatient?.visit) return <InlineNotification kind="error" title={t('noVisit', 'No visit found')} />;
|
|
55
|
+
|
|
65
56
|
return (
|
|
66
57
|
<div className={styles.workspaceContent}>
|
|
67
58
|
<div className={styles.patientWorkspaceBanner}>
|