@kenyaemr/esm-ward-app 8.5.1-pre.37 → 8.5.1-pre.41
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 +12 -12
- package/dist/1160.js +1 -0
- package/dist/1160.js.map +1 -0
- package/dist/1917.js +1 -1
- package/dist/1917.js.map +1 -1
- package/dist/2059.js +1 -0
- package/dist/2059.js.map +1 -0
- package/dist/3161.js +1 -1
- package/dist/3161.js.map +1 -1
- package/dist/4224.js +2 -0
- package/dist/4224.js.map +1 -0
- package/dist/6012.js +1 -1
- package/dist/6012.js.map +1 -1
- package/dist/6871.js +1 -1
- package/dist/6871.js.map +1 -1
- package/dist/7059.js +2 -0
- package/dist/7059.js.map +1 -0
- package/dist/7661.js +1 -1
- package/dist/7661.js.map +1 -1
- package/dist/8205.js +1 -1
- package/dist/8205.js.map +1 -1
- package/dist/8308.js +1 -0
- package/dist/8308.js.map +1 -0
- package/dist/9113.js +1 -0
- package/dist/{9880.js.map → 9113.js.map} +1 -1
- package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +163 -112
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -1
- package/src/config-schema.ts +6 -0
- package/src/discharge-printouts/discharge-printout.preview-modal.tsx +40 -0
- package/src/discharge-printouts/discharge-printout.resource.ts +215 -0
- package/src/discharge-printouts/discharge-printouts.scss +125 -0
- package/src/discharge-printouts/discharge-summary.tsx +171 -0
- package/src/discharge-printouts/field-input.tsx +21 -0
- package/src/discharge-printouts/gate-pass-printout.tsx +125 -0
- package/src/hooks/useIpdDischargeEncounter.ts +10 -8
- package/src/index.ts +4 -0
- package/src/routes.json +4 -0
- package/src/ward-patients/admitted-patients.tsx +2 -2
- package/src/ward-patients/discharge-in-patients.tsx +22 -17
- package/src/ward-patients/discharge-patients.tsx +23 -3
- package/src/ward-patients/patient-cells.tsx +4 -10
- package/src/ward-workspace/admit-patient-form-workspace/patient-admission.resources.ts +19 -1
- package/src/ward-workspace/kenya-emr-patient-discharge/patient-discharge.resource.tsx +2 -1
- package/src/ward-workspace/patient-transfer-bed-swap/patient-admit-or-transfer-request-form.component.tsx +1 -1
- package/dist/1352.js +0 -2
- package/dist/1352.js.map +0 -1
- package/dist/3423.js +0 -1
- package/dist/3423.js.map +0 -1
- package/dist/4701.js +0 -2
- package/dist/4701.js.map +0 -1
- package/dist/9880.js +0 -1
- /package/dist/{1352.js.LICENSE.txt → 4224.js.LICENSE.txt} +0 -0
- /package/dist/{4701.js.LICENSE.txt → 7059.js.LICENSE.txt} +0 -0
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.2.0","emrapi":"^2.0.0 || 2.0.0-SNAPSHOT"},"optionalBackendDependencies":{"bedmanagement":{"version":"^6.0.0 || 6.0.0-SNAPSHOT","feature":{"flagName":"bedmanagement-module","label":"Ward App Patient Service","description":"This module, if installed, provides services for managing patients admitted to the ward."}}},"extensions":[{"name":"ward-dashboard-link","component":"wardDashboardLink","slot":"homepage-dashboard-slot","meta":{"name":"ward","slot":"ward-dashboard-slot","title":"Wards"}},{"component":"root","name":"ward-dashboard","slot":"ward-dashboard-slot"},{"component":"wardView","name":"ward-view","slot":"ward-view-slot"},{"component":"wardPatientActionButtonExtension","name":"ward-patient-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"wardPatientNotesActionButtonExtension","name":"ward-inpatient-notes-form-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"coloredObsTagCardRowExtension","name":"colored-obs-tags-card-row","slot":"ward-patient-card-slot"},{"name":"transfer-swap-patient-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientTransferAndSwapWorkspaceSiderailIcon"},{"name":"patient-discharge-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientDischargeWorkspaceSideRailIcon"},{"name":"clinical-forms-workspace-siderail-button","component":"clinicalFormWorkspaceSideRailIcon","slot":"action-menu-ward-patient-items-slot"},{"name":"admission-requests-workspace-siderail-button","component":"admissionRequestsWorkspaceSideRailIcon","slot":"action-menu-ward-patient-admission-requests-items-slot"},{"name":"create-admission-request-workspace-siderail-button","component":"createAdmissionEncounterWorkspaceSideRailIcon","slot":"action-menu-ward-patient-admission-requests-items-slot"},{"component":"defaultWardView","name":"default-ward","slot":"default-ward"},{"component":"maternalWardView","name":"maternal-ward","slot":"maternal-ward"},{"component":"wardPatientWorkspaceBanner","name":"ward-patient-workspace-banner","slot":"ward-workspace-patient-banner-slot"}],"workspaces":[{"name":"admission-requests-workspace","component":"admissionRequestWorkspace","title":"admissionRequests","type":"pending-admission-requests","width":"wider"},{"name":"create-admission-encounter-workspace","component":"createAdmissionEncounterWorkspace","title":"admitPatient","type":"patient-search-workspace","width":"wider"},{"name":"ward-patient-notes-workspace","component":"wardPatientNotesWorkspace","type":"ward-patient-notes","title":"inpatientNotesWorkspaceTitle"},{"name":"admit-patient-form-workspace","component":"admitPatientFormWorkspace","title":"admissionRequests","type":"admission-requests"},{"name":"ward-patient-workspace","component":"wardPatientWorkspace","type":"ward","title":"Ward patient","width":"extra-wide"},{"name":"patient-transfer-swap-workspace","component":"patientTransferAndSwapWorkspace","title":"transfers","type":"transfer-swap-bed-form"},{"name":"patient-transfer-request-workspace","component":"patientTransferRequestWorkspace","title":"transferRequest","type":"transfer-request-form"},{"name":"patient-discharge-workspace","component":"patientDischargeWorkspace","title":"discharge","width":"extra-wide","canMaximize":true,"type":"ward-patient-discharge"},{"name":"ward-patient-clinical-forms-workspace","component":"patientClinicalFormsWorkspace","title":"clinicalForms","type":"ward-patient-clinical-forms","width":"wider"},{"name":"cancel-admission-request-workspace","component":"cancelAdmissionRequestWorkspace","title":"cancelAdmissionRequest","type":"cancel-admission-request"}],"workspaceGroups":[{"name":"ward-patient","members":["ward-patient-workspace","ward-patient-notes-workspace","patient-transfer-swap-workspace","patient-discharge-workspace","ward-patient-clinical-forms-workspace","add-drug-order","order-basket","add-lab-order"]},{"name":"ward-patient-admission-requests","members":["admission-requests-workspace","create-admission-encounter-workspace"]}],"version":"8.5.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.2.0","emrapi":"^2.0.0 || 2.0.0-SNAPSHOT"},"optionalBackendDependencies":{"bedmanagement":{"version":"^6.0.0 || 6.0.0-SNAPSHOT","feature":{"flagName":"bedmanagement-module","label":"Ward App Patient Service","description":"This module, if installed, provides services for managing patients admitted to the ward."}}},"extensions":[{"name":"ward-dashboard-link","component":"wardDashboardLink","slot":"homepage-dashboard-slot","meta":{"name":"ward","slot":"ward-dashboard-slot","title":"Wards"}},{"component":"root","name":"ward-dashboard","slot":"ward-dashboard-slot"},{"component":"wardView","name":"ward-view","slot":"ward-view-slot"},{"component":"wardPatientActionButtonExtension","name":"ward-patient-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"wardPatientNotesActionButtonExtension","name":"ward-inpatient-notes-form-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"coloredObsTagCardRowExtension","name":"colored-obs-tags-card-row","slot":"ward-patient-card-slot"},{"name":"transfer-swap-patient-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientTransferAndSwapWorkspaceSiderailIcon"},{"name":"patient-discharge-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientDischargeWorkspaceSideRailIcon"},{"name":"clinical-forms-workspace-siderail-button","component":"clinicalFormWorkspaceSideRailIcon","slot":"action-menu-ward-patient-items-slot"},{"name":"admission-requests-workspace-siderail-button","component":"admissionRequestsWorkspaceSideRailIcon","slot":"action-menu-ward-patient-admission-requests-items-slot"},{"name":"create-admission-request-workspace-siderail-button","component":"createAdmissionEncounterWorkspaceSideRailIcon","slot":"action-menu-ward-patient-admission-requests-items-slot"},{"component":"defaultWardView","name":"default-ward","slot":"default-ward"},{"component":"maternalWardView","name":"maternal-ward","slot":"maternal-ward"},{"component":"patientDischargeDocumentPreview","name":"patient-discharge-document-preview-modal"},{"component":"wardPatientWorkspaceBanner","name":"ward-patient-workspace-banner","slot":"ward-workspace-patient-banner-slot"}],"workspaces":[{"name":"admission-requests-workspace","component":"admissionRequestWorkspace","title":"admissionRequests","type":"pending-admission-requests","width":"wider"},{"name":"create-admission-encounter-workspace","component":"createAdmissionEncounterWorkspace","title":"admitPatient","type":"patient-search-workspace","width":"wider"},{"name":"ward-patient-notes-workspace","component":"wardPatientNotesWorkspace","type":"ward-patient-notes","title":"inpatientNotesWorkspaceTitle"},{"name":"admit-patient-form-workspace","component":"admitPatientFormWorkspace","title":"admissionRequests","type":"admission-requests"},{"name":"ward-patient-workspace","component":"wardPatientWorkspace","type":"ward","title":"Ward patient","width":"extra-wide"},{"name":"patient-transfer-swap-workspace","component":"patientTransferAndSwapWorkspace","title":"transfers","type":"transfer-swap-bed-form"},{"name":"patient-transfer-request-workspace","component":"patientTransferRequestWorkspace","title":"transferRequest","type":"transfer-request-form"},{"name":"patient-discharge-workspace","component":"patientDischargeWorkspace","title":"discharge","width":"extra-wide","canMaximize":true,"type":"ward-patient-discharge"},{"name":"ward-patient-clinical-forms-workspace","component":"patientClinicalFormsWorkspace","title":"clinicalForms","type":"ward-patient-clinical-forms","width":"wider"},{"name":"cancel-admission-request-workspace","component":"cancelAdmissionRequestWorkspace","title":"cancelAdmissionRequest","type":"cancel-admission-request"}],"workspaceGroups":[{"name":"ward-patient","members":["ward-patient-workspace","ward-patient-notes-workspace","patient-transfer-swap-workspace","patient-discharge-workspace","ward-patient-clinical-forms-workspace","add-drug-order","order-basket","add-lab-order"]},{"name":"ward-patient-admission-requests","members":["admission-requests-workspace","create-admission-encounter-workspace"]}],"version":"8.5.1-pre.41"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenyaemr/esm-ward-app",
|
|
3
|
-
"version": "8.5.1-pre.
|
|
3
|
+
"version": "8.5.1-pre.41",
|
|
4
4
|
"description": "Ward and bed management module for O3",
|
|
5
5
|
"browser": "dist/kenyaemr-esm-ward-app.js",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"classnames": "^2.3.2",
|
|
43
43
|
"lodash-es": "^4.17.15",
|
|
44
44
|
"react-hook-form": "^7.54.0",
|
|
45
|
+
"react-to-print": "^2.14.13",
|
|
45
46
|
"zod": "3.24.1"
|
|
46
47
|
},
|
|
47
48
|
"peerDependencies": {
|
package/src/config-schema.ts
CHANGED
|
@@ -350,6 +350,11 @@ export const configSchema: ConfigSchema = {
|
|
|
350
350
|
_type: Type.UUID,
|
|
351
351
|
_default: '37ce491f-b2dd-4433-b203-efebb8ba1469',
|
|
352
352
|
},
|
|
353
|
+
drugOrderEncounterType: {
|
|
354
|
+
_description: 'Drug Order encounter type Uuid',
|
|
355
|
+
_type: Type.UUID,
|
|
356
|
+
_default: '7df67b83-1b84-4fe2-b1b7-794b4e9bfcc3',
|
|
357
|
+
},
|
|
353
358
|
};
|
|
354
359
|
|
|
355
360
|
export interface WardConfigObject {
|
|
@@ -387,6 +392,7 @@ export interface WardConfigObject {
|
|
|
387
392
|
inpatientAdmissionEncounterProviderRole: string;
|
|
388
393
|
mortuaryAdmissionLoctionTagUuid: string;
|
|
389
394
|
dailyBedFeeBillableService: string;
|
|
395
|
+
drugOrderEncounterType: string;
|
|
390
396
|
}
|
|
391
397
|
|
|
392
398
|
export interface PendingItemsElementConfig {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ModalHeader , ModalBody , ModalFooter , ButtonSet , Button } from '@carbon/react';
|
|
2
|
+
import React, { type FC, type ReactNode, useRef } from 'react';
|
|
3
|
+
import styles from './discharge-printouts.scss';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { useReactToPrint } from 'react-to-print';
|
|
6
|
+
|
|
7
|
+
type DischargePrintoutPreviewModalProps = {
|
|
8
|
+
printout: ReactNode;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
};
|
|
11
|
+
const DischargePrintoutPreviewModal: FC<DischargePrintoutPreviewModalProps> = ({ onClose, printout }) => {
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
14
|
+
|
|
15
|
+
const handlePrint = useReactToPrint({
|
|
16
|
+
content: () => ref.current,
|
|
17
|
+
});
|
|
18
|
+
return (
|
|
19
|
+
<React.Fragment>
|
|
20
|
+
<ModalHeader className={styles.sectionHeader} closeModal={onClose}>
|
|
21
|
+
{t('printPreview', 'Print Preview')}
|
|
22
|
+
</ModalHeader>
|
|
23
|
+
<ModalBody>
|
|
24
|
+
<div ref={ref}>{printout}</div>
|
|
25
|
+
</ModalBody>
|
|
26
|
+
<ModalFooter>
|
|
27
|
+
<ButtonSet className={styles.buttonSet}>
|
|
28
|
+
<Button kind="secondary" onClick={onClose} className={styles.button}>
|
|
29
|
+
{t('cancel', 'Cancel')}
|
|
30
|
+
</Button>
|
|
31
|
+
<Button kind="primary" onClick={handlePrint} className={styles.button}>
|
|
32
|
+
{t('print', 'Print')}
|
|
33
|
+
</Button>
|
|
34
|
+
</ButtonSet>
|
|
35
|
+
</ModalFooter>
|
|
36
|
+
</React.Fragment>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default DischargePrintoutPreviewModal;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FetchResponse,
|
|
3
|
+
fhirBaseUrl,
|
|
4
|
+
openmrsFetch,
|
|
5
|
+
restBaseUrl,
|
|
6
|
+
useConfig,
|
|
7
|
+
type Visit,
|
|
8
|
+
} from '@openmrs/esm-framework';
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
import useSWR from 'swr';
|
|
11
|
+
import { useEncounterDetails } from '../hooks/useIpdDischargeEncounter';
|
|
12
|
+
import { type WardConfigObject } from '../config-schema';
|
|
13
|
+
import { type Order } from '@openmrs/esm-patient-common-lib';
|
|
14
|
+
export const DATE_FORMART = 'DD/MM/YYYY';
|
|
15
|
+
export const TIME_FORMART = 'hh:mm A';
|
|
16
|
+
|
|
17
|
+
export const usePatientDiagnosis = (encounterUuid: string) => {
|
|
18
|
+
const customRepresentation =
|
|
19
|
+
'custom:(uuid,display,visit:(uuid,encounters:(uuid,diagnoses:(uuid,display,certainty,diagnosis:(coded:(uuid,display))))))';
|
|
20
|
+
const url = `${restBaseUrl}/encounter/${encounterUuid}?v=${customRepresentation}`;
|
|
21
|
+
|
|
22
|
+
const { data, error, isLoading } = useSWR<FetchResponse<{ visit: Visit }>>(url, openmrsFetch);
|
|
23
|
+
|
|
24
|
+
const diagnoses = useMemo(() => {
|
|
25
|
+
return (
|
|
26
|
+
data?.data?.visit?.encounters?.flatMap(
|
|
27
|
+
(encounter) =>
|
|
28
|
+
encounter.diagnoses.map((diagnosis) => ({
|
|
29
|
+
id: diagnosis.diagnosis.coded.uuid,
|
|
30
|
+
text: diagnosis.display,
|
|
31
|
+
certainty: diagnosis.certainty,
|
|
32
|
+
})) || [],
|
|
33
|
+
) || []
|
|
34
|
+
);
|
|
35
|
+
}, [data]);
|
|
36
|
+
const display = useMemo(() => {
|
|
37
|
+
if (diagnoses?.length) return diagnoses.map((d) => d.text).join(', ');
|
|
38
|
+
return null;
|
|
39
|
+
}, [diagnoses]);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
error,
|
|
43
|
+
isLoading,
|
|
44
|
+
diagnoses: (diagnoses ?? []) as Array<{ id: string; text: string; certainty: string }>,
|
|
45
|
+
display,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export interface AllergyIntoleranceResponse {
|
|
50
|
+
resourceType: string;
|
|
51
|
+
id: string;
|
|
52
|
+
meta: {
|
|
53
|
+
lastUpdated: string;
|
|
54
|
+
};
|
|
55
|
+
type: string;
|
|
56
|
+
total: number;
|
|
57
|
+
entry: Array<{
|
|
58
|
+
resource: AllergyIntolerance;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface AllergyIntolerance {
|
|
63
|
+
resourceType: string;
|
|
64
|
+
id: string;
|
|
65
|
+
meta: {
|
|
66
|
+
lastUpdated: string;
|
|
67
|
+
};
|
|
68
|
+
clinicalStatus: {
|
|
69
|
+
coding: [
|
|
70
|
+
{
|
|
71
|
+
system: string;
|
|
72
|
+
code: string;
|
|
73
|
+
display: string;
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
text: string;
|
|
77
|
+
};
|
|
78
|
+
verificationStatus: {
|
|
79
|
+
coding: [
|
|
80
|
+
{
|
|
81
|
+
system: string;
|
|
82
|
+
code: string;
|
|
83
|
+
display: string;
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
text: string;
|
|
87
|
+
};
|
|
88
|
+
type: string;
|
|
89
|
+
category: Array<string>;
|
|
90
|
+
criticality: string;
|
|
91
|
+
code: {
|
|
92
|
+
coding: [
|
|
93
|
+
{
|
|
94
|
+
code: string;
|
|
95
|
+
display: string;
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
text: string;
|
|
99
|
+
};
|
|
100
|
+
patient: {
|
|
101
|
+
reference: string;
|
|
102
|
+
type: string;
|
|
103
|
+
display: string;
|
|
104
|
+
};
|
|
105
|
+
recordedDate: string;
|
|
106
|
+
recorder: {
|
|
107
|
+
reference: string;
|
|
108
|
+
type: string;
|
|
109
|
+
display: string;
|
|
110
|
+
};
|
|
111
|
+
reaction: [
|
|
112
|
+
{
|
|
113
|
+
substance: {
|
|
114
|
+
coding: [
|
|
115
|
+
{
|
|
116
|
+
code: string;
|
|
117
|
+
display: string;
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
text: string;
|
|
121
|
+
};
|
|
122
|
+
manifestation: [
|
|
123
|
+
{
|
|
124
|
+
coding: [
|
|
125
|
+
{
|
|
126
|
+
code: string;
|
|
127
|
+
display: string;
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
text: string;
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
severity: string;
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface Coding {
|
|
139
|
+
system?: string;
|
|
140
|
+
code: string;
|
|
141
|
+
display?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function getConceptCoding(codings: Coding[]): Coding {
|
|
145
|
+
return codings ? codings.find((c) => !('system' in c) || c.system === undefined) : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function getConceptCodingDisplay(codings: Coding[]): string {
|
|
149
|
+
return getConceptCoding(codings)?.display;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function usePatientAllergies(patientUuid: string) {
|
|
153
|
+
const { data, error, isLoading } = useSWR<{ data: AllergyIntoleranceResponse }, Error>(
|
|
154
|
+
`${fhirBaseUrl}/AllergyIntolerance?patient=${patientUuid}`,
|
|
155
|
+
openmrsFetch,
|
|
156
|
+
);
|
|
157
|
+
const allergies: Array<AllergyIntolerance> = useMemo(() => {
|
|
158
|
+
const _allergies: Array<AllergyIntolerance> = [];
|
|
159
|
+
if (data) {
|
|
160
|
+
const entries = data?.data.entry;
|
|
161
|
+
entries?.map((allergy) => {
|
|
162
|
+
return _allergies.push(allergy.resource);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return _allergies;
|
|
166
|
+
}, [data]);
|
|
167
|
+
|
|
168
|
+
const display = useMemo(() => {
|
|
169
|
+
if (allergies?.length) return allergies.map((allergy) => getConceptCodingDisplay(allergy.code.coding)).join(', ');
|
|
170
|
+
return null;
|
|
171
|
+
}, [allergies]);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
allergies,
|
|
175
|
+
totalAllergies: data?.data.total,
|
|
176
|
+
error,
|
|
177
|
+
isLoading,
|
|
178
|
+
display,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function usePatientOrders(dischargeEncounterUuId: string) {
|
|
183
|
+
const rep =
|
|
184
|
+
'custom:(uuid,display,location:(display),encounterDatetime,visit:(uuid,display,encounters:(uuid,display,encounterType:(uuid,display),encounterDatetime,orders,obs)))';
|
|
185
|
+
const { encounter, error, isLoading } = useEncounterDetails(dischargeEncounterUuId, rep);
|
|
186
|
+
const { drugOrderEncounterType } = useConfig<WardConfigObject>();
|
|
187
|
+
const orderEncounters = useMemo(() => {
|
|
188
|
+
const encounters = (encounter?.visit?.encounters ?? []).filter(
|
|
189
|
+
(enc) => enc.encounterType.uuid === drugOrderEncounterType,
|
|
190
|
+
);
|
|
191
|
+
return encounters;
|
|
192
|
+
}, [encounter, drugOrderEncounterType]);
|
|
193
|
+
const { drugorder, testorder } = useMemo<{ drugorder: Array<Order>; testorder: Array<Order> }>(
|
|
194
|
+
() =>
|
|
195
|
+
orderEncounters.reduce(
|
|
196
|
+
(prev, curr) => {
|
|
197
|
+
if (curr.orders?.length) {
|
|
198
|
+
prev['drugorder'].push(...curr.orders.filter((order) => order.type === 'drugorder'));
|
|
199
|
+
prev['testorder'].push(...curr.orders.filter((order) => order.type === 'testorder'));
|
|
200
|
+
}
|
|
201
|
+
return prev;
|
|
202
|
+
},
|
|
203
|
+
{ drugorder: [], testorder: [] },
|
|
204
|
+
),
|
|
205
|
+
[orderEncounters],
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
isLoading,
|
|
210
|
+
error,
|
|
211
|
+
drugOrders: drugorder,
|
|
212
|
+
testOrders: testorder,
|
|
213
|
+
orderEncounters,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
@use '@carbon/type';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/colors';
|
|
4
|
+
|
|
5
|
+
.sectionHeader {
|
|
6
|
+
@include type.type-style('heading-02');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.button {
|
|
10
|
+
height: layout.$spacing-10;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-content: flex-start;
|
|
13
|
+
align-items: baseline;
|
|
14
|
+
min-width: 20%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.buttonSet {
|
|
18
|
+
padding: 0rem;
|
|
19
|
+
margin-top: layout.$spacing-05;
|
|
20
|
+
display: flex;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
width: 100%;
|
|
23
|
+
margin-bottom: layout.$spacing-05;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.content {
|
|
27
|
+
padding: 6rem;
|
|
28
|
+
margin: 0 auto;
|
|
29
|
+
max-width: 800px;
|
|
30
|
+
font-size: 14px;
|
|
31
|
+
line-height: 1.5;
|
|
32
|
+
color: #000;
|
|
33
|
+
background-color: #fff;
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
gap: layout.$spacing-06;
|
|
37
|
+
|
|
38
|
+
// Print-specific tweaks
|
|
39
|
+
@media print {
|
|
40
|
+
padding: 0.5in;
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
line-height: 1.4;
|
|
43
|
+
color-adjust: exact; // prevents color fading during print
|
|
44
|
+
-webkit-print-color-adjust: exact;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Optional: section separators
|
|
48
|
+
& + .content {
|
|
49
|
+
margin-top: 2rem;
|
|
50
|
+
border-top: 1px dashed #ccc;
|
|
51
|
+
padding-top: 1rem;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.header {
|
|
56
|
+
justify-content: center;
|
|
57
|
+
align-items: center;
|
|
58
|
+
text-align: center;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
font-weight: bold;
|
|
62
|
+
padding-top: layout.$spacing-05;
|
|
63
|
+
padding-bottom: layout.$spacing-05;
|
|
64
|
+
gap: layout.$spacing-03;
|
|
65
|
+
text-transform: uppercase;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.field {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: baseline;
|
|
71
|
+
gap: 0;
|
|
72
|
+
width: 100%;
|
|
73
|
+
flex-grow: 1;
|
|
74
|
+
|
|
75
|
+
> .name {
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
margin-right: 8px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
> .value {
|
|
81
|
+
text-transform: uppercase;
|
|
82
|
+
border-bottom: 2px dotted black;
|
|
83
|
+
flex: 1;
|
|
84
|
+
line-height: 1.4;
|
|
85
|
+
display: inline-block;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@mixin gridByColumnCount($columns: 3, $gap: 1rem) {
|
|
90
|
+
display: grid;
|
|
91
|
+
grid-template-columns: repeat($columns, 1fr);
|
|
92
|
+
gap: $gap;
|
|
93
|
+
align-items: baseline;
|
|
94
|
+
|
|
95
|
+
@media print {
|
|
96
|
+
padding: 0;
|
|
97
|
+
gap: 0.5rem;
|
|
98
|
+
border: none;
|
|
99
|
+
grid-template-columns: repeat($columns, 1fr);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.txtUpper {
|
|
104
|
+
text-transform: uppercase;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.txtTitle {
|
|
108
|
+
text-transform: capitalize;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.cols4 {
|
|
112
|
+
@include gridByColumnCount(4);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.cols2 {
|
|
116
|
+
@include gridByColumnCount(2);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.cols3 {
|
|
120
|
+
@include gridByColumnCount(3);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.cols7 {
|
|
124
|
+
@include gridByColumnCount(7);
|
|
125
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import React, { type FC, useMemo } from 'react';
|
|
2
|
+
import styles from './discharge-printouts.scss';
|
|
3
|
+
import FieldInput from './field-input';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { useEmrConfiguration, usePatient, useSession } from '@openmrs/esm-framework';
|
|
6
|
+
import { useEncounterDetails } from '../hooks/useIpdDischargeEncounter';
|
|
7
|
+
import { InlineLoading, InlineNotification } from '@carbon/react';
|
|
8
|
+
import dayjs from 'dayjs';
|
|
9
|
+
import {
|
|
10
|
+
DATE_FORMART,
|
|
11
|
+
usePatientAllergies,
|
|
12
|
+
usePatientDiagnosis,
|
|
13
|
+
usePatientOrders,
|
|
14
|
+
} from './discharge-printout.resource';
|
|
15
|
+
import { useProvider } from '../ward-workspace/admit-patient-form-workspace/patient-admission.resources';
|
|
16
|
+
|
|
17
|
+
type DischargeSummaryProps = {
|
|
18
|
+
dischargeEncounterUuid: string;
|
|
19
|
+
patient: {
|
|
20
|
+
uuid: string;
|
|
21
|
+
openmrsId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const DischargeSummary: FC<DischargeSummaryProps> = ({ dischargeEncounterUuid, patient: _patient }) => {
|
|
27
|
+
const { t } = useTranslation();
|
|
28
|
+
const { encounter, error, isLoading } = useEncounterDetails(dischargeEncounterUuid);
|
|
29
|
+
const { isLoading: isLoadingPatient, patient, error: patientError } = usePatient(_patient.uuid);
|
|
30
|
+
const {
|
|
31
|
+
isLoading: isLoadingDiagnosis,
|
|
32
|
+
error: diagnosisError,
|
|
33
|
+
display: diagnoses,
|
|
34
|
+
} = usePatientDiagnosis(dischargeEncounterUuid);
|
|
35
|
+
const {
|
|
36
|
+
display: allergies,
|
|
37
|
+
error: allergiesError,
|
|
38
|
+
isLoading: isLoadingAllergies,
|
|
39
|
+
} = usePatientAllergies(_patient.uuid);
|
|
40
|
+
const session = useSession();
|
|
41
|
+
const { error: errorProvider, isLoading: isLoadingProvider, provider } = useProvider(session.currentProvider.uuid);
|
|
42
|
+
const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
|
|
43
|
+
const {
|
|
44
|
+
isLoading: isLoadingOders,
|
|
45
|
+
error: orderserror,
|
|
46
|
+
drugOrders,
|
|
47
|
+
testOrders,
|
|
48
|
+
} = usePatientOrders(dischargeEncounterUuid);
|
|
49
|
+
const admissionDate = useMemo(() => {
|
|
50
|
+
const admisionEncounter = encounter?.visit?.encounters?.find(
|
|
51
|
+
(e) => e.encounterType.uuid === emrConfiguration?.admissionEncounterType?.uuid,
|
|
52
|
+
);
|
|
53
|
+
if (!admisionEncounter || !admisionEncounter.encounterDatetime) return null;
|
|
54
|
+
return admisionEncounter.encounterDatetime;
|
|
55
|
+
}, [encounter, emrConfiguration]);
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
isLoading ||
|
|
59
|
+
isLoadingPatient ||
|
|
60
|
+
isLoadingEmrConfiguration ||
|
|
61
|
+
isLoadingDiagnosis ||
|
|
62
|
+
isLoadingProvider ||
|
|
63
|
+
isLoadingAllergies ||
|
|
64
|
+
isLoadingOders
|
|
65
|
+
)
|
|
66
|
+
return <InlineLoading />;
|
|
67
|
+
if (
|
|
68
|
+
error ||
|
|
69
|
+
patientError ||
|
|
70
|
+
errorFetchingEmrConfiguration ||
|
|
71
|
+
diagnosisError ||
|
|
72
|
+
errorProvider ||
|
|
73
|
+
allergiesError ||
|
|
74
|
+
orderserror
|
|
75
|
+
)
|
|
76
|
+
return (
|
|
77
|
+
<InlineNotification
|
|
78
|
+
kind="error"
|
|
79
|
+
title={
|
|
80
|
+
error?.message ??
|
|
81
|
+
patientError?.message ??
|
|
82
|
+
errorFetchingEmrConfiguration?.message ??
|
|
83
|
+
diagnosisError?.message ??
|
|
84
|
+
errorProvider?.message ??
|
|
85
|
+
allergiesError?.message ??
|
|
86
|
+
orderserror?.message
|
|
87
|
+
}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
return (
|
|
91
|
+
<div className={styles.content}>
|
|
92
|
+
<div className={styles.header}>
|
|
93
|
+
<h5>{session.sessionLocation.display}</h5>
|
|
94
|
+
<h5>{t('dischargeSummary', 'Discharge Summary')}</h5>
|
|
95
|
+
</div>
|
|
96
|
+
<div className={styles.cols3}>
|
|
97
|
+
<FieldInput name={t('name', 'Name')} value={_patient.name} />
|
|
98
|
+
<FieldInput name={t('ipNo', 'IP No')} value={_patient.openmrsId} />
|
|
99
|
+
<FieldInput
|
|
100
|
+
name={t('age', 'Age')}
|
|
101
|
+
value={`${Math.abs(dayjs(patient.birthDate).diff(dayjs(), 'years'))} years`}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
<div className={styles.cols3}>
|
|
105
|
+
<FieldInput name={t('sex', 'Sex')} value={patient.gender} />
|
|
106
|
+
<FieldInput name={t('dateOfAdmissionAbrv', 'DOA')} value={dayjs(admissionDate).format(DATE_FORMART)} />
|
|
107
|
+
<FieldInput
|
|
108
|
+
name={t('dateOfDischargeAbrv', 'DOD')}
|
|
109
|
+
value={dayjs(encounter.encounterDatetime).format(DATE_FORMART)}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
<div className={styles.cols2}>
|
|
113
|
+
<FieldInput name={t('nameOfConsultant', 'Name of consultant')} value={provider?.display?.split('-')?.at(-1)} />
|
|
114
|
+
<FieldInput name={t('department', 'Department')} value={encounter.location?.display} />
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div>
|
|
118
|
+
<strong className={styles.txtUpper}>{t('diagnosis', 'Diagnosis')}</strong>
|
|
119
|
+
<p className={styles.txtTitle}>{diagnoses?.toLowerCase() ?? t('noDiagnoses', 'No Diagnoses')}</p>
|
|
120
|
+
</div>
|
|
121
|
+
<div>
|
|
122
|
+
<strong className={styles.txtUpper}>{t('history', 'History')}</strong>
|
|
123
|
+
<p>
|
|
124
|
+
{`${_patient.name}, a ${Math.abs(dayjs(patient.birthDate).diff(dayjs(), 'years'))} year ${
|
|
125
|
+
patient.gender
|
|
126
|
+
} presented with .${
|
|
127
|
+
allergies
|
|
128
|
+
? t('knownAlergies', 'Known Alergies') + ': ' + allergies
|
|
129
|
+
: t('noKnownAlergies', 'No known alergies')
|
|
130
|
+
}`}
|
|
131
|
+
</p>
|
|
132
|
+
</div>
|
|
133
|
+
<div>
|
|
134
|
+
<strong className={styles.txtUpper}>{t('physicalExamination', 'Physical Examination')}</strong>
|
|
135
|
+
<p></p>
|
|
136
|
+
</div>
|
|
137
|
+
<div>
|
|
138
|
+
<strong className={styles.txtUpper}>{t('investigation', 'Investigation')}</strong>
|
|
139
|
+
<p>
|
|
140
|
+
{testOrders.map((order) => (
|
|
141
|
+
<p key={order.uuid} className={styles.txtTitle}>
|
|
142
|
+
{order.display?.toLowerCase()}
|
|
143
|
+
</p>
|
|
144
|
+
))}
|
|
145
|
+
</p>
|
|
146
|
+
</div>
|
|
147
|
+
<div>
|
|
148
|
+
<strong className={styles.txtUpper}>{t('treatment', 'Treatment')}</strong>
|
|
149
|
+
<div>
|
|
150
|
+
{drugOrders.map((order) => (
|
|
151
|
+
<p key={order.uuid}>{order.display}</p>
|
|
152
|
+
))}
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
<div>
|
|
156
|
+
<strong className={styles.txtUpper}>{t('dischargeInstructions', 'Discharge Instructions')}</strong>
|
|
157
|
+
<p></p>
|
|
158
|
+
</div>
|
|
159
|
+
<div className={styles.cols2}>
|
|
160
|
+
<FieldInput name={t('name', 'Name')} value={provider?.display?.split('-')?.at(-1)} />
|
|
161
|
+
<FieldInput name={t('signature', 'Signature')} />
|
|
162
|
+
</div>
|
|
163
|
+
<div className={styles.cols2}>
|
|
164
|
+
<FieldInput name={t('destination', 'Destination')} value={t('discharge', 'Discharge')} />
|
|
165
|
+
<FieldInput name={t('date', 'Date')} value={dayjs().format(DATE_FORMART)} />
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export default DischargeSummary;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { type FC } from 'react';
|
|
2
|
+
import styles from './discharge-printouts.scss';
|
|
3
|
+
type FieldInputProps = {
|
|
4
|
+
name: string;
|
|
5
|
+
value?: string;
|
|
6
|
+
delimiter?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const FieldInput: FC<FieldInputProps> = ({ name: fieldName, value, delimiter = ':' }) => {
|
|
10
|
+
return (
|
|
11
|
+
<div className={styles.field}>
|
|
12
|
+
<span className={styles.name}>
|
|
13
|
+
{fieldName}
|
|
14
|
+
{delimiter && `${delimiter} `}
|
|
15
|
+
</span>
|
|
16
|
+
<strong className={styles.value}>{value}</strong>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default FieldInput;
|