@kenyaemr/esm-morgue-app 5.4.1-pre.1848 → 5.4.1-pre.1850
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 +84 -21
- package/dist/160.js +1 -0
- package/dist/160.js.map +1 -0
- package/dist/195.js +1 -0
- package/dist/195.js.map +1 -0
- package/dist/240.js +1 -0
- package/dist/240.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/300.js +1 -1
- package/dist/{561.js → 521.js} +1 -1
- package/dist/521.js.map +1 -0
- package/dist/527.js +1 -0
- package/dist/527.js.map +1 -0
- package/dist/596.js +1 -0
- package/dist/596.js.map +1 -0
- package/dist/652.js +1 -1
- package/dist/652.js.map +1 -1
- package/dist/659.js +2 -0
- package/dist/659.js.map +1 -0
- package/dist/675.js +2 -0
- package/dist/{485.js.map → 675.js.map} +1 -1
- package/dist/730.js +1 -0
- package/dist/730.js.map +1 -0
- package/dist/755.js +1 -0
- package/dist/755.js.map +1 -0
- package/dist/795.js +1 -0
- package/dist/795.js.map +1 -0
- package/dist/818.js +1 -0
- package/dist/818.js.map +1 -0
- package/dist/{942.js → 870.js} +1 -1
- package/dist/870.js.map +1 -0
- package/dist/909.js +2 -0
- package/dist/909.js.map +1 -0
- package/dist/926.js +1 -1
- package/dist/926.js.map +1 -1
- package/dist/929.js +1 -0
- package/dist/929.js.map +1 -0
- package/dist/960.js +1 -0
- package/dist/960.js.map +1 -0
- package/dist/kenyaemr-esm-morgue-app.js +1 -1
- package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +371 -127
- package/dist/kenyaemr-esm-morgue-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/autosuggest/autosuggest.component.tsx +162 -0
- package/src/autosuggest/autosuggest.scss +61 -0
- package/src/autosuggest/patient-search-info.component.tsx +75 -0
- package/src/autosuggest/patient-search-info.scss +62 -0
- package/src/autosuggest/search-empty-state.component.tsx +21 -0
- package/src/autosuggest/search-empty-state.scss +18 -0
- package/src/card/avail-compartment.compartment.tsx +40 -41
- package/src/card/compartment-view.compartment.tsx +37 -14
- package/src/card/compartment.scss +18 -13
- package/src/card/compartmentSharing.component.tsx +21 -0
- package/src/card/compartmentSharing.scss +24 -0
- package/src/card/empty-compartment.component.tsx +11 -7
- package/src/card/empty-compartment.scss +61 -0
- package/src/component/deceasedInfo/deceased-info.component.tsx +1 -1
- package/src/component/main.component.tsx +1 -7
- package/src/component/next-of-kin-details/nextOfKinDetails.component.tsx +50 -0
- package/src/component/next-of-kin-details/nextOfKinDetails.scss +37 -0
- package/src/config-schema.ts +30 -22
- package/src/extension/actionButton.component.tsx +74 -0
- package/src/extension/actionButton.scss +69 -0
- package/src/extension/deceasedInfoBanner.component.tsx +57 -0
- package/src/hook/useAdmitPatient.ts +285 -0
- package/src/hook/useDeceasedPatients.ts +12 -0
- package/src/hook/useDischargedPatient.ts +55 -0
- package/src/hook/useMorgue.resource.ts +11 -120
- package/src/hook/useMortuaryAdmissionLocation.ts +64 -0
- package/src/hook/usePersonAttributes.ts +65 -0
- package/src/index.ts +4 -0
- package/src/routes.json +24 -7
- package/src/tables/admitted-queue.component.tsx +17 -21
- package/src/tables/discharge-queue.component.tsx +33 -27
- package/src/tables/generic-table.component.tsx +44 -19
- package/src/tabs/tabs.component.tsx +36 -16
- package/src/tabs/tabs.scss +3 -186
- package/src/types/index.ts +291 -9
- package/src/utils/utils.ts +55 -4
- package/src/workspaces/admit-body.scss +46 -0
- package/src/workspaces/admit-body.workspace.tsx +79 -0
- package/src/workspaces/discharge-body.scss +2 -2
- package/src/workspaces/discharge-body.workspace.tsx +157 -101
- package/src/workspaces/patientAdditionalInfoForm.workspace.tsx +141 -218
- package/src/workspaces/swap-unit.scss +46 -0
- package/src/workspaces/swap-unit.workspace.tsx +168 -0
- package/translations/en.json +22 -7
- package/dist/340.js +0 -1
- package/dist/340.js.map +0 -1
- package/dist/38.js +0 -1
- package/dist/38.js.map +0 -1
- package/dist/485.js +0 -2
- package/dist/553.js +0 -1
- package/dist/553.js.map +0 -1
- package/dist/561.js.map +0 -1
- package/dist/592.js +0 -2
- package/dist/592.js.map +0 -1
- package/dist/759.js +0 -1
- package/dist/759.js.map +0 -1
- package/dist/942.js.map +0 -1
- package/dist/987.js +0 -2
- package/dist/987.js.map +0 -1
- package/src/component/deceasedInfo/deceased-header.component.tsx +0 -37
- package/src/tables/waiting-queue.component.tsx +0 -91
- /package/dist/{987.js.LICENSE.txt → 659.js.LICENSE.txt} +0 -0
- /package/dist/{485.js.LICENSE.txt → 675.js.LICENSE.txt} +0 -0
- /package/dist/{592.js.LICENSE.txt → 909.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Button } from '@carbon/react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { launchWorkspace, navigate } from '@openmrs/esm-framework';
|
|
4
|
+
import { Movement, Return, ShareKnowledge } from '@carbon/react/icons';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { useAdmissionLocation } from '../hook/useMortuaryAdmissionLocation';
|
|
7
|
+
import styles from './actionButton.scss';
|
|
8
|
+
|
|
9
|
+
interface ActionButtonProps {
|
|
10
|
+
patientUuid: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const ActionButton: React.FC<ActionButtonProps> = ({ patientUuid }) => {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const { admissionLocation, isLoading, error } = useAdmissionLocation();
|
|
16
|
+
|
|
17
|
+
const isPatientInAdmissionLocation = admissionLocation?.bedLayouts?.some((bed) =>
|
|
18
|
+
bed.patients.some((patient) => patient.uuid === patientUuid),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const bedId = admissionLocation?.bedLayouts?.find((bed) =>
|
|
22
|
+
bed.patients.some((patient) => patient.uuid === patientUuid),
|
|
23
|
+
)?.bedId;
|
|
24
|
+
|
|
25
|
+
const personUuid = admissionLocation?.bedLayouts
|
|
26
|
+
?.find((bed) => bed.patients.some((patient) => patient.uuid === patientUuid))
|
|
27
|
+
?.patients.find((patient) => patient.uuid === patientUuid)?.person?.uuid;
|
|
28
|
+
|
|
29
|
+
const handleNavigateToAllocationPage = () =>
|
|
30
|
+
navigate({
|
|
31
|
+
to: window.getOpenmrsSpaBase() + `home/morgue/allocation`,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const handleDischargeForm = (uuid: string, bedId: number) => {
|
|
35
|
+
launchWorkspace('discharge-body-form', {
|
|
36
|
+
workspaceTitle: t('dischargeForm', 'Discharge form'),
|
|
37
|
+
patientUuid: uuid,
|
|
38
|
+
bedId,
|
|
39
|
+
personUuid,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const handleSwapForm = (uuid: string, bedId: number) => {
|
|
44
|
+
launchWorkspace('swap-unit-form', {
|
|
45
|
+
workspaceTitle: t('swapCompartment', 'Swap compartment'),
|
|
46
|
+
patientUuid: uuid,
|
|
47
|
+
bedId,
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className={styles.actionButton}>
|
|
53
|
+
<Button kind="primary" size="sm" renderIcon={Return} onClick={handleNavigateToAllocationPage}>
|
|
54
|
+
{t('allocation', 'Allocation View')}
|
|
55
|
+
</Button>
|
|
56
|
+
{isPatientInAdmissionLocation && (
|
|
57
|
+
<>
|
|
58
|
+
<Button
|
|
59
|
+
kind="secondary"
|
|
60
|
+
size="sm"
|
|
61
|
+
renderIcon={ShareKnowledge}
|
|
62
|
+
onClick={() => handleSwapForm(patientUuid, bedId)}>
|
|
63
|
+
{t('swapCompartment', 'Swap compartment')}
|
|
64
|
+
</Button>
|
|
65
|
+
<Button kind="danger" size="sm" renderIcon={Movement} onClick={() => handleDischargeForm(patientUuid, bedId)}>
|
|
66
|
+
{t('discharge', 'Discharge body')}
|
|
67
|
+
</Button>
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default ActionButton;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@carbon/colors';
|
|
4
|
+
|
|
5
|
+
.actionButton {
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: layout.$spacing-03;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.metrics {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: row;
|
|
13
|
+
justify-content: flex-end;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: layout.$spacing-06;
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
padding: layout.$spacing-04 layout.$spacing-05 0 0;
|
|
18
|
+
width: auto;
|
|
19
|
+
height: auto;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.wrapMetrics {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
align-items: flex-start;
|
|
27
|
+
gap: layout.$spacing-03;
|
|
28
|
+
padding: 0;
|
|
29
|
+
min-width: layout.$spacing-12;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.metricLabel {
|
|
33
|
+
flex-grow: 0;
|
|
34
|
+
font-size: layout.$spacing-04;
|
|
35
|
+
line-height: 1.33;
|
|
36
|
+
letter-spacing: 0.32px;
|
|
37
|
+
text-align: left;
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
text-overflow: ellipsis;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.metricValue {
|
|
44
|
+
flex-grow: 0;
|
|
45
|
+
font-size: type.type-scale(2);
|
|
46
|
+
color: colors.$gray-60;
|
|
47
|
+
line-height: 1.4;
|
|
48
|
+
text-align: left;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
text-overflow: ellipsis;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.metricLocationDate {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
justify-content: flex-start;
|
|
58
|
+
align-items: flex-start;
|
|
59
|
+
gap: layout.$spacing-05;
|
|
60
|
+
width: auto;
|
|
61
|
+
max-width: 120px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.metricList {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-flow: row wrap;
|
|
67
|
+
padding-bottom: layout.$spacing-03;
|
|
68
|
+
margin: 0 layout.$spacing-05;
|
|
69
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Button } from '@carbon/react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { launchWorkspace, navigate, usePatient, useVisit } from '@openmrs/esm-framework';
|
|
4
|
+
import { Movement, Return, ShareKnowledge } from '@carbon/react/icons';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { useAdmissionLocation } from '../hook/useMortuaryAdmissionLocation';
|
|
7
|
+
import styles from './actionButton.scss';
|
|
8
|
+
import { convertDateToDays, formatDateTime } from '../utils/utils';
|
|
9
|
+
import { Console } from '@carbon/pictograms-react';
|
|
10
|
+
|
|
11
|
+
interface BannerInfoProps {
|
|
12
|
+
patientUuid: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const BannerInfo: React.FC<BannerInfoProps> = ({ patientUuid }) => {
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
const { admissionLocation, isLoading, error } = useAdmissionLocation();
|
|
18
|
+
const { patient } = usePatient(patientUuid);
|
|
19
|
+
const { currentVisit } = useVisit(patientUuid);
|
|
20
|
+
|
|
21
|
+
const bedNumber =
|
|
22
|
+
admissionLocation?.bedLayouts?.find((bed) => bed.patients.some((patient) => patient.uuid === patientUuid))
|
|
23
|
+
?.bedNumber || t('discharged', 'Discharged');
|
|
24
|
+
|
|
25
|
+
const timeAndDateOfDeath = patient?.deceasedDateTime;
|
|
26
|
+
|
|
27
|
+
const startDate = currentVisit?.startDatetime;
|
|
28
|
+
|
|
29
|
+
const lengthOfStay = `${convertDateToDays(startDate)} ${
|
|
30
|
+
convertDateToDays(startDate) === 1 ? t('day', 'Day') : t('days', 'Days')
|
|
31
|
+
}`;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={styles.metricList}>
|
|
35
|
+
<div className={styles.metrics}>
|
|
36
|
+
<div className={styles.wrapMetrics}>
|
|
37
|
+
<span className={styles.metricLabel}>{t('dateOfAdmission', 'Date of admission')}</span>
|
|
38
|
+
<span className={styles.metricValue}>{formatDateTime(startDate)}</span>
|
|
39
|
+
</div>
|
|
40
|
+
<div className={styles.wrapMetrics}>
|
|
41
|
+
<span className={styles.metricLabel}>{t('dateAndTimeofDeath', 'Date and time of death')}</span>{' '}
|
|
42
|
+
<span className={styles.metricValue}>{formatDateTime(timeAndDateOfDeath)}</span>
|
|
43
|
+
</div>
|
|
44
|
+
<div className={styles.wrapMetrics}>
|
|
45
|
+
<span className={styles.metricLabel}>{t('lengthofStay', 'Length of stay')}</span>
|
|
46
|
+
<span className={styles.metricValue}>{lengthOfStay}</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div className={styles.wrapMetrics}>
|
|
49
|
+
<span className={styles.metricLabel}>{t('compartment', 'Compartment')}</span>
|
|
50
|
+
<span className={styles.metricValue}>{bedNumber}</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default BannerInfo;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import {
|
|
2
|
+
closeWorkspace,
|
|
3
|
+
FetchResponse,
|
|
4
|
+
openmrsFetch,
|
|
5
|
+
restBaseUrl,
|
|
6
|
+
showSnackbar,
|
|
7
|
+
updateVisit,
|
|
8
|
+
useConfig,
|
|
9
|
+
useSession,
|
|
10
|
+
type Visit,
|
|
11
|
+
} from '@openmrs/esm-framework';
|
|
12
|
+
import dayjs from 'dayjs';
|
|
13
|
+
import { useCallback, useMemo } from 'react';
|
|
14
|
+
import useSWR from 'swr';
|
|
15
|
+
import useSWRImmutable from 'swr/immutable';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { ConfigObject } from '../config-schema';
|
|
18
|
+
import {
|
|
19
|
+
CurrentLocationEncounterResponse,
|
|
20
|
+
customRepProps,
|
|
21
|
+
EmrApiConfigurationResponse,
|
|
22
|
+
Encounter,
|
|
23
|
+
MappedVisitQueueEntry,
|
|
24
|
+
VisitQueueEntry,
|
|
25
|
+
} from '../types';
|
|
26
|
+
import { dischargeSchema, patientInfoSchema } from '../utils/utils';
|
|
27
|
+
import { useMortuaryLocation } from './useMortuaryAdmissionLocation';
|
|
28
|
+
import { removeQueuedPatient } from './useMorgue.resource';
|
|
29
|
+
const customRep = `custom:${customRepProps.map((prop) => prop.join(':')).join(',')}`;
|
|
30
|
+
|
|
31
|
+
export default function useEmrConfiguration() {
|
|
32
|
+
const swrData = useSWRImmutable<FetchResponse<EmrApiConfigurationResponse>>(
|
|
33
|
+
`${restBaseUrl}/emrapi/configuration?v=${customRep}`,
|
|
34
|
+
openmrsFetch,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const results = useMemo(
|
|
38
|
+
() => ({
|
|
39
|
+
emrConfiguration: swrData.data?.data,
|
|
40
|
+
isLoadingEmrConfiguration: swrData.isLoading,
|
|
41
|
+
mutateEmrConfiguration: swrData.mutate,
|
|
42
|
+
errorFetchingEmrConfiguration: swrData.error,
|
|
43
|
+
}),
|
|
44
|
+
[swrData],
|
|
45
|
+
);
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const useMortuaryOperation = () => {
|
|
50
|
+
const { location } = useMortuaryLocation();
|
|
51
|
+
const { currentProvider } = useSession();
|
|
52
|
+
const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
|
|
53
|
+
const {
|
|
54
|
+
visitPaymentMethodAttributeUuid,
|
|
55
|
+
tagNumberUuid,
|
|
56
|
+
obNumberUuid,
|
|
57
|
+
burialPermitNumberUuid,
|
|
58
|
+
policeNameUuid,
|
|
59
|
+
policeIDNumber,
|
|
60
|
+
dischargeAreaUuid,
|
|
61
|
+
} = useConfig<ConfigObject>();
|
|
62
|
+
|
|
63
|
+
const createMortuaryAdmissionEncounter = useCallback(
|
|
64
|
+
async (
|
|
65
|
+
patientUuid: string,
|
|
66
|
+
{
|
|
67
|
+
availableCompartment,
|
|
68
|
+
dateOfAdmission,
|
|
69
|
+
insuranceScheme,
|
|
70
|
+
dischargeArea,
|
|
71
|
+
obNumber,
|
|
72
|
+
paymentMethod,
|
|
73
|
+
period,
|
|
74
|
+
policeIDNo,
|
|
75
|
+
policeName,
|
|
76
|
+
tagNumber,
|
|
77
|
+
visitType,
|
|
78
|
+
}: z.infer<typeof patientInfoSchema>,
|
|
79
|
+
) => {
|
|
80
|
+
const obs = [];
|
|
81
|
+
if (tagNumber) {
|
|
82
|
+
obs.push({ concept: tagNumberUuid, value: tagNumber });
|
|
83
|
+
}
|
|
84
|
+
if (obNumber) {
|
|
85
|
+
obs.push({ concept: obNumberUuid, value: obNumber });
|
|
86
|
+
}
|
|
87
|
+
if (policeName) {
|
|
88
|
+
obs.push({ concept: policeNameUuid, value: policeName });
|
|
89
|
+
}
|
|
90
|
+
if (policeIDNo) {
|
|
91
|
+
obs.push({ concept: policeIDNumber, value: policeIDNo });
|
|
92
|
+
}
|
|
93
|
+
if (dischargeArea) {
|
|
94
|
+
obs.push({ concept: dischargeAreaUuid, value: dischargeArea });
|
|
95
|
+
}
|
|
96
|
+
const encounterPayload = {
|
|
97
|
+
visit: {
|
|
98
|
+
patient: patientUuid,
|
|
99
|
+
startDatetime: dayjs(dateOfAdmission).toISOString(),
|
|
100
|
+
visitType: visitType,
|
|
101
|
+
location: location?.uuid,
|
|
102
|
+
attributes: [
|
|
103
|
+
{
|
|
104
|
+
attributeType: visitPaymentMethodAttributeUuid,
|
|
105
|
+
value: paymentMethod,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
patient: patientUuid,
|
|
110
|
+
encounterType: emrConfiguration?.admissionEncounterType?.uuid,
|
|
111
|
+
location: location?.uuid,
|
|
112
|
+
encounterProviders: [
|
|
113
|
+
{
|
|
114
|
+
provider: currentProvider?.uuid,
|
|
115
|
+
encounterRole: emrConfiguration?.clinicianEncounterRole?.uuid,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
obs: obs.length > 0 ? obs : undefined,
|
|
119
|
+
};
|
|
120
|
+
return openmrsFetch<Encounter>(`${restBaseUrl}/encounter`, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: {
|
|
123
|
+
'content-type': 'application/json',
|
|
124
|
+
},
|
|
125
|
+
body: encounterPayload,
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
[
|
|
129
|
+
currentProvider?.uuid,
|
|
130
|
+
dischargeAreaUuid,
|
|
131
|
+
emrConfiguration?.admissionEncounterType?.uuid,
|
|
132
|
+
emrConfiguration?.clinicianEncounterRole?.uuid,
|
|
133
|
+
location?.uuid,
|
|
134
|
+
obNumberUuid,
|
|
135
|
+
policeIDNumber,
|
|
136
|
+
policeNameUuid,
|
|
137
|
+
tagNumberUuid,
|
|
138
|
+
visitPaymentMethodAttributeUuid,
|
|
139
|
+
],
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const assignDeceasedToCompartment = useCallback(
|
|
143
|
+
async (patientUuid: string, bedId: number, encounterUuid: string) =>
|
|
144
|
+
openmrsFetch(`${restBaseUrl}/beds/${bedId}`, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: {
|
|
147
|
+
'content-type': 'application/json',
|
|
148
|
+
},
|
|
149
|
+
body: {
|
|
150
|
+
patientUuid,
|
|
151
|
+
encounterUuid,
|
|
152
|
+
},
|
|
153
|
+
}),
|
|
154
|
+
[],
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const admitBody = useCallback(
|
|
158
|
+
async (patientUuid: string, data: z.infer<typeof patientInfoSchema>) => {
|
|
159
|
+
const admissionEncounter = await createMortuaryAdmissionEncounter(patientUuid, data);
|
|
160
|
+
const compartment = await assignDeceasedToCompartment(
|
|
161
|
+
patientUuid,
|
|
162
|
+
data.availableCompartment,
|
|
163
|
+
admissionEncounter.data.uuid,
|
|
164
|
+
);
|
|
165
|
+
return { admissionEncounter, compartment };
|
|
166
|
+
},
|
|
167
|
+
[assignDeceasedToCompartment, createMortuaryAdmissionEncounter],
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const createDischargeMortuaryEncounter = useCallback(
|
|
171
|
+
async (visit: Visit, data: z.infer<typeof dischargeSchema>) => {
|
|
172
|
+
const obs = [];
|
|
173
|
+
if (data.burialPermitNumber) {
|
|
174
|
+
obs.push({ concept: burialPermitNumberUuid, value: data.burialPermitNumber });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const encounterPayload = {
|
|
178
|
+
encounterDatetime: data?.dateOfDischarge,
|
|
179
|
+
patient: visit?.patient?.uuid,
|
|
180
|
+
encounterType: emrConfiguration?.exitFromInpatientEncounterType,
|
|
181
|
+
location: visit?.location?.uuid,
|
|
182
|
+
encounterProviders: [
|
|
183
|
+
{
|
|
184
|
+
provider: currentProvider?.uuid,
|
|
185
|
+
encounterRole: emrConfiguration?.clinicianEncounterRole?.uuid,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
visit: visit?.uuid,
|
|
189
|
+
obs: obs.length > 0 ? obs : undefined,
|
|
190
|
+
};
|
|
191
|
+
return openmrsFetch<Encounter>(`${restBaseUrl}/encounter`, {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
headers: {
|
|
194
|
+
'content-type': 'application/json',
|
|
195
|
+
},
|
|
196
|
+
body: encounterPayload,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
[
|
|
200
|
+
burialPermitNumberUuid,
|
|
201
|
+
currentProvider?.uuid,
|
|
202
|
+
emrConfiguration?.clinicianEncounterRole?.uuid,
|
|
203
|
+
emrConfiguration?.exitFromInpatientEncounterType,
|
|
204
|
+
],
|
|
205
|
+
);
|
|
206
|
+
const removeDeceasedFromCompartment = useCallback(
|
|
207
|
+
async (patientUuid: string, bedId: number) =>
|
|
208
|
+
openmrsFetch(`${restBaseUrl}/beds/${bedId}?patientUuid=${patientUuid}`, {
|
|
209
|
+
method: 'DELETE',
|
|
210
|
+
}),
|
|
211
|
+
[],
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const endCurrentVisit = useCallback(
|
|
215
|
+
async (currentVisit: Visit, queueEntry: MappedVisitQueueEntry, data: z.infer<typeof dischargeSchema>) => {
|
|
216
|
+
const abortController = new AbortController();
|
|
217
|
+
const response = await updateVisit(
|
|
218
|
+
currentVisit.uuid,
|
|
219
|
+
{
|
|
220
|
+
stopDatetime: new Date(),
|
|
221
|
+
},
|
|
222
|
+
abortController,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
if (queueEntry) {
|
|
226
|
+
removeQueuedPatient(
|
|
227
|
+
queueEntry.queue.uuid,
|
|
228
|
+
queueEntry.queueEntryUuid,
|
|
229
|
+
abortController,
|
|
230
|
+
response?.data?.stopDatetime,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
[],
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const dischargeBody = useCallback(
|
|
238
|
+
async (visit: Visit, queueEntry: MappedVisitQueueEntry, bedId: number, data: z.infer<typeof dischargeSchema>) => {
|
|
239
|
+
const dischargeEncounter = await createDischargeMortuaryEncounter(visit, data);
|
|
240
|
+
const compartment = await removeDeceasedFromCompartment(visit?.patient?.uuid, bedId);
|
|
241
|
+
await endCurrentVisit(visit, queueEntry, data);
|
|
242
|
+
return { dischargeEncounter, compartment };
|
|
243
|
+
},
|
|
244
|
+
[createDischargeMortuaryEncounter, endCurrentVisit, removeDeceasedFromCompartment],
|
|
245
|
+
);
|
|
246
|
+
const createEncounterForCompartmentSwap = useCallback(
|
|
247
|
+
async (patientUuid: string, visitUuid: string) =>
|
|
248
|
+
openmrsFetch<Encounter>(`${restBaseUrl}/encounter`, {
|
|
249
|
+
method: 'POST',
|
|
250
|
+
headers: {
|
|
251
|
+
'content-type': 'application/json',
|
|
252
|
+
},
|
|
253
|
+
body: {
|
|
254
|
+
encounterDatetime: new Date(),
|
|
255
|
+
patient: patientUuid,
|
|
256
|
+
encounterType: emrConfiguration?.bedAssignmentEncounterType,
|
|
257
|
+
location: location?.uuid,
|
|
258
|
+
encounterProviders: [
|
|
259
|
+
{
|
|
260
|
+
provider: currentProvider?.uuid,
|
|
261
|
+
encounterRole: emrConfiguration?.clinicianEncounterRole?.uuid,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
visit: visitUuid,
|
|
265
|
+
obs: [],
|
|
266
|
+
},
|
|
267
|
+
}),
|
|
268
|
+
[
|
|
269
|
+
currentProvider?.uuid,
|
|
270
|
+
emrConfiguration?.bedAssignmentEncounterType,
|
|
271
|
+
emrConfiguration?.clinicianEncounterRole?.uuid,
|
|
272
|
+
location?.uuid,
|
|
273
|
+
],
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
admitBody,
|
|
278
|
+
createEncounterForCompartmentSwap,
|
|
279
|
+
assignDeceasedToCompartment,
|
|
280
|
+
removeDeceasedFromCompartment,
|
|
281
|
+
dischargeBody,
|
|
282
|
+
isLoadingEmrConfiguration,
|
|
283
|
+
errorFetchingEmrConfiguration,
|
|
284
|
+
};
|
|
285
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import { PaginatedResponse } from '../types';
|
|
3
|
+
export async function fetchDeceasedPatient(query: string, abortController: AbortController) {
|
|
4
|
+
const customRepresentation =
|
|
5
|
+
'custom:(uuid,display,identifiers:(identifier,uuid,preferred,location:(uuid,name)),person:(uuid,display,gender,birthdate,dead,age,deathDate,causeOfDeath:(uuid,display),preferredAddress:(uuid,stateProvince,countyDistrict,address4)))';
|
|
6
|
+
const url = `${restBaseUrl}/morgue/patient?v=${customRepresentation}&dead=true&name=${query}`;
|
|
7
|
+
|
|
8
|
+
const resp = await openmrsFetch<{ results: Array<PaginatedResponse> }>(url, {
|
|
9
|
+
signal: abortController.signal,
|
|
10
|
+
});
|
|
11
|
+
return resp?.data?.results;
|
|
12
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { FetchResponse, openmrsFetch, restBaseUrl, useFhirFetchAll } from '@openmrs/esm-framework';
|
|
2
|
+
import { type Patient, type FHIREncounter } from '../types';
|
|
3
|
+
import { useAdmissionLocation } from './useMortuaryAdmissionLocation';
|
|
4
|
+
import useSWR from 'swr';
|
|
5
|
+
|
|
6
|
+
export const useDischargedPatient = (dischargeEncounterTypeUuid: string) => {
|
|
7
|
+
const url = dischargeEncounterTypeUuid
|
|
8
|
+
? `/ws/fhir2/R4/Encounter?_format=json&type=${dischargeEncounterTypeUuid}`
|
|
9
|
+
: null;
|
|
10
|
+
const { data, error, isLoading } = useFhirFetchAll<FHIREncounter>(url);
|
|
11
|
+
|
|
12
|
+
const dischargedPatientUuids = data
|
|
13
|
+
?.map((encounter) => {
|
|
14
|
+
const reference = encounter.subject?.reference;
|
|
15
|
+
if (reference && reference.startsWith('Patient/')) {
|
|
16
|
+
return reference.split('/')[1];
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
})
|
|
20
|
+
.filter((uuid) => uuid);
|
|
21
|
+
|
|
22
|
+
const { admissionLocation } = useAdmissionLocation();
|
|
23
|
+
|
|
24
|
+
const admittedPatientUuids = admissionLocation?.bedLayouts
|
|
25
|
+
?.flatMap((bed) => bed.patients?.map((patient) => patient.uuid))
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
|
|
28
|
+
const filteredDischargedPatientUuids = dischargedPatientUuids?.filter(
|
|
29
|
+
(uuid) => !admittedPatientUuids?.includes(uuid),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const uniqueDischargedPatientUuids = [...new Set(filteredDischargedPatientUuids)];
|
|
33
|
+
|
|
34
|
+
return { dischargedPatientUuids: uniqueDischargedPatientUuids, error, isLoading };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const usePatients = (uuids: string[]) => {
|
|
38
|
+
const customRepresentation =
|
|
39
|
+
'custom:(uuid,display,identifiers:(uuid,display),person:(uuid,display,gender,birthdate,dead,age,deathDate,causeOfDeath:(uuid,display),attributes:(uuid,display,value,attributeType:(uuid,))))';
|
|
40
|
+
const urls = uuids.map((uuid) => `${restBaseUrl}/patient/${uuid}?v=${customRepresentation}`);
|
|
41
|
+
|
|
42
|
+
const { data, error, isLoading } = useSWR<FetchResponse<Patient>[]>(urls, (urls) =>
|
|
43
|
+
Promise.all(urls.map((url) => openmrsFetch<Patient>(url))),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const deceasedPatients = data?.map((response) => response.data)?.filter((patient) => patient?.person?.dead === true);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
isLoading,
|
|
50
|
+
error,
|
|
51
|
+
patients: deceasedPatients,
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default usePatients;
|