@kenyaemr/esm-ward-app 8.5.1-pre.35 → 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.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +12 -12
  2. package/dist/1160.js +1 -0
  3. package/dist/1160.js.map +1 -0
  4. package/dist/1917.js +1 -1
  5. package/dist/1917.js.map +1 -1
  6. package/dist/2059.js +1 -0
  7. package/dist/2059.js.map +1 -0
  8. package/dist/3161.js +1 -1
  9. package/dist/3161.js.map +1 -1
  10. package/dist/4224.js +2 -0
  11. package/dist/4224.js.map +1 -0
  12. package/dist/6012.js +1 -1
  13. package/dist/6012.js.map +1 -1
  14. package/dist/6871.js +1 -1
  15. package/dist/6871.js.map +1 -1
  16. package/dist/7059.js +2 -0
  17. package/dist/7059.js.map +1 -0
  18. package/dist/7661.js +1 -1
  19. package/dist/7661.js.map +1 -1
  20. package/dist/8205.js +1 -1
  21. package/dist/8205.js.map +1 -1
  22. package/dist/8308.js +1 -0
  23. package/dist/8308.js.map +1 -0
  24. package/dist/9113.js +1 -0
  25. package/dist/{9880.js.map → 9113.js.map} +1 -1
  26. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +163 -112
  27. package/dist/main.js +1 -1
  28. package/dist/main.js.map +1 -1
  29. package/dist/routes.json +1 -1
  30. package/package.json +2 -1
  31. package/src/config-schema.ts +6 -0
  32. package/src/discharge-printouts/discharge-printout.preview-modal.tsx +40 -0
  33. package/src/discharge-printouts/discharge-printout.resource.ts +215 -0
  34. package/src/discharge-printouts/discharge-printouts.scss +125 -0
  35. package/src/discharge-printouts/discharge-summary.tsx +171 -0
  36. package/src/discharge-printouts/field-input.tsx +21 -0
  37. package/src/discharge-printouts/gate-pass-printout.tsx +125 -0
  38. package/src/hooks/useIpdDischargeEncounter.ts +10 -8
  39. package/src/index.ts +4 -0
  40. package/src/routes.json +4 -0
  41. package/src/ward-patients/admitted-patients.tsx +2 -2
  42. package/src/ward-patients/discharge-in-patients.tsx +22 -17
  43. package/src/ward-patients/discharge-patients.tsx +23 -3
  44. package/src/ward-patients/patient-cells.tsx +6 -12
  45. package/src/ward-workspace/admit-patient-form-workspace/patient-admission.resources.ts +19 -1
  46. package/src/ward-workspace/kenya-emr-patient-discharge/patient-discharge.resource.tsx +2 -1
  47. package/src/ward-workspace/patient-transfer-bed-swap/patient-admit-or-transfer-request-form.component.tsx +1 -1
  48. package/dist/1352.js +0 -2
  49. package/dist/1352.js.map +0 -1
  50. package/dist/3423.js +0 -1
  51. package/dist/3423.js.map +0 -1
  52. package/dist/4701.js +0 -2
  53. package/dist/4701.js.map +0 -1
  54. package/dist/9880.js +0 -1
  55. /package/dist/{1352.js.LICENSE.txt → 4224.js.LICENSE.txt} +0 -0
  56. /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.35"}
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.35",
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": {
@@ -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;