@kenyaemr/esm-morgue-app 5.4.1-pre.1842 → 5.4.1-pre.1850
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +85 -22
- package/dist/160.js +1 -0
- package/dist/160.js.map +1 -0
- package/dist/195.js +1 -0
- package/dist/195.js.map +1 -0
- package/dist/240.js +1 -0
- package/dist/240.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/300.js +1 -1
- package/dist/{561.js → 521.js} +1 -1
- package/dist/521.js.map +1 -0
- package/dist/527.js +1 -0
- package/dist/527.js.map +1 -0
- package/dist/596.js +1 -0
- package/dist/596.js.map +1 -0
- package/dist/652.js +1 -1
- package/dist/652.js.map +1 -1
- package/dist/659.js +2 -0
- package/dist/659.js.map +1 -0
- package/dist/675.js +2 -0
- package/dist/{485.js.map → 675.js.map} +1 -1
- package/dist/730.js +1 -0
- package/dist/730.js.map +1 -0
- package/dist/755.js +1 -0
- package/dist/755.js.map +1 -0
- package/dist/795.js +1 -0
- package/dist/795.js.map +1 -0
- package/dist/818.js +1 -0
- package/dist/818.js.map +1 -0
- package/dist/{942.js → 870.js} +1 -1
- package/dist/870.js.map +1 -0
- package/dist/909.js +2 -0
- package/dist/909.js.map +1 -0
- package/dist/926.js +1 -1
- package/dist/926.js.map +1 -1
- package/dist/929.js +1 -0
- package/dist/929.js.map +1 -0
- package/dist/960.js +1 -0
- package/dist/960.js.map +1 -0
- package/dist/kenyaemr-esm-morgue-app.js +1 -1
- package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +371 -127
- package/dist/kenyaemr-esm-morgue-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/autosuggest/autosuggest.component.tsx +162 -0
- package/src/autosuggest/autosuggest.scss +61 -0
- package/src/autosuggest/patient-search-info.component.tsx +75 -0
- package/src/autosuggest/patient-search-info.scss +62 -0
- package/src/autosuggest/search-empty-state.component.tsx +21 -0
- package/src/autosuggest/search-empty-state.scss +18 -0
- package/src/card/avail-compartment.compartment.tsx +40 -41
- package/src/card/compartment-view.compartment.tsx +37 -14
- package/src/card/compartment.scss +18 -13
- package/src/card/compartmentSharing.component.tsx +21 -0
- package/src/card/compartmentSharing.scss +24 -0
- package/src/card/empty-compartment.component.tsx +11 -7
- package/src/card/empty-compartment.scss +61 -0
- package/src/component/deceasedInfo/deceased-info.component.tsx +1 -1
- package/src/component/main.component.tsx +1 -7
- package/src/component/next-of-kin-details/nextOfKinDetails.component.tsx +50 -0
- package/src/component/next-of-kin-details/nextOfKinDetails.scss +37 -0
- package/src/config-schema.ts +30 -22
- package/src/extension/actionButton.component.tsx +74 -0
- package/src/extension/actionButton.scss +69 -0
- package/src/extension/deceasedInfoBanner.component.tsx +57 -0
- package/src/hook/useAdmitPatient.ts +285 -0
- package/src/hook/useDeceasedPatients.ts +12 -0
- package/src/hook/useDischargedPatient.ts +55 -0
- package/src/hook/useMorgue.resource.ts +11 -120
- package/src/hook/useMortuaryAdmissionLocation.ts +64 -0
- package/src/hook/usePersonAttributes.ts +65 -0
- package/src/index.ts +4 -0
- package/src/routes.json +24 -7
- package/src/tables/admitted-queue.component.tsx +17 -21
- package/src/tables/discharge-queue.component.tsx +33 -27
- package/src/tables/generic-table.component.tsx +44 -19
- package/src/tabs/tabs.component.tsx +36 -16
- package/src/tabs/tabs.scss +3 -186
- package/src/types/index.ts +291 -9
- package/src/utils/utils.ts +55 -4
- package/src/workspaces/admit-body.scss +46 -0
- package/src/workspaces/admit-body.workspace.tsx +79 -0
- package/src/workspaces/discharge-body.scss +2 -2
- package/src/workspaces/discharge-body.workspace.tsx +157 -101
- package/src/workspaces/patientAdditionalInfoForm.workspace.tsx +141 -218
- package/src/workspaces/swap-unit.scss +46 -0
- package/src/workspaces/swap-unit.workspace.tsx +168 -0
- package/translations/en.json +22 -7
- package/dist/340.js +0 -1
- package/dist/340.js.map +0 -1
- package/dist/38.js +0 -1
- package/dist/38.js.map +0 -1
- package/dist/485.js +0 -2
- package/dist/553.js +0 -1
- package/dist/553.js.map +0 -1
- package/dist/561.js.map +0 -1
- package/dist/592.js +0 -2
- package/dist/592.js.map +0 -1
- package/dist/759.js +0 -1
- package/dist/759.js.map +0 -1
- package/dist/942.js.map +0 -1
- package/dist/987.js +0 -2
- package/dist/987.js.map +0 -1
- package/src/component/deceasedInfo/deceased-header.component.tsx +0 -37
- package/src/tables/waiting-queue.component.tsx +0 -91
- /package/dist/{987.js.LICENSE.txt → 659.js.LICENSE.txt} +0 -0
- /package/dist/{485.js.LICENSE.txt → 675.js.LICENSE.txt} +0 -0
- /package/dist/{592.js.LICENSE.txt → 909.js.LICENSE.txt} +0 -0
|
@@ -29,88 +29,6 @@ import useSWRImmutable from 'swr/immutable';
|
|
|
29
29
|
import { makeUrlUrl } from '../utils/utils';
|
|
30
30
|
import type { Observable } from 'rxjs';
|
|
31
31
|
|
|
32
|
-
const getMorguePatientStatus = async (
|
|
33
|
-
patientUuid: string,
|
|
34
|
-
morgueVisitTypeUuid: string,
|
|
35
|
-
morgueDischargeEncounter: string,
|
|
36
|
-
): Promise<'discharged' | 'admitted' | 'awaiting'> => {
|
|
37
|
-
const customRepresentation = 'custom:(visitType:(uuid),startDatetime,stopDatetime,encounters:(encounterType:(uuid)))';
|
|
38
|
-
const url = `${restBaseUrl}/visit?v=${customRepresentation}&patient=${patientUuid}&limit=1`;
|
|
39
|
-
const response = await openmrsFetch<{
|
|
40
|
-
results: Array<{
|
|
41
|
-
visitType: { uuid: string };
|
|
42
|
-
startDatetime: string;
|
|
43
|
-
stopDatetime: any;
|
|
44
|
-
encounters: Array<{
|
|
45
|
-
encounterType: {
|
|
46
|
-
uuid: string;
|
|
47
|
-
};
|
|
48
|
-
}>;
|
|
49
|
-
}>;
|
|
50
|
-
}>(url);
|
|
51
|
-
const visit = response?.data?.results[0];
|
|
52
|
-
const hasDischargeEncounter = visit?.encounters?.some(
|
|
53
|
-
(encounter) => encounter.encounterType.uuid === morgueDischargeEncounter,
|
|
54
|
-
);
|
|
55
|
-
const isMorgueVisit = visit?.visitType?.uuid === morgueVisitTypeUuid;
|
|
56
|
-
const isVisitActive = !(typeof visit?.stopDatetime === 'string');
|
|
57
|
-
|
|
58
|
-
if (!isMorgueVisit) {
|
|
59
|
-
return 'awaiting';
|
|
60
|
-
}
|
|
61
|
-
if (hasDischargeEncounter) {
|
|
62
|
-
return 'discharged';
|
|
63
|
-
}
|
|
64
|
-
return 'admitted';
|
|
65
|
-
};
|
|
66
|
-
export const useDeceasedPatient = (searchTerm?: string) => {
|
|
67
|
-
const { morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid } = useConfig<ConfigObject>();
|
|
68
|
-
const [deceasedPatient, setDeceasedPatient] = useState([]);
|
|
69
|
-
const [isLoadingStatus, setIsLoadingStatus] = useState(false);
|
|
70
|
-
const [statusError, setStatusError] = useState();
|
|
71
|
-
const customRepresentation =
|
|
72
|
-
'custom:(uuid,display,identifiers:(identifier,uuid,preferred,location:(uuid,name)),person:(uuid,display,gender,birthdate,dead,age,deathDate,causeOfDeath:(uuid,display),preferredAddress:(uuid,stateProvince,countyDistrict,address4)))';
|
|
73
|
-
const uril = makeUrlUrl(`${restBaseUrl}/morgue/patient?v=${customRepresentation}&dead=true&q=${searchTerm}`);
|
|
74
|
-
const pageSize = 10;
|
|
75
|
-
const {
|
|
76
|
-
data: paginatedData,
|
|
77
|
-
error: paginatedError,
|
|
78
|
-
isLoading: isPaginatedLoading,
|
|
79
|
-
} = useOpenmrsPagination<PaginatedResponse>(uril, pageSize);
|
|
80
|
-
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (paginatedData?.length) {
|
|
83
|
-
(async () => {
|
|
84
|
-
try {
|
|
85
|
-
setIsLoadingStatus(true);
|
|
86
|
-
const status = await Promise.all(
|
|
87
|
-
paginatedData.map((data) =>
|
|
88
|
-
getMorguePatientStatus(data.person?.uuid, morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid),
|
|
89
|
-
),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
setDeceasedPatient(
|
|
93
|
-
paginatedData.map((patient, index) => ({
|
|
94
|
-
...patient,
|
|
95
|
-
status: status[index],
|
|
96
|
-
})),
|
|
97
|
-
);
|
|
98
|
-
} catch (error) {
|
|
99
|
-
setStatusError(error);
|
|
100
|
-
} finally {
|
|
101
|
-
setIsLoadingStatus(false);
|
|
102
|
-
}
|
|
103
|
-
})();
|
|
104
|
-
}
|
|
105
|
-
}, [morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid, paginatedData]);
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
data: deceasedPatient,
|
|
109
|
-
error: paginatedError ?? statusError,
|
|
110
|
-
isLoading: isPaginatedLoading || isLoadingStatus,
|
|
111
|
-
};
|
|
112
|
-
};
|
|
113
|
-
|
|
114
32
|
export const useVisitType = () => {
|
|
115
33
|
const customRepresentation = 'custom:(uuid,display,name)';
|
|
116
34
|
const url = `${restBaseUrl}/visittype?v=${customRepresentation}`;
|
|
@@ -137,7 +55,6 @@ export const usePaymentModes = (excludeWaiver: boolean = true) => {
|
|
|
137
55
|
error,
|
|
138
56
|
};
|
|
139
57
|
};
|
|
140
|
-
|
|
141
58
|
export const useBillableItems = () => {
|
|
142
59
|
const url = `${restBaseUrl}/cashier/billableService?v=custom:(uuid,name,shortName,serviceStatus,serviceType:(uuid,display),servicePrices:(uuid,name,price,paymentMode))`;
|
|
143
60
|
const { data, isLoading, error } = useSWR<{ data: { results: Array<OpenmrsResource> } }>(url, openmrsFetch);
|
|
@@ -152,53 +69,19 @@ export const useBillableItems = () => {
|
|
|
152
69
|
setSearchTerm,
|
|
153
70
|
};
|
|
154
71
|
};
|
|
155
|
-
|
|
156
|
-
export const useMorgueCompartment = () => {
|
|
157
|
-
const { morgueCompartmentTagUuid } = useConfig<ConfigObject>();
|
|
158
|
-
const customRepresentation = 'custom:(uuid,display,name)';
|
|
159
|
-
const url = `${restBaseUrl}/location?v=${customRepresentation}&tag=${morgueCompartmentTagUuid}`;
|
|
160
|
-
const { data, isLoading, error } = useSWR<FetchResponse<{ results: Array<Location> }>>(url, openmrsFetch);
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
morgueCompartments: data?.data?.results ?? [],
|
|
164
|
-
isLoading,
|
|
165
|
-
error,
|
|
166
|
-
};
|
|
167
|
-
};
|
|
168
|
-
|
|
72
|
+
// // Used
|
|
169
73
|
export const createPatientBill = (payload) => {
|
|
170
74
|
const postUrl = `${restBaseUrl}/cashier/bill`;
|
|
171
75
|
return openmrsFetch(postUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload });
|
|
172
76
|
};
|
|
173
|
-
|
|
77
|
+
// Used
|
|
174
78
|
export const useCashPoint = () => {
|
|
175
79
|
const url = `/ws/rest/v1/cashier/cashPoint`;
|
|
176
80
|
const { data, isLoading, error } = useSWR<{ data: { results: Array<OpenmrsResource> } }>(url, openmrsFetch);
|
|
177
81
|
|
|
178
82
|
return { isLoading, error, cashPoints: data?.data?.results ?? [] };
|
|
179
83
|
};
|
|
180
|
-
|
|
181
|
-
export const startVisitWithEncounter = (payload) => {
|
|
182
|
-
const postUrl = `${restBaseUrl}/encounter`;
|
|
183
|
-
return openmrsFetch(postUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload });
|
|
184
|
-
};
|
|
185
|
-
export const useActiveMorgueVisit = (patientUuid: string) => {
|
|
186
|
-
const customRepresentation = 'custom:(visitType:(uuid),startDatetime,stopDatetime,encounters:(encounterType:(uuid)))';
|
|
187
|
-
const url = `${restBaseUrl}/visit?v=${customRepresentation}&includeInactive=false&patient=${patientUuid}&limit=1`;
|
|
188
|
-
const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<Visit> }>>(url, openmrsFetch);
|
|
189
|
-
const activeDeceased = data?.data?.results;
|
|
190
|
-
|
|
191
|
-
return { data: activeDeceased, error, isLoading };
|
|
192
|
-
};
|
|
193
|
-
const usePerson = (uuid: string) => {
|
|
194
|
-
const customRepresentation = `custom:(uuid,display,gender,birthdate,dead,age,deathDate,causeOfDeath:(uuid,display))`;
|
|
195
|
-
const url = `${restBaseUrl}/person/${uuid}?v=${customRepresentation}`;
|
|
196
|
-
const { isLoading, error, data } = useSWR<FetchResponse<Patient['person']>>(url, openmrsFetch);
|
|
197
|
-
const person = data?.data;
|
|
198
|
-
return { isLoading, error, person };
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
export default usePerson;
|
|
84
|
+
// // Used
|
|
202
85
|
|
|
203
86
|
export function useVisitQueueEntry(patientUuid, visitUuid): UseVisitQueueEntries {
|
|
204
87
|
const apiUrl = `${restBaseUrl}/visit-queue-entry?v=full&patient=${patientUuid}`;
|
|
@@ -237,7 +120,15 @@ export function useVisitQueueEntry(patientUuid, visitUuid): UseVisitQueueEntries
|
|
|
237
120
|
mutate,
|
|
238
121
|
};
|
|
239
122
|
}
|
|
123
|
+
export const usePerson = (uuid: string) => {
|
|
124
|
+
const customRepresentation = `custom:(uuid,display,gender,birthdate,dead,age,deathDate,causeOfDeath:(uuid,display))`;
|
|
125
|
+
const url = `${restBaseUrl}/person/${uuid}?v=${customRepresentation}`;
|
|
126
|
+
const { isLoading, error, data } = useSWR<FetchResponse<Patient['person']>>(url, openmrsFetch);
|
|
127
|
+
const person = data?.data;
|
|
128
|
+
return { isLoading, error, person };
|
|
129
|
+
};
|
|
240
130
|
|
|
131
|
+
// Used
|
|
241
132
|
export function removeQueuedPatient(
|
|
242
133
|
queueUuid: string,
|
|
243
134
|
queueEntryUuid: string,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FetchResponse,
|
|
3
|
+
openmrsFetch,
|
|
4
|
+
restBaseUrl,
|
|
5
|
+
useFeatureFlag,
|
|
6
|
+
type Location,
|
|
7
|
+
useSession,
|
|
8
|
+
} from '@openmrs/esm-framework';
|
|
9
|
+
import useSWR from 'swr';
|
|
10
|
+
import useSWRImmutable from 'swr/immutable';
|
|
11
|
+
import { useParams } from 'react-router-dom';
|
|
12
|
+
import { type MortuaryLocationFetchResponse, type FHIREncounter } from '../types';
|
|
13
|
+
|
|
14
|
+
export function useLocation(locationUuid: string, rep: string = 'custom:(display,uuid)') {
|
|
15
|
+
return useSWRImmutable<FetchResponse<Location>>(
|
|
16
|
+
locationUuid ? `${restBaseUrl}/location/${locationUuid}?v=${rep}` : null,
|
|
17
|
+
openmrsFetch,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useMortuaryLocation(): {
|
|
22
|
+
location: Location | undefined;
|
|
23
|
+
isLoadingLocation: boolean;
|
|
24
|
+
errorFetchingLocation: Error | undefined;
|
|
25
|
+
invalidLocation: boolean;
|
|
26
|
+
} {
|
|
27
|
+
const { locationUuid: locationUuidFromUrl } = useParams();
|
|
28
|
+
const { sessionLocation } = useSession();
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
data: locationResponse,
|
|
32
|
+
isLoading: isLoadingLocation,
|
|
33
|
+
error: errorFetchingLocation,
|
|
34
|
+
} = useLocation(locationUuidFromUrl ?? null);
|
|
35
|
+
|
|
36
|
+
const invalidLocation = !!locationUuidFromUrl && !!errorFetchingLocation;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
location: locationUuidFromUrl ? locationResponse?.data : sessionLocation,
|
|
40
|
+
isLoadingLocation,
|
|
41
|
+
errorFetchingLocation,
|
|
42
|
+
invalidLocation,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const requestRep =
|
|
47
|
+
'custom:(ward,totalBeds,occupiedBeds,bedLayouts:(rowNumber,columnNumber,bedNumber,bedId,bedUuid,status,location,patients:(person:full,identifiers,uuid)))';
|
|
48
|
+
|
|
49
|
+
export function useAdmissionLocation(rep: string = requestRep) {
|
|
50
|
+
const { location } = useMortuaryLocation();
|
|
51
|
+
const isBedManagementModuleInstalled = useFeatureFlag('bedmanagement-module');
|
|
52
|
+
|
|
53
|
+
const apiUrl = location?.uuid ? `${restBaseUrl}/admissionLocation/${location.uuid}?v=${rep}` : null;
|
|
54
|
+
|
|
55
|
+
const { data, ...rest } = useSWR<FetchResponse<MortuaryLocationFetchResponse>, Error>(
|
|
56
|
+
isBedManagementModuleInstalled ? apiUrl : null,
|
|
57
|
+
openmrsFetch,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
admissionLocation: data?.data,
|
|
62
|
+
...rest,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import { Patient, PatientInfo } from '../types';
|
|
3
|
+
import useSWR from 'swr';
|
|
4
|
+
|
|
5
|
+
export const usePersonAttributes = (personUuid: string) => {
|
|
6
|
+
const { data, error, isLoading } = useSWR<FetchResponse<{ results: Patient['person']['attributes'] }>>(
|
|
7
|
+
personUuid ? `${restBaseUrl}/person/${personUuid}/attribute` : null,
|
|
8
|
+
openmrsFetch,
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const updatePersonAttributes = async (
|
|
12
|
+
payload: { attributeType: string; value: string },
|
|
13
|
+
personUuid: string,
|
|
14
|
+
attributeUuid: string,
|
|
15
|
+
) => {
|
|
16
|
+
const url = `${restBaseUrl}/person/${personUuid}/attribute/${attributeUuid}`;
|
|
17
|
+
return openmrsFetch(url, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
body: JSON.stringify(payload),
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const createPersonAttribute = async (payload: { attributeType: string; value: string }, personUuid: string) => {
|
|
27
|
+
const url = `${restBaseUrl}/person/${personUuid}/attribute`;
|
|
28
|
+
return openmrsFetch(url, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
body: JSON.stringify(payload),
|
|
31
|
+
headers: {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const createOrUpdatePersonAttribute = async (
|
|
38
|
+
personUuid: string,
|
|
39
|
+
payload: { attributeType: string; value: string },
|
|
40
|
+
person: PatientInfo,
|
|
41
|
+
) => {
|
|
42
|
+
const { attributeType, value } = payload;
|
|
43
|
+
|
|
44
|
+
const existingAttribute = person.attributes.find((attr) => attr.uuid === attributeType);
|
|
45
|
+
|
|
46
|
+
if (existingAttribute) {
|
|
47
|
+
return updatePersonAttributes(
|
|
48
|
+
{ attributeType: existingAttribute.uuid, value },
|
|
49
|
+
personUuid,
|
|
50
|
+
existingAttribute.uuid,
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
return createPersonAttribute({ attributeType, value }, personUuid);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
personAttributes: data?.data?.results ?? [],
|
|
59
|
+
error,
|
|
60
|
+
isLoading,
|
|
61
|
+
updatePersonAttributes,
|
|
62
|
+
createPersonAttribute,
|
|
63
|
+
createOrUpdatePersonAttribute,
|
|
64
|
+
};
|
|
65
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -38,3 +38,7 @@ export const patientAdditionalInfoForm = getAsyncLifecycle(
|
|
|
38
38
|
options,
|
|
39
39
|
);
|
|
40
40
|
export const dischargeBodyForm = getAsyncLifecycle(() => import('./workspaces/discharge-body.workspace'), options);
|
|
41
|
+
export const admitBodyForm = getAsyncLifecycle(() => import('./workspaces/admit-body.workspace'), options);
|
|
42
|
+
export const swapForm = getAsyncLifecycle(() => import('./workspaces/swap-unit.workspace'), options);
|
|
43
|
+
export const actionBarButtons = getAsyncLifecycle(() => import('./extension/actionButton.component'), options);
|
|
44
|
+
export const bannerInfo = getAsyncLifecycle(() => import('./extension/deceasedInfoBanner.component'), options);
|
package/src/routes.json
CHANGED
|
@@ -16,14 +16,19 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"slot": "
|
|
19
|
+
"name": "action-buttons",
|
|
20
|
+
"component": "actionBarButtons",
|
|
21
|
+
"slot": "mortuary-action-buttons-slot",
|
|
22
|
+
"meta": {
|
|
23
|
+
"fullWidth": false
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "deceased-banner-info",
|
|
28
|
+
"component": "bannerInfo",
|
|
29
|
+
"slot": "deceased-banner-info-slot",
|
|
22
30
|
"meta": {
|
|
23
|
-
"
|
|
24
|
-
"title": "Peer Calendar",
|
|
25
|
-
"slot": "peer-calendar-dashboard-slot",
|
|
26
|
-
"path": "peer-management"
|
|
31
|
+
"fullWidth": false
|
|
27
32
|
}
|
|
28
33
|
},
|
|
29
34
|
{
|
|
@@ -44,6 +49,18 @@
|
|
|
44
49
|
"component": "dischargeBodyForm",
|
|
45
50
|
"title": "discharge body form",
|
|
46
51
|
"type": "other-form"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "admit-body-form",
|
|
55
|
+
"component": "admitBodyForm",
|
|
56
|
+
"title": "Admission form",
|
|
57
|
+
"type": "other-form"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "swap-unit-form",
|
|
61
|
+
"component": "swapForm",
|
|
62
|
+
"title": "Swap form",
|
|
63
|
+
"type": "other-form"
|
|
47
64
|
}
|
|
48
65
|
],
|
|
49
66
|
"pages": [
|
|
@@ -3,50 +3,46 @@ import { useTranslation } from 'react-i18next';
|
|
|
3
3
|
import DeceasedFilter from '../header/admitted-queue-header.component';
|
|
4
4
|
import styles from './admitted-queue.scss';
|
|
5
5
|
import CompartmentView from '../card/compartment-view.compartment';
|
|
6
|
-
import {
|
|
6
|
+
import { useAdmissionLocation } from '../hook/useMortuaryAdmissionLocation';
|
|
7
7
|
import { InlineLoading } from '@carbon/react';
|
|
8
8
|
import { CardHeader, ErrorState } from '@openmrs/esm-patient-common-lib';
|
|
9
9
|
import EmptyMorgueAdmission from '../empty-state/empty-morgue-admission.component';
|
|
10
|
+
import useEmrConfiguration from '../hook/useAdmitPatient';
|
|
11
|
+
import { useDischargedPatient } from '../hook/useDischargedPatient';
|
|
10
12
|
|
|
11
13
|
export const AdmittedQueue: React.FC = () => {
|
|
12
|
-
const { data: deceasedPatients, error, isLoading } = useDeceasedPatient();
|
|
13
14
|
const { t } = useTranslation();
|
|
14
15
|
const [searchQuery, setSearchQuery] = useState('');
|
|
15
|
-
|
|
16
|
+
const { admissionLocation, isLoading, error } = useAdmissionLocation();
|
|
16
17
|
const handleSearchChange = (query: string) => {
|
|
17
18
|
setSearchQuery(query);
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const admittedPatients = deceasedPatients?.filter((patient) => patient.status === 'admitted') || [];
|
|
21
21
|
if (isLoading) {
|
|
22
|
-
return (
|
|
23
|
-
<InlineLoading
|
|
24
|
-
status="active"
|
|
25
|
-
iconDescription="Loading"
|
|
26
|
-
description={t('pullingCompartment', 'Pulling compartments data.....')}
|
|
27
|
-
/>
|
|
28
|
-
);
|
|
22
|
+
return <InlineLoading description={t('loading', 'Loading...')} />;
|
|
29
23
|
}
|
|
30
24
|
|
|
31
25
|
if (error) {
|
|
32
|
-
return <ErrorState
|
|
26
|
+
return <ErrorState headerTitle={t('errorLoadingData', 'Error loading data')} error={error} />;
|
|
33
27
|
}
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
|
|
38
|
-
title={t('allocations', 'Allocation')}
|
|
39
|
-
subTitle={t('noAdmittedBodies', 'There are no admitted bodies')}
|
|
40
|
-
/>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
29
|
+
const filteredBedLayouts = admissionLocation?.bedLayouts?.filter((bed) => {
|
|
30
|
+
return bed.patients.some((patient) => patient?.person?.display?.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
31
|
+
});
|
|
43
32
|
|
|
44
33
|
return (
|
|
45
34
|
<div className={styles.layoutWrapper}>
|
|
46
35
|
<CardHeader title={t('allocation', 'Allocation')} children={''} />
|
|
47
36
|
<DeceasedFilter onSearchChange={handleSearchChange} />
|
|
48
37
|
<div className={styles.patientCardContainer}>
|
|
49
|
-
|
|
38
|
+
{filteredBedLayouts?.length === 0 ? (
|
|
39
|
+
<EmptyMorgueAdmission
|
|
40
|
+
title={t('allocations', 'Allocation')}
|
|
41
|
+
subTitle={t('noAdmittedBodies', 'There are no admitted bodies matching your search')}
|
|
42
|
+
/>
|
|
43
|
+
) : (
|
|
44
|
+
<CompartmentView />
|
|
45
|
+
)}
|
|
50
46
|
</div>
|
|
51
47
|
</div>
|
|
52
48
|
);
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
import { DataTableSkeleton } from '@carbon/react';
|
|
2
|
+
import { ConfigurableLink, ErrorState, formatDate, useConfig } from '@openmrs/esm-framework';
|
|
1
3
|
import React from 'react';
|
|
2
|
-
import GenericTable from './generic-table.component';
|
|
3
|
-
import { ConfigurableLink, ErrorState, formatDate, launchWorkspace } from '@openmrs/esm-framework';
|
|
4
|
-
import { toUpperCase } from '../helpers/expression-helper';
|
|
5
|
-
import { Tag, Button, DataTableSkeleton, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
6
|
-
import styles from './generic-table.scss';
|
|
7
4
|
import { useTranslation } from 'react-i18next';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
5
|
+
import styles from './generic-table.scss';
|
|
6
|
+
import GenericTable from './generic-table.component';
|
|
7
|
+
import { ConfigObject } from '../config-schema';
|
|
8
|
+
import usePatients from '../hook/useDischargedPatient';
|
|
10
9
|
|
|
11
10
|
interface DischargedProps {
|
|
11
|
+
dischargedPatientUuids: string[];
|
|
12
12
|
isLoading: boolean;
|
|
13
|
-
deceasedPatients: any;
|
|
14
13
|
error: any;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
export const DischargedBodies: React.FC<DischargedProps> = ({ isLoading,
|
|
16
|
+
export const DischargedBodies: React.FC<DischargedProps> = ({ isLoading, error, dischargedPatientUuids }) => {
|
|
18
17
|
const { t } = useTranslation();
|
|
19
18
|
const dischargedInLine = t('dischargeBodies', 'Discharged bodies');
|
|
20
19
|
const patientChartUrl = '${openmrsSpaBase}/patient/${patientUuid}/chart/deceased-panel';
|
|
20
|
+
const { patients, isLoading: isLoadingPatients, error: patientsError } = usePatients(dischargedPatientUuids);
|
|
21
|
+
|
|
22
|
+
const { nextOfKinAddressUuid, nextOfKinNameUuid, nextOfKinPhoneUuid, nextOfKinRelationshipUuid } =
|
|
23
|
+
useConfig<ConfigObject>();
|
|
21
24
|
|
|
22
25
|
const genericTableHeader = [
|
|
23
26
|
{ header: 'Patient Name', key: 'name' },
|
|
@@ -26,7 +29,6 @@ export const DischargedBodies: React.FC<DischargedProps> = ({ isLoading, decease
|
|
|
26
29
|
{ header: 'Age', key: 'age' },
|
|
27
30
|
{ header: 'Date of Death', key: 'deathDate' },
|
|
28
31
|
{ header: 'Cause of Death', key: 'causeOfDeath' },
|
|
29
|
-
{ header: 'Status', key: 'status' },
|
|
30
32
|
];
|
|
31
33
|
|
|
32
34
|
if (isLoading) {
|
|
@@ -49,33 +51,37 @@ export const DischargedBodies: React.FC<DischargedProps> = ({ isLoading, decease
|
|
|
49
51
|
return <ErrorState error={error} headerTitle={t('dischargeBodies', 'Discharged bodies')} />;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
const
|
|
54
|
+
const getAttributeValue = (patient, uuid) => {
|
|
55
|
+
return patient?.person?.attributes?.find((attr) => attr?.attributeType?.uuid === uuid)?.value || '--';
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const rows = patients?.map((patient) => {
|
|
59
|
+
const openmrsID = patient?.identifiers?.find((id) => id.display.startsWith('OpenMRS ID'))?.display.split(' = ')[1];
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
patient
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
const nextOfKin = {
|
|
62
|
+
name: getAttributeValue(patient, nextOfKinNameUuid),
|
|
63
|
+
phone: getAttributeValue(patient, nextOfKinPhoneUuid),
|
|
64
|
+
address: getAttributeValue(patient, nextOfKinAddressUuid),
|
|
65
|
+
relationship: getAttributeValue(patient, nextOfKinRelationshipUuid),
|
|
66
|
+
};
|
|
60
67
|
|
|
61
68
|
return {
|
|
62
|
-
id:
|
|
69
|
+
id: patient?.uuid,
|
|
63
70
|
name: (
|
|
64
71
|
<ConfigurableLink
|
|
65
72
|
style={{ textDecoration: 'none', maxWidth: '50%' }}
|
|
66
73
|
to={patientChartUrl}
|
|
67
|
-
templateParams={{ patientUuid: patient?.
|
|
68
|
-
{patient?.person?.
|
|
74
|
+
templateParams={{ patientUuid: patient?.uuid }}>
|
|
75
|
+
{patient?.person?.display?.toUpperCase()}
|
|
69
76
|
</ConfigurableLink>
|
|
70
77
|
),
|
|
71
|
-
gender: patient?.person?.
|
|
72
|
-
age: patient?.person?.
|
|
73
|
-
identifier:
|
|
74
|
-
deathDate:
|
|
75
|
-
causeOfDeath: patient?.person?.
|
|
76
|
-
|
|
78
|
+
gender: patient?.person?.gender || '--',
|
|
79
|
+
age: patient?.person?.age || '--',
|
|
80
|
+
identifier: openmrsID || '--',
|
|
81
|
+
deathDate: formatDate(new Date(patient?.person?.deathDate)) || '--',
|
|
82
|
+
causeOfDeath: patient?.person?.causeOfDeath?.display || '--',
|
|
83
|
+
nextOfKin: nextOfKin, // Ensure nextOfKin is included in the row
|
|
77
84
|
};
|
|
78
85
|
});
|
|
79
|
-
|
|
80
86
|
return <GenericTable rows={rows} headers={genericTableHeader} title={dischargedInLine} />;
|
|
81
87
|
};
|
|
@@ -10,12 +10,17 @@ import {
|
|
|
10
10
|
TableHead,
|
|
11
11
|
TableHeader,
|
|
12
12
|
TableRow,
|
|
13
|
+
TableExpandRow,
|
|
14
|
+
TableExpandedRow,
|
|
13
15
|
Search,
|
|
16
|
+
TableExpandHeader,
|
|
14
17
|
} from '@carbon/react';
|
|
15
18
|
import { CardHeader, PatientChartPagination } from '@openmrs/esm-patient-common-lib';
|
|
16
19
|
import { useLayoutType, usePagination } from '@openmrs/esm-framework';
|
|
17
20
|
import styles from './generic-table.scss';
|
|
18
21
|
import EmptyDeceasedSearch from '../empty-state/empty-morgue-admission.component';
|
|
22
|
+
import { Patient } from '../types';
|
|
23
|
+
import NextOfKinDetails from '../component/next-of-kin-details/nextOfKinDetails.component';
|
|
19
24
|
|
|
20
25
|
interface GenericTableProps {
|
|
21
26
|
rows: any[];
|
|
@@ -31,7 +36,7 @@ const GenericTable: React.FC<GenericTableProps> = ({ rows, headers, actionColumn
|
|
|
31
36
|
|
|
32
37
|
const [searchTerm, setSearchTerm] = useState('');
|
|
33
38
|
|
|
34
|
-
const filteredRows = rows
|
|
39
|
+
const filteredRows = rows?.filter((row) =>
|
|
35
40
|
headers.some((header) => row[header.key]?.toString().toLowerCase().includes(searchTerm.toLowerCase())),
|
|
36
41
|
);
|
|
37
42
|
|
|
@@ -53,12 +58,12 @@ const GenericTable: React.FC<GenericTableProps> = ({ rows, headers, actionColumn
|
|
|
53
58
|
/>
|
|
54
59
|
</div>
|
|
55
60
|
|
|
56
|
-
{rows
|
|
61
|
+
{rows?.length === 0 ? (
|
|
57
62
|
<EmptyDeceasedSearch
|
|
58
63
|
title={t('noWaitingList', 'Waiting List')}
|
|
59
64
|
subTitle={t('noDeceasedPersons', 'There are no deceased persons on the waiting list')}
|
|
60
65
|
/>
|
|
61
|
-
) : searchTerm && filteredRows
|
|
66
|
+
) : searchTerm && filteredRows?.length === 0 ? (
|
|
62
67
|
<EmptyDeceasedSearch
|
|
63
68
|
title={t('noResultFound', 'Sorry, no results found')}
|
|
64
69
|
subTitle={t('searchForADeceased', "Try to search again using the deceased patient's unique ID number")}
|
|
@@ -66,18 +71,25 @@ const GenericTable: React.FC<GenericTableProps> = ({ rows, headers, actionColumn
|
|
|
66
71
|
) : (
|
|
67
72
|
<div className={styles.genericTable}>
|
|
68
73
|
<DataTable rows={paginatedData} headers={headers} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
|
|
69
|
-
{({
|
|
70
|
-
|
|
74
|
+
{({
|
|
75
|
+
rows: tableRows,
|
|
76
|
+
headers,
|
|
77
|
+
getHeaderProps,
|
|
78
|
+
getTableProps,
|
|
79
|
+
getRowProps,
|
|
80
|
+
getTableContainerProps,
|
|
81
|
+
getExpandHeaderProps,
|
|
82
|
+
}) => (
|
|
83
|
+
<TableContainer {...getTableContainerProps()} className={styles.tableContainer}>
|
|
71
84
|
<Table {...getTableProps()}>
|
|
72
85
|
<TableHead>
|
|
73
86
|
<TableRow>
|
|
74
|
-
{
|
|
87
|
+
<TableExpandHeader enableToggle {...getExpandHeaderProps()} />
|
|
88
|
+
{headers?.map((header, i) => (
|
|
75
89
|
<TableHeader
|
|
76
|
-
key={
|
|
77
|
-
className={classNames(styles.productiveHeading01, styles.text02)}
|
|
90
|
+
key={i}
|
|
78
91
|
{...getHeaderProps({
|
|
79
92
|
header,
|
|
80
|
-
isSortable: header.isSortable,
|
|
81
93
|
})}>
|
|
82
94
|
{header.header}
|
|
83
95
|
</TableHeader>
|
|
@@ -86,22 +98,35 @@ const GenericTable: React.FC<GenericTableProps> = ({ rows, headers, actionColumn
|
|
|
86
98
|
</TableRow>
|
|
87
99
|
</TableHead>
|
|
88
100
|
<TableBody>
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
{tableRows?.map((row, i) => {
|
|
102
|
+
const originalRow = rows.find((r) => r.id === row.id);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<React.Fragment key={row.id}>
|
|
106
|
+
<TableExpandRow {...getRowProps({ row })}>
|
|
107
|
+
{row.cells.map((cell) => (
|
|
108
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
109
|
+
))}
|
|
110
|
+
{actionColumn && <TableCell>{actionColumn(row)}</TableCell>}
|
|
111
|
+
</TableExpandRow>
|
|
112
|
+
{row.isExpanded && (
|
|
113
|
+
<TableExpandedRow colSpan={headers.length + 1}>
|
|
114
|
+
<div className={styles.expandedRowContent}>
|
|
115
|
+
{originalRow && <NextOfKinDetails nextOfKin={originalRow.nextOfKin} />}
|
|
116
|
+
</div>
|
|
117
|
+
</TableExpandedRow>
|
|
118
|
+
)}
|
|
119
|
+
</React.Fragment>
|
|
120
|
+
);
|
|
121
|
+
})}
|
|
97
122
|
</TableBody>
|
|
98
123
|
</Table>
|
|
99
124
|
</TableContainer>
|
|
100
125
|
)}
|
|
101
126
|
</DataTable>
|
|
102
127
|
<PatientChartPagination
|
|
103
|
-
currentItems={paginatedData
|
|
104
|
-
totalItems={filteredRows
|
|
128
|
+
currentItems={paginatedData?.length}
|
|
129
|
+
totalItems={filteredRows?.length}
|
|
105
130
|
onPageNumberChange={({ page }) => goTo(page)}
|
|
106
131
|
pageNumber={currentPage}
|
|
107
132
|
pageSize={pageSize}
|