@kenyaemr/esm-morgue-app 5.4.2-pre.2344 → 5.4.2-pre.2349
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 +21 -26
- package/dist/162.js +2 -0
- package/dist/162.js.map +1 -0
- package/dist/197.js +1 -1
- package/dist/221.js +1 -1
- package/dist/221.js.map +1 -1
- package/dist/293.js +1 -1
- package/dist/294.js +1 -1
- package/dist/300.js +1 -1
- package/dist/351.js +1 -0
- package/dist/351.js.map +1 -0
- package/dist/38.js +1 -1
- package/dist/38.js.map +1 -1
- package/dist/404.js +1 -0
- package/dist/404.js.map +1 -0
- package/dist/441.js +1 -0
- package/dist/441.js.map +1 -0
- package/dist/467.js +1 -1
- package/dist/467.js.map +1 -1
- package/dist/500.js +1 -0
- package/dist/500.js.map +1 -0
- package/dist/570.js +1 -0
- package/dist/570.js.map +1 -0
- package/dist/608.js +2 -0
- package/dist/608.js.map +1 -0
- package/dist/611.js +2 -0
- package/dist/611.js.map +1 -0
- package/dist/632.js +2 -1
- package/dist/632.js.map +1 -1
- package/dist/653.js +1 -1
- package/dist/653.js.map +1 -1
- package/dist/657.js +1 -0
- package/dist/657.js.map +1 -0
- package/dist/805.js +1 -1
- package/dist/805.js.map +1 -1
- package/dist/814.js +2 -0
- package/dist/814.js.LICENSE.txt +5 -0
- package/dist/814.js.map +1 -0
- package/dist/824.js +1 -1
- package/dist/824.js.map +1 -1
- package/dist/845.js +1 -0
- package/dist/845.js.map +1 -0
- package/dist/888.js +1 -0
- package/dist/888.js.map +1 -0
- package/dist/899.js +1 -0
- package/dist/899.js.map +1 -0
- package/dist/918.js +1 -1
- package/dist/918.js.map +1 -1
- package/dist/990.js +1 -0
- package/dist/990.js.map +1 -0
- package/dist/kenyaemr-esm-morgue-app.js +1 -1
- package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +258 -184
- package/dist/kenyaemr-esm-morgue-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.LICENSE.txt +0 -6
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/bed/bed.component.tsx +63 -134
- package/src/bed/components/deceased-patient-card-header.component.tsx +73 -0
- package/src/bed/components/deceased-patient-info.component.tsx +47 -0
- package/src/bed/components/deceased-patient-status-footer.component.tsx +43 -0
- package/src/bed-layout/admitted/admitted-bed-layout.component.tsx +175 -96
- package/src/bed-layout/awaiting/awaiting-bed-layout.component.tsx +103 -36
- package/src/bed-layout/bed-layout.scss +4 -0
- package/src/bed-layout/discharged/discharged-bed-layout.component.tsx +131 -73
- package/src/bed-linelist-view/admitted/admitted-bed-linelist-view.component.tsx +182 -134
- package/src/bed-linelist-view/awaiting/awaiting-bed-linelist-view.component.tsx +115 -71
- package/src/bed-linelist-view/discharged/discharged-bed-line-view.component.tsx +181 -109
- package/src/config-schema.ts +140 -4
- package/src/context/deceased-person-context.tsx +33 -0
- package/src/extension/actionButton.component.tsx +1 -1
- package/src/forms/admit-deceased-person-workspace/admit-deceased-person.resource.ts +84 -166
- package/src/forms/admit-deceased-person-workspace/admit-deceased-person.scss +14 -0
- package/src/forms/admit-deceased-person-workspace/admit-deceased-person.workspace.tsx +504 -334
- package/src/forms/discharge-deceased-person-workspace/discharge-body.resource.ts +0 -1
- package/src/forms/discharge-deceased-person-workspace/discharge-body.scss +15 -0
- package/src/forms/discharge-deceased-person-workspace/discharge-body.workspace.tsx +303 -244
- package/src/helpers/expression-helper.ts +122 -0
- package/src/home/home.component.tsx +23 -4
- package/src/index.ts +0 -2
- package/src/metrics/metrics-card.component.tsx +2 -2
- package/src/routes.json +0 -6
- package/src/schemas/index.ts +243 -51
- package/src/summary/summary.component.tsx +16 -9
- package/src/switcher/content-switcher.component.tsx +61 -35
- package/src/switcher/content-switcher.scss +13 -0
- package/src/types/index.ts +43 -1
- package/translations/am.json +16 -6
- package/translations/en.json +16 -6
- package/translations/sw.json +16 -6
- package/dist/347.js +0 -1
- package/dist/347.js.map +0 -1
- package/dist/373.js +0 -2
- package/dist/373.js.map +0 -1
- package/dist/398.js +0 -1
- package/dist/398.js.map +0 -1
- package/dist/410.js +0 -1
- package/dist/410.js.map +0 -1
- package/dist/429.js +0 -2
- package/dist/429.js.map +0 -1
- package/dist/579.js +0 -2
- package/dist/579.js.map +0 -1
- package/dist/619.js +0 -1
- package/dist/619.js.map +0 -1
- package/dist/633.js +0 -1
- package/dist/633.js.map +0 -1
- package/dist/712.js +0 -1
- package/dist/712.js.map +0 -1
- package/dist/713.js +0 -1
- package/dist/713.js.map +0 -1
- package/dist/723.js +0 -1
- package/dist/723.js.map +0 -1
- package/dist/989.js +0 -2
- package/dist/989.js.map +0 -1
- package/src/forms/dispose-deceased-person-workspace/dispose-deceased-person.resource.ts +0 -18
- package/src/forms/dispose-deceased-person-workspace/dispose-deceased-person.scss +0 -84
- package/src/forms/dispose-deceased-person-workspace/dispose-deceased-person.workspace.tsx +0 -505
- /package/dist/{989.js.LICENSE.txt → 162.js.LICENSE.txt} +0 -0
- /package/dist/{373.js.LICENSE.txt → 608.js.LICENSE.txt} +0 -0
- /package/dist/{429.js.LICENSE.txt → 611.js.LICENSE.txt} +0 -0
- /package/dist/{579.js.LICENSE.txt → 632.js.LICENSE.txt} +0 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { showModal, useConfig, useLayoutType } from '@openmrs/esm-framework';
|
|
4
|
+
import { InlineLoading, Pagination, Search } from '@carbon/react';
|
|
5
5
|
import styles from '../bed-layout.scss';
|
|
6
|
-
import { Patient, type MortuaryLocationResponse } from '../../types';
|
|
6
|
+
import { Patient, type MortuaryLocationResponse, EnhancedPatient } from '../../types';
|
|
7
7
|
import { ConfigObject } from '../../config-schema';
|
|
8
|
-
import { mutate as mutateSWR } from 'swr';
|
|
9
8
|
import usePatients, { useMortuaryDischargeEncounter } from './discharged-bed-layout.resource';
|
|
10
9
|
import BedCard from '../../bed/bed.component';
|
|
11
|
-
|
|
10
|
+
import EmptyMorgueAdmission from '../../empty-state/empty-morgue-admission.component';
|
|
11
|
+
import { transformDischargedPatient, extractPatientFromEnhanced } from '../../helpers/expression-helper';
|
|
12
|
+
import { PatientProvider } from '../../context/deceased-person-context';
|
|
12
13
|
interface BedLayoutProps {
|
|
13
14
|
AdmittedDeceasedPatient: MortuaryLocationResponse | null;
|
|
14
15
|
isLoading: boolean;
|
|
@@ -17,15 +18,12 @@ interface BedLayoutProps {
|
|
|
17
18
|
mutate?: () => void;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
const DischargedBedLayout: React.FC<BedLayoutProps> = ({
|
|
21
|
-
AdmittedDeceasedPatient,
|
|
22
|
-
isLoading,
|
|
23
|
-
onPrintGatePass,
|
|
24
|
-
onPrintPostmortem,
|
|
25
|
-
mutate,
|
|
26
|
-
}) => {
|
|
21
|
+
const DischargedBedLayout: React.FC<BedLayoutProps> = ({ AdmittedDeceasedPatient, isLoading, onPrintGatePass }) => {
|
|
27
22
|
const { t } = useTranslation();
|
|
28
23
|
const { morgueDischargeEncounterTypeUuid } = useConfig<ConfigObject>();
|
|
24
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
25
|
+
const isTablet = useLayoutType() === 'tablet';
|
|
26
|
+
const controlSize = isTablet ? 'md' : 'sm';
|
|
29
27
|
|
|
30
28
|
const {
|
|
31
29
|
dischargedPatientUuids,
|
|
@@ -45,6 +43,32 @@ const DischargedBedLayout: React.FC<BedLayoutProps> = ({
|
|
|
45
43
|
error: patientsError,
|
|
46
44
|
} = usePatients(dischargedPatientUuids || []);
|
|
47
45
|
|
|
46
|
+
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
47
|
+
setSearchTerm(event.target.value);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const filteredPatients = useMemo(() => {
|
|
51
|
+
if (!dischargedPatients || !searchTerm.trim()) {
|
|
52
|
+
return dischargedPatients || [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const lowerSearchTerm = searchTerm.toLowerCase().trim();
|
|
56
|
+
|
|
57
|
+
return dischargedPatients.filter((patient) => {
|
|
58
|
+
const patientName = patient?.person?.display?.toLowerCase() || '';
|
|
59
|
+
const gender = patient?.person?.gender?.toLowerCase() || '';
|
|
60
|
+
const patientId = patient?.uuid?.toLowerCase() || '';
|
|
61
|
+
const causeOfDeath = patient?.person?.causeOfDeath?.display?.toLowerCase() || '';
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
patientName.includes(lowerSearchTerm) ||
|
|
65
|
+
gender.includes(lowerSearchTerm) ||
|
|
66
|
+
patientId.includes(lowerSearchTerm) ||
|
|
67
|
+
causeOfDeath.includes(lowerSearchTerm)
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
}, [dischargedPatients, searchTerm]);
|
|
71
|
+
|
|
48
72
|
const getEncounterDateForPatient = (patientUuid: string): string | null => {
|
|
49
73
|
if (!encounters || encounters.length === 0) {
|
|
50
74
|
return null;
|
|
@@ -55,29 +79,30 @@ const DischargedBedLayout: React.FC<BedLayoutProps> = ({
|
|
|
55
79
|
return patientEncounter?.encounterDateTime || null;
|
|
56
80
|
};
|
|
57
81
|
|
|
58
|
-
const handlePrintGatePass = (patient: Patient, encounterDate?: string) => {
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
const handlePrintGatePass = (patient: EnhancedPatient | Patient, encounterDate?: string) => {
|
|
83
|
+
let originalPatient: Patient | null = null;
|
|
84
|
+
|
|
85
|
+
if ('originalPatient' in patient || 'originalMortuaryPatient' in patient) {
|
|
86
|
+
originalPatient = extractPatientFromEnhanced(patient as EnhancedPatient);
|
|
61
87
|
} else {
|
|
88
|
+
originalPatient = patient as Patient;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (onPrintGatePass && originalPatient) {
|
|
92
|
+
onPrintGatePass(originalPatient, encounterDate);
|
|
93
|
+
} else if (originalPatient) {
|
|
62
94
|
const dispose = showModal('print-confirmation-modal', {
|
|
63
95
|
onClose: () => dispose(),
|
|
64
|
-
patient:
|
|
96
|
+
patient: originalPatient,
|
|
65
97
|
encounterDate: encounterDate,
|
|
66
98
|
});
|
|
67
99
|
}
|
|
68
100
|
};
|
|
69
101
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const handlePageSizeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
77
|
-
const newPageSize = parseInt(event.target.value, 10);
|
|
78
|
-
setCurrPageSize(newPageSize);
|
|
79
|
-
// Reset to first page when changing page size
|
|
80
|
-
goTo(1);
|
|
102
|
+
const patientContextValue = {
|
|
103
|
+
mortuaryLocation: AdmittedDeceasedPatient,
|
|
104
|
+
isLoading: isLoading || encountersLoading || patientsLoading,
|
|
105
|
+
onPrintGatePass: handlePrintGatePass,
|
|
81
106
|
};
|
|
82
107
|
|
|
83
108
|
if (isLoading || encountersLoading || patientsLoading) {
|
|
@@ -98,57 +123,90 @@ const DischargedBedLayout: React.FC<BedLayoutProps> = ({
|
|
|
98
123
|
|
|
99
124
|
if (!dischargedPatients || dischargedPatients.length === 0) {
|
|
100
125
|
return (
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
<EmptyMorgueAdmission
|
|
127
|
+
title={t('noDischargedPatient', 'No deceased patients currently discharged')}
|
|
128
|
+
subTitle={t(
|
|
129
|
+
'noDischargedPatientsDescription',
|
|
130
|
+
'There are no discharged deceased patients to display at this time.',
|
|
131
|
+
)}
|
|
132
|
+
/>
|
|
104
133
|
);
|
|
105
134
|
}
|
|
106
135
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
136
|
+
const patientsToShow = filteredPatients;
|
|
137
|
+
|
|
138
|
+
if (searchTerm.trim() && patientsToShow.length === 0) {
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
<div className={styles.searchContainer}>
|
|
142
|
+
<Search
|
|
143
|
+
labelText={t('searchDeceasedPatients', 'Search deceased patients')}
|
|
144
|
+
placeholder={t(
|
|
145
|
+
'searchPatientsPlaceholder',
|
|
146
|
+
'Search by name, ID number, gender, compartment, or bed type...',
|
|
147
|
+
)}
|
|
148
|
+
value={searchTerm}
|
|
149
|
+
onChange={handleSearchChange}
|
|
150
|
+
size={controlSize}
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
<EmptyMorgueAdmission
|
|
154
|
+
title={t('noMatchingDischargedPatients', 'No matching discharged patients found')}
|
|
155
|
+
subTitle={t(
|
|
156
|
+
'noMatchingDischargedPatientsDescription',
|
|
157
|
+
'Try adjusting your search terms to find discharged patients.',
|
|
158
|
+
)}
|
|
159
|
+
/>
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
134
163
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
goTo(1);
|
|
145
|
-
} else {
|
|
146
|
-
goTo(page);
|
|
147
|
-
}
|
|
148
|
-
}}
|
|
164
|
+
return (
|
|
165
|
+
<PatientProvider value={patientContextValue}>
|
|
166
|
+
<div className={styles.searchContainer}>
|
|
167
|
+
<Search
|
|
168
|
+
labelText={t('searchDeceasedPatients', 'Search deceased patients')}
|
|
169
|
+
placeholder={t('searchPatientsPlaceholder', 'Search by name, ID number, gender, compartment, or bed type...')}
|
|
170
|
+
value={searchTerm}
|
|
171
|
+
onChange={handleSearchChange}
|
|
172
|
+
size="sm"
|
|
149
173
|
/>
|
|
150
174
|
</div>
|
|
151
|
-
|
|
175
|
+
<div className={styles.bedLayoutWrapper}>
|
|
176
|
+
<div className={styles.bedLayoutContainer}>
|
|
177
|
+
{patientsToShow.map((patient) => {
|
|
178
|
+
const encounterDate = getEncounterDateForPatient(patient.uuid);
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<BedCard
|
|
182
|
+
key={patient.uuid}
|
|
183
|
+
patient={transformDischargedPatient(patient, encounterDate)}
|
|
184
|
+
showActions={{
|
|
185
|
+
printGatePass: true,
|
|
186
|
+
}}
|
|
187
|
+
/>
|
|
188
|
+
);
|
|
189
|
+
})}
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
<div className={styles.paginationFooter}>
|
|
193
|
+
<Pagination
|
|
194
|
+
page={currentPage || 1}
|
|
195
|
+
totalItems={totalCount || 0}
|
|
196
|
+
pageSize={currPageSize}
|
|
197
|
+
pageSizes={[10, 20, 50, 100]}
|
|
198
|
+
onChange={({ page, pageSize }: { page: number; pageSize: number }) => {
|
|
199
|
+
if (pageSize !== currPageSize) {
|
|
200
|
+
setCurrPageSize(pageSize);
|
|
201
|
+
goTo(1);
|
|
202
|
+
} else {
|
|
203
|
+
goTo(page);
|
|
204
|
+
}
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</PatientProvider>
|
|
152
210
|
);
|
|
153
211
|
};
|
|
154
212
|
|
|
@@ -14,14 +14,15 @@ import {
|
|
|
14
14
|
OverflowMenuItem,
|
|
15
15
|
Tag,
|
|
16
16
|
DataTableSkeleton,
|
|
17
|
+
Search,
|
|
17
18
|
} from '@carbon/react';
|
|
18
|
-
import { launchWorkspace, navigate, useConfig, useVisit } from '@openmrs/esm-framework';
|
|
19
|
+
import { launchWorkspace, navigate, useConfig, useLayoutType, useVisit } from '@openmrs/esm-framework';
|
|
19
20
|
import styles from '../bed-linelist-view.scss';
|
|
20
21
|
import { convertDateToDays, formatDateTime } from '../../utils/utils';
|
|
21
22
|
import { Patient, Person, type MortuaryLocationResponse } from '../../types';
|
|
22
23
|
import { ConfigObject } from '../../config-schema';
|
|
23
24
|
import { mutate as mutateSWR } from 'swr';
|
|
24
|
-
import
|
|
25
|
+
import EmptyMorgueAdmission from '../../empty-state/empty-morgue-admission.component';
|
|
25
26
|
|
|
26
27
|
interface AdmittedBedLineListViewProps {
|
|
27
28
|
AdmittedDeceasedPatient: MortuaryLocationResponse | null;
|
|
@@ -50,9 +51,12 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
50
51
|
}) => {
|
|
51
52
|
const { t } = useTranslation();
|
|
52
53
|
const { autopsyFormUuid } = useConfig<ConfigObject>();
|
|
54
|
+
const isTablet = useLayoutType() === 'tablet';
|
|
55
|
+
const controlSize = isTablet ? 'md' : 'sm';
|
|
53
56
|
|
|
54
57
|
const [currentPage, setCurrentPage] = useState(1);
|
|
55
58
|
const [currPageSize, setCurrPageSize] = useState(initialPageSize);
|
|
59
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
56
60
|
|
|
57
61
|
const DaysInMortuary = ({ patientUuid }: { patientUuid: string }) => {
|
|
58
62
|
const { activeVisit } = useVisit(patientUuid);
|
|
@@ -84,7 +88,9 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
84
88
|
{ key: 'action', header: t('action', 'Action') },
|
|
85
89
|
];
|
|
86
90
|
|
|
87
|
-
const handlePostmortem = (patientUuid: string) => {
|
|
91
|
+
const handlePostmortem = (patientUuid: string, bedInfo?: { bedNumber: string; bedId: number }) => {
|
|
92
|
+
const hasBedInfo = bedInfo?.bedNumber && bedInfo?.bedId;
|
|
93
|
+
|
|
88
94
|
if (onPostmortem) {
|
|
89
95
|
onPostmortem(patientUuid);
|
|
90
96
|
} else {
|
|
@@ -100,8 +106,12 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
100
106
|
},
|
|
101
107
|
});
|
|
102
108
|
}
|
|
109
|
+
const base = `${window.getOpenmrsSpaBase()}home/morgue/patient/${patientUuid}`;
|
|
110
|
+
const to = hasBedInfo
|
|
111
|
+
? `${base}/compartment/${bedInfo.bedNumber}/${bedInfo.bedId}/mortuary-chart`
|
|
112
|
+
: `${base}/mortuary-chart`;
|
|
113
|
+
navigate({ to });
|
|
103
114
|
};
|
|
104
|
-
|
|
105
115
|
const handleDischarge = (patientUuid: string, bedId: number) => {
|
|
106
116
|
if (onDischarge) {
|
|
107
117
|
onDischarge(patientUuid);
|
|
@@ -129,19 +139,6 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
129
139
|
}
|
|
130
140
|
};
|
|
131
141
|
|
|
132
|
-
const handleDispose = (patientUuid: string, bedId: number) => {
|
|
133
|
-
if (onDispose) {
|
|
134
|
-
onDispose(patientUuid);
|
|
135
|
-
} else {
|
|
136
|
-
launchWorkspace('dispose-deceased-person-form', {
|
|
137
|
-
workspaceTitle: t('disposeForm', 'Dispose form'),
|
|
138
|
-
patientUuid: patientUuid,
|
|
139
|
-
bedId,
|
|
140
|
-
mutate,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
|
|
145
142
|
const calculateDaysAdmitted = (dateOfDeath: string): number => {
|
|
146
143
|
if (!dateOfDeath) {
|
|
147
144
|
return 0;
|
|
@@ -226,6 +223,7 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
226
223
|
daysAdmitted: '-',
|
|
227
224
|
isEmpty: true,
|
|
228
225
|
bedId,
|
|
226
|
+
searchableText: `${bedNumber} ${bedType}`.toLowerCase(),
|
|
229
227
|
});
|
|
230
228
|
} else {
|
|
231
229
|
for (const patient of patients) {
|
|
@@ -236,6 +234,7 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
236
234
|
const causeOfDeath = patient.person?.causeOfDeath?.display || t('unknown', 'Unknown');
|
|
237
235
|
const dateOfDeath = patient.person?.deathDate;
|
|
238
236
|
const daysAdmitted = calculateDaysAdmitted(dateOfDeath).toString();
|
|
237
|
+
const idNumber = getIdNumber(patient);
|
|
239
238
|
|
|
240
239
|
rows.push({
|
|
241
240
|
id: `${bedUuid}-${patientUuid}`,
|
|
@@ -244,7 +243,7 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
244
243
|
bedType,
|
|
245
244
|
status: bedStatus,
|
|
246
245
|
patientName,
|
|
247
|
-
idNumber
|
|
246
|
+
idNumber,
|
|
248
247
|
gender,
|
|
249
248
|
age,
|
|
250
249
|
dateOfDeath: formatDateTime(dateOfDeath),
|
|
@@ -255,6 +254,7 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
255
254
|
bedUuid,
|
|
256
255
|
bedId,
|
|
257
256
|
personUuid: patient.person?.uuid || '',
|
|
257
|
+
searchableText: `${patientName} ${idNumber} ${gender} ${bedNumber} ${bedType}`.toLowerCase(),
|
|
258
258
|
});
|
|
259
259
|
}
|
|
260
260
|
}
|
|
@@ -263,6 +263,32 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
263
263
|
return rows;
|
|
264
264
|
}, [getCompartmentShare, AdmittedDeceasedPatient, t, isLoading]);
|
|
265
265
|
|
|
266
|
+
const filteredRows = useMemo(() => {
|
|
267
|
+
if (!searchTerm.trim()) {
|
|
268
|
+
return allRows;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const searchLower = searchTerm.toLowerCase().trim();
|
|
272
|
+
return allRows.filter(
|
|
273
|
+
(row) =>
|
|
274
|
+
row.searchableText.includes(searchLower) ||
|
|
275
|
+
row.patientName.toLowerCase().includes(searchLower) ||
|
|
276
|
+
row.idNumber.toLowerCase().includes(searchLower) ||
|
|
277
|
+
row.gender.toLowerCase().includes(searchLower) ||
|
|
278
|
+
row.bedNumber?.toString().includes(searchLower) ||
|
|
279
|
+
row.bedType.toLowerCase().includes(searchLower),
|
|
280
|
+
);
|
|
281
|
+
}, [allRows, searchTerm]);
|
|
282
|
+
|
|
283
|
+
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
284
|
+
setSearchTerm(event.target.value);
|
|
285
|
+
setCurrentPage(1);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const hasSearchTerm = searchTerm.trim().length > 0;
|
|
289
|
+
const hasNoSearchResults = hasSearchTerm && filteredRows.length === 0;
|
|
290
|
+
const totalCount = filteredRows.length;
|
|
291
|
+
|
|
266
292
|
if (isLoading) {
|
|
267
293
|
return (
|
|
268
294
|
<div className={styles.loadingContainer}>
|
|
@@ -274,18 +300,20 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
274
300
|
if (!AdmittedDeceasedPatient) {
|
|
275
301
|
return (
|
|
276
302
|
<div className={styles.loadingContainer}>
|
|
277
|
-
<
|
|
278
|
-
|
|
279
|
-
|
|
303
|
+
<EmptyMorgueAdmission
|
|
304
|
+
title={t('noAdmittedPatient', 'No deceased patients currently admitted')}
|
|
305
|
+
subTitle={t(
|
|
306
|
+
'noAdmittedPatientsDescription',
|
|
307
|
+
'There are no admitted deceased patients to display at this time.',
|
|
308
|
+
)}
|
|
280
309
|
/>
|
|
281
310
|
</div>
|
|
282
311
|
);
|
|
283
312
|
}
|
|
284
313
|
|
|
285
|
-
const totalCount = allRows.length;
|
|
286
314
|
const startIndex = (currentPage - 1) * currPageSize;
|
|
287
315
|
const endIndex = startIndex + currPageSize;
|
|
288
|
-
const paginatedRows = paginated ?
|
|
316
|
+
const paginatedRows = paginated ? filteredRows.slice(startIndex, endIndex) : filteredRows;
|
|
289
317
|
|
|
290
318
|
const goTo = (page: number) => setCurrentPage(page);
|
|
291
319
|
|
|
@@ -299,120 +327,140 @@ const AdmittedBedLineListView: React.FC<AdmittedBedLineListViewProps> = ({
|
|
|
299
327
|
}
|
|
300
328
|
};
|
|
301
329
|
|
|
330
|
+
const NoSearchResults = () => (
|
|
331
|
+
<div className={styles.emptyState}>
|
|
332
|
+
<EmptyMorgueAdmission
|
|
333
|
+
title={t('noSearchResults', 'We couldn’t find anything')}
|
|
334
|
+
subTitle={t('tryAgain', 'Try adjusting your search {{searchTerm}} and try again', { searchTerm })}
|
|
335
|
+
/>
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
|
|
302
339
|
return (
|
|
303
340
|
<div className={styles.bedLayoutWrapper}>
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
<TableHeader key={header.key} {...getHeaderProps({ header })}>
|
|
312
|
-
{header.header}
|
|
313
|
-
</TableHeader>
|
|
314
|
-
))}
|
|
315
|
-
</TableRow>
|
|
316
|
-
</TableHead>
|
|
317
|
-
<TableBody>
|
|
318
|
-
{rows.map((row) => {
|
|
319
|
-
const rowData = allRows.find((r) => r.id === row.id);
|
|
320
|
-
if (!rowData) {
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
return (
|
|
325
|
-
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
326
|
-
{row.cells.map((cell) => {
|
|
327
|
-
const cellKey = cell.info.header as keyof typeof rowData;
|
|
328
|
-
|
|
329
|
-
if (cell.info.header === 'daysAdmitted' && !rowData.isEmpty) {
|
|
330
|
-
return (
|
|
331
|
-
<TableCell key={cell.id}>
|
|
332
|
-
<DaysInMortuary patientUuid={rowData.patientUuid} />
|
|
333
|
-
</TableCell>
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (cell.info.header === 'admissionDate' && !rowData.isEmpty) {
|
|
338
|
-
return (
|
|
339
|
-
<TableCell key={cell.id}>
|
|
340
|
-
<AdmissionDate patientUuid={rowData.patientUuid} />
|
|
341
|
-
</TableCell>
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (cell.info.header === 'status') {
|
|
346
|
-
return (
|
|
347
|
-
<TableCell key={cell.id}>
|
|
348
|
-
<Tag type={rowData.status === 'AVAILABLE' ? 'green' : 'red'} size="sm">
|
|
349
|
-
{rowData.status === 'AVAILABLE'
|
|
350
|
-
? t('available', 'Available')
|
|
351
|
-
: t('occupied', 'Occupied')}
|
|
352
|
-
</Tag>
|
|
353
|
-
</TableCell>
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (cell.info.header === 'action') {
|
|
358
|
-
return (
|
|
359
|
-
<TableCell key={cell.id}>
|
|
360
|
-
{!rowData.isEmpty && (
|
|
361
|
-
<OverflowMenu flipped>
|
|
362
|
-
<OverflowMenuItem
|
|
363
|
-
onClick={() => {
|
|
364
|
-
const hasBedInfo = rowData.bedNumber && rowData.bedId;
|
|
365
|
-
const base = `${window.getOpenmrsSpaBase()}home/morgue/patient/${
|
|
366
|
-
rowData.patientUuid
|
|
367
|
-
}`;
|
|
368
|
-
const to = hasBedInfo
|
|
369
|
-
? `${base}/compartment/${rowData.bedNumber}/${rowData.bedId}/mortuary-chart`
|
|
370
|
-
: `${base}/mortuary-chart`;
|
|
371
|
-
navigate({ to });
|
|
372
|
-
}}
|
|
373
|
-
itemText={t('viewDetails', 'View details')}
|
|
374
|
-
/>
|
|
375
|
-
<OverflowMenuItem
|
|
376
|
-
onClick={() => handlePostmortem(rowData.patientUuid)}
|
|
377
|
-
itemText={t('postmortemForm', 'Postmortem')}
|
|
378
|
-
/>
|
|
379
|
-
<OverflowMenuItem
|
|
380
|
-
onClick={() => handleSwapCompartment(rowData.patientUuid, rowData.bedId)}
|
|
381
|
-
itemText={t('compartmentSwap', 'Compartment swap')}
|
|
382
|
-
/>
|
|
383
|
-
<OverflowMenuItem
|
|
384
|
-
onClick={() => handleDispose(rowData.patientUuid, rowData.bedId)}
|
|
385
|
-
itemText={t('disposeForm', 'Dispose')}
|
|
386
|
-
/>
|
|
387
|
-
<OverflowMenuItem
|
|
388
|
-
onClick={() => handleDischarge(rowData.patientUuid, rowData.bedId)}
|
|
389
|
-
itemText={t('dischargeForm', 'Discharge')}
|
|
390
|
-
/>
|
|
391
|
-
</OverflowMenu>
|
|
392
|
-
)}
|
|
393
|
-
</TableCell>
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
return <TableCell key={cell.id}>{rowData[cellKey] || '-'}</TableCell>;
|
|
397
|
-
})}
|
|
398
|
-
</TableRow>
|
|
399
|
-
);
|
|
400
|
-
})}
|
|
401
|
-
</TableBody>
|
|
402
|
-
</Table>
|
|
403
|
-
</TableContainer>
|
|
404
|
-
)}
|
|
405
|
-
</DataTable>
|
|
406
|
-
|
|
407
|
-
{paginated && !isLoading && totalCount > 0 && (
|
|
408
|
-
<Pagination
|
|
409
|
-
page={currentPage}
|
|
410
|
-
pageSize={currPageSize}
|
|
411
|
-
pageSizes={pageSizes}
|
|
412
|
-
totalItems={totalCount}
|
|
413
|
-
size={'sm'}
|
|
414
|
-
onChange={handlePaginationChange}
|
|
341
|
+
<div className={styles.searchContainer}>
|
|
342
|
+
<Search
|
|
343
|
+
labelText={t('noSearchDeceasedPatients', 'Search deceased patients')}
|
|
344
|
+
placeholder={t('searchPatientsPlaceholder', 'Search by name, ID number, gender, compartment, or bed type...')}
|
|
345
|
+
value={searchTerm}
|
|
346
|
+
onChange={handleSearchChange}
|
|
347
|
+
size={controlSize}
|
|
415
348
|
/>
|
|
349
|
+
</div>
|
|
350
|
+
{hasNoSearchResults ? (
|
|
351
|
+
<NoSearchResults />
|
|
352
|
+
) : (
|
|
353
|
+
<>
|
|
354
|
+
<DataTable rows={paginatedRows} headers={headers} isSortable useZebraStyles>
|
|
355
|
+
{({ rows, headers, getHeaderProps, getRowProps, getTableProps }) => (
|
|
356
|
+
<TableContainer>
|
|
357
|
+
<Table {...getTableProps()} aria-label="mortuary beds table">
|
|
358
|
+
<TableHead>
|
|
359
|
+
<TableRow>
|
|
360
|
+
{headers.map((header) => (
|
|
361
|
+
<TableHeader key={header.key} {...getHeaderProps({ header })}>
|
|
362
|
+
{header.header}
|
|
363
|
+
</TableHeader>
|
|
364
|
+
))}
|
|
365
|
+
</TableRow>
|
|
366
|
+
</TableHead>
|
|
367
|
+
<TableBody>
|
|
368
|
+
{rows.map((row) => {
|
|
369
|
+
const rowData = allRows.find((r) => r.id === row.id);
|
|
370
|
+
if (!rowData) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
376
|
+
{row.cells.map((cell) => {
|
|
377
|
+
const cellKey = cell.info.header as keyof typeof rowData;
|
|
378
|
+
|
|
379
|
+
if (cell.info.header === 'daysAdmitted' && !rowData.isEmpty) {
|
|
380
|
+
return (
|
|
381
|
+
<TableCell key={cell.id}>
|
|
382
|
+
<DaysInMortuary patientUuid={rowData.patientUuid} />
|
|
383
|
+
</TableCell>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (cell.info.header === 'admissionDate' && !rowData.isEmpty) {
|
|
388
|
+
return (
|
|
389
|
+
<TableCell key={cell.id}>
|
|
390
|
+
<AdmissionDate patientUuid={rowData.patientUuid} />
|
|
391
|
+
</TableCell>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (cell.info.header === 'status') {
|
|
396
|
+
return (
|
|
397
|
+
<TableCell key={cell.id}>
|
|
398
|
+
<Tag type={rowData.status === 'AVAILABLE' ? 'green' : 'red'} size="sm">
|
|
399
|
+
{rowData.status === 'AVAILABLE'
|
|
400
|
+
? t('available', 'Available')
|
|
401
|
+
: t('occupied', 'Occupied')}
|
|
402
|
+
</Tag>
|
|
403
|
+
</TableCell>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (cell.info.header === 'action') {
|
|
408
|
+
return (
|
|
409
|
+
<TableCell key={cell.id}>
|
|
410
|
+
{!rowData.isEmpty && (
|
|
411
|
+
<OverflowMenu flipped>
|
|
412
|
+
<OverflowMenuItem
|
|
413
|
+
onClick={() => {
|
|
414
|
+
const hasBedInfo = rowData.bedNumber && rowData.bedId;
|
|
415
|
+
const base = `${window.getOpenmrsSpaBase()}home/morgue/patient/${
|
|
416
|
+
rowData.patientUuid
|
|
417
|
+
}`;
|
|
418
|
+
const to = hasBedInfo
|
|
419
|
+
? `${base}/compartment/${rowData.bedNumber}/${rowData.bedId}/mortuary-chart`
|
|
420
|
+
: `${base}/mortuary-chart`;
|
|
421
|
+
navigate({ to });
|
|
422
|
+
}}
|
|
423
|
+
itemText={t('viewDetails', 'View details')}
|
|
424
|
+
/>
|
|
425
|
+
<OverflowMenuItem
|
|
426
|
+
onClick={() => handlePostmortem(rowData.patientUuid)}
|
|
427
|
+
itemText={t('postmortemForm', 'Postmortem')}
|
|
428
|
+
/>
|
|
429
|
+
<OverflowMenuItem
|
|
430
|
+
onClick={() => handleSwapCompartment(rowData.patientUuid, rowData.bedId)}
|
|
431
|
+
itemText={t('compartmentSwap', 'Compartment swap')}
|
|
432
|
+
/>
|
|
433
|
+
<OverflowMenuItem
|
|
434
|
+
onClick={() => handleDischarge(rowData.patientUuid, rowData.bedId)}
|
|
435
|
+
itemText={t('dischargeForm', 'Discharge')}
|
|
436
|
+
/>
|
|
437
|
+
</OverflowMenu>
|
|
438
|
+
)}
|
|
439
|
+
</TableCell>
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
return <TableCell key={cell.id}>{rowData[cellKey] || '-'}</TableCell>;
|
|
443
|
+
})}
|
|
444
|
+
</TableRow>
|
|
445
|
+
);
|
|
446
|
+
})}
|
|
447
|
+
</TableBody>
|
|
448
|
+
</Table>
|
|
449
|
+
</TableContainer>
|
|
450
|
+
)}
|
|
451
|
+
</DataTable>
|
|
452
|
+
|
|
453
|
+
{paginated && !isLoading && totalCount > 0 && (
|
|
454
|
+
<Pagination
|
|
455
|
+
page={currentPage}
|
|
456
|
+
pageSize={currPageSize}
|
|
457
|
+
pageSizes={pageSizes}
|
|
458
|
+
totalItems={totalCount}
|
|
459
|
+
size={'sm'}
|
|
460
|
+
onChange={handlePaginationChange}
|
|
461
|
+
/>
|
|
462
|
+
)}
|
|
463
|
+
</>
|
|
416
464
|
)}
|
|
417
465
|
</div>
|
|
418
466
|
);
|