@kenyaemr/esm-morgue-app 5.3.8-pre.1562 → 5.3.8-pre.1570

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 (54) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/dist/300.js +1 -1
  3. package/dist/38.js +1 -0
  4. package/dist/38.js.map +1 -0
  5. package/dist/553.js +1 -0
  6. package/dist/553.js.map +1 -0
  7. package/dist/570.js +1 -0
  8. package/dist/570.js.map +1 -0
  9. package/dist/632.js +1 -1
  10. package/dist/632.js.map +1 -1
  11. package/dist/687.js +2 -0
  12. package/dist/687.js.map +1 -0
  13. package/dist/759.js +1 -1
  14. package/dist/759.js.map +1 -1
  15. package/dist/864.js +1 -1
  16. package/dist/926.js +1 -1
  17. package/dist/926.js.map +1 -1
  18. package/dist/942.js +1 -1
  19. package/dist/942.js.map +1 -1
  20. package/dist/kenyaemr-esm-morgue-app.js +1 -1
  21. package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +129 -108
  22. package/dist/kenyaemr-esm-morgue-app.js.map +1 -1
  23. package/dist/main.js +1 -1
  24. package/dist/main.js.map +1 -1
  25. package/dist/routes.json +1 -1
  26. package/package.json +1 -1
  27. package/src/card/compartment-view.compartment.tsx +1 -1
  28. package/src/component/main.component.tsx +7 -0
  29. package/src/config-schema.ts +7 -1
  30. package/src/empty-state/{empty-search-deceased.component.tsx → empty-morgue-admission.component.tsx} +3 -3
  31. package/src/header/morgue-header.component.tsx +11 -15
  32. package/src/header/morgue-header.scss +34 -40
  33. package/src/hook/useMorgue.resource.ts +107 -19
  34. package/src/tables/admitted-queue.component.tsx +11 -4
  35. package/src/tables/discharge-queue.component.tsx +73 -0
  36. package/src/tables/generic-table.component.tsx +1 -1
  37. package/src/tables/waiting-queue.component.tsx +10 -12
  38. package/src/tabs/tabs.component.tsx +44 -26
  39. package/src/tabs/tabs.scss +1 -2
  40. package/src/types/index.ts +120 -0
  41. package/src/utils/utils.ts +23 -0
  42. package/src/workspaces/discharge-body.scss +13 -0
  43. package/src/workspaces/discharge-body.workspace.tsx +224 -24
  44. package/translations/en.json +4 -5
  45. package/dist/268.js +0 -2
  46. package/dist/268.js.map +0 -1
  47. package/dist/340.js +0 -1
  48. package/dist/340.js.map +0 -1
  49. package/dist/701.js +0 -1
  50. package/dist/701.js.map +0 -1
  51. package/dist/809.js +0 -1
  52. package/dist/809.js.map +0 -1
  53. /package/dist/{268.js.LICENSE.txt → 687.js.LICENSE.txt} +0 -0
  54. /package/src/empty-state/{empty-deceased.scss → empty-morgue-admission.scss} +0 -0
@@ -2,9 +2,16 @@ import React from 'react';
2
2
  import { MorgueHeader } from '../header/morgue-header.component';
3
3
  import { MorgueTabs } from '../tabs/tabs.component';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import { useDeceasedPatient } from '../hook/useMorgue.resource';
5
6
 
6
7
  const MainComponent: React.FC = () => {
7
8
  const { t } = useTranslation();
9
+ const { data: deceasedPatients, isLoading } = useDeceasedPatient();
10
+
11
+ const awaitingCount = isLoading ? null : deceasedPatients?.filter((p) => p.status === 'awaiting').length || 0;
12
+ const admittedCount = isLoading ? null : deceasedPatients?.filter((p) => p.status === 'admitted').length || 0;
13
+ const dischargedCount = isLoading ? null : deceasedPatients?.filter((p) => p.status === 'discharged').length || 0;
14
+
8
15
  return (
9
16
  <div className={`omrs-main-content`}>
10
17
  <MorgueHeader title={t('mortuary', 'Mortuary')} />
@@ -46,7 +46,7 @@ export const configSchema = {
46
46
  morgueDischargeEncounterTypeUuid: {
47
47
  type: Type.String,
48
48
  _description: 'Encounter type for morgue discharge',
49
- _default: ' d618f40b-b5a3-4f17-81c8-2f04e2aad58e',
49
+ _default: 'd618f40b-b5a3-4f17-81c8-2f04e2aad58e',
50
50
  },
51
51
  visitPaymentMethodAttributeUuid: {
52
52
  _type: Type.String,
@@ -83,6 +83,11 @@ export const configSchema = {
83
83
  _description: 'UUID for discharge area concept',
84
84
  _default: 'a458fd00-199e-4d08-ad5c-1be322a6ccbe',
85
85
  },
86
+ adminUuid: {
87
+ _type: Type.String,
88
+ _description: 'UUID for admin user',
89
+ _default: 'e02c40e5-04e7-11e5-ae3c-a0b3cc4f922f',
90
+ },
86
91
  };
87
92
 
88
93
  export interface BillingConfig {
@@ -127,4 +132,5 @@ export type ConfigObject = {
127
132
  policeIDNumber: string;
128
133
  dischargeAreaUuid: string;
129
134
  morgueDischargeEncounterTypeUuid: string;
135
+ adminUuid: string;
130
136
  };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
- import styles from './empty-deceased.scss';
3
+ import styles from './empty-morgue-admission.scss';
4
4
  import { EmptyDataIllustration } from '@openmrs/esm-patient-common-lib';
5
5
  import { DocumentUnknown, IbmWatsonKnowledgeStudio } from '@carbon/react/icons';
6
6
 
@@ -9,7 +9,7 @@ interface EmptyDeceasedSearchProps {
9
9
  subTitle: string;
10
10
  }
11
11
 
12
- const EmptyDeceasedSearch: React.FC<EmptyDeceasedSearchProps> = ({ title, subTitle }) => {
12
+ const EmptyMorgueAdmission: React.FC<EmptyDeceasedSearchProps> = ({ title, subTitle }) => {
13
13
  const { t } = useTranslation();
14
14
 
15
15
  return (
@@ -21,4 +21,4 @@ const EmptyDeceasedSearch: React.FC<EmptyDeceasedSearchProps> = ({ title, subTit
21
21
  );
22
22
  };
23
23
 
24
- export default EmptyDeceasedSearch;
24
+ export default EmptyMorgueAdmission;
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
- import { Calendar, Location } from '@carbon/react/icons';
3
+ import { Calendar, Location, UserFollow } from '@carbon/react/icons';
4
4
  import { useSession, formatDate } from '@openmrs/esm-framework';
5
5
  import styles from './morgue-header.scss';
6
6
  import MorgueIllustration from './morgue-illustration.component';
7
+ import { InlineLoading } from '@carbon/react';
7
8
 
8
9
  interface MorgueHeaderProps {
9
10
  title: string;
@@ -11,8 +12,8 @@ interface MorgueHeaderProps {
11
12
 
12
13
  export const MorgueHeader: React.FC<MorgueHeaderProps> = ({ title }) => {
13
14
  const { t } = useTranslation();
14
- const userSession = useSession();
15
- const userLocation = userSession?.sessionLocation?.display;
15
+ const session = useSession();
16
+ const location = session?.sessionLocation?.display;
16
17
 
17
18
  return (
18
19
  <div className={styles.header}>
@@ -23,18 +24,13 @@ export const MorgueHeader: React.FC<MorgueHeaderProps> = ({ title }) => {
23
24
  <p>{t('mortuaryManagement', 'Mortuary management')}</p>
24
25
  </div>
25
26
  </div>
26
- <div className={styles.metrics}>
27
- <div className={styles.wrapMetrics}>
28
- <span className={styles.metricLabel}>{t('awaiting', 'Awaiting')}</span>
29
- <span className={styles.metricValue}>0</span>
30
- </div>
31
- <div className={styles.wrapMetrics}>
32
- <span className={styles.metricLabel}>{t('admittedOnes', 'Admitted')}</span>
33
- <span className={styles.metricValue}>0</span>
34
- </div>
35
- <div className={styles.wrapMetrics}>
36
- <span className={styles.metricLabel}>{t('discharges', 'Discharges')}</span>
37
- <span className={styles.metricValue}>0</span>
27
+ <div className={styles['right-justified-items']}>
28
+ <div className={styles['date-and-location']}>
29
+ <Location size={16} />
30
+ <span className={styles.value}>{location}</span>
31
+ <span className={styles.middot}>&middot;</span>
32
+ <Calendar size={16} />
33
+ <span className={styles.value}>{formatDate(new Date(), { mode: 'standard' })}</span>
38
34
  </div>
39
35
  </div>
40
36
  </div>
@@ -43,59 +43,53 @@ svg.iconOverrides {
43
43
  fill: var(--brand-03);
44
44
  }
45
45
 
46
- .metrics {
46
+ .left-justified-items {
47
47
  display: flex;
48
48
  flex-direction: row;
49
- justify-content: flex-end;
50
49
  align-items: center;
51
- gap: layout.$spacing-06;
52
- flex-wrap: wrap;
53
- padding: layout.$spacing-04 layout.$spacing-05 0 0;
54
- width: auto;
55
- height: auto;
50
+ cursor: pointer;
51
+ align-items: center;
56
52
  }
57
53
 
58
- .wrapMetrics {
59
- width: auto;
60
- height: auto;
61
- flex-grow: 1;
54
+ .right-justified-items {
55
+ @include type.type-style('body-compact-02');
62
56
  display: flex;
63
57
  flex-direction: column;
64
- justify-content: center;
65
- align-items: flex-start;
66
- gap: layout.$spacing-03;
67
- padding: 0;
68
- min-width: layout.$spacing-12;
58
+ justify-content: space-between;
69
59
  }
70
60
 
71
- .metricLabel {
72
- flex-grow: 0;
73
- font-size: layout.$spacing-04;
74
- line-height: 1.33;
75
- letter-spacing: 0.32px;
76
- text-align: left;
77
- white-space: nowrap;
78
- overflow: hidden;
79
- text-overflow: ellipsis;
61
+ .page-name {
62
+ @include type.type-style('heading-04');
80
63
  }
81
64
 
82
- .metricValue {
83
- flex-grow: 0;
84
- font-family: IBMPlexSans;
85
- font-size: layout.$spacing-06;
86
- line-height: 1.4;
87
- text-align: left;
88
- white-space: nowrap;
89
- overflow: hidden;
90
- text-overflow: ellipsis;
65
+ .page-labels {
66
+ margin: layout.$spacing-05;
67
+
68
+ p:first-of-type {
69
+ margin-bottom: layout.$spacing-02;
70
+ }
91
71
  }
92
72
 
93
- .metricLocationDate {
73
+ .date-and-location {
94
74
  display: flex;
95
- flex-direction: column;
96
- justify-content: flex-start;
97
- align-items: flex-start;
75
+ justify-content: flex-end;
76
+ align-items: center;
77
+ }
78
+
79
+ .userContainer {
80
+ display: flex;
81
+ justify-content: flex-end;
98
82
  gap: layout.$spacing-05;
99
- width: auto;
100
- max-width: 120px;
83
+ }
84
+
85
+ .value {
86
+ margin-left: layout.$spacing-02;
87
+ }
88
+
89
+ .middot {
90
+ margin: 0 layout.$spacing-03;
91
+ }
92
+
93
+ .view {
94
+ @include type.type-style('label-01');
101
95
  }
@@ -1,17 +1,33 @@
1
1
  import {
2
2
  FetchResponse,
3
3
  openmrsFetch,
4
+ openmrsObservableFetch,
4
5
  OpenmrsResource,
5
6
  restBaseUrl,
7
+ toDateObjectStrict,
8
+ toOmrsIsoString,
6
9
  useConfig,
7
10
  useOpenmrsPagination,
8
11
  } from '@openmrs/esm-framework';
9
- import { DeceasedPatientResponse, PaymentMethod, VisitTypeResponse, Location, Patient, Visit } from '../types';
12
+ import {
13
+ DeceasedPatientResponse,
14
+ PaymentMethod,
15
+ VisitTypeResponse,
16
+ Location,
17
+ Patient,
18
+ Visit,
19
+ UseVisitQueueEntries,
20
+ VisitQueueEntry,
21
+ MappedVisitQueueEntry,
22
+ UpdateVisitPayload,
23
+ PaginatedResponse,
24
+ } from '../types';
10
25
  import useSWR from 'swr';
11
26
  import { BillingConfig, ConfigObject } from '../config-schema';
12
27
  import { useEffect, useState } from 'react';
13
28
  import useSWRImmutable from 'swr/immutable';
14
29
  import { makeUrlUrl } from '../utils/utils';
30
+ import type { Observable } from 'rxjs';
15
31
 
16
32
  const getMorguePatientStatus = async (
17
33
  patientUuid: string,
@@ -19,7 +35,7 @@ const getMorguePatientStatus = async (
19
35
  morgueDischargeEncounter: string,
20
36
  ): Promise<'discharged' | 'admitted' | 'awaiting'> => {
21
37
  const customRepresentation = 'custom:(visitType:(uuid),startDatetime,stopDatetime,encounters:(encounterType:(uuid)))';
22
- const url = `${restBaseUrl}/visit?v=${customRepresentation}&includeInactive=false&patient=${patientUuid}&limit=1`;
38
+ const url = `${restBaseUrl}/visit?v=${customRepresentation}&patient=${patientUuid}&limit=1`;
23
39
  const response = await openmrsFetch<{
24
40
  results: Array<{
25
41
  visitType: { uuid: string };
@@ -38,6 +54,7 @@ const getMorguePatientStatus = async (
38
54
  );
39
55
  const isMorgueVisit = visit?.visitType?.uuid === morgueVisitTypeUuid;
40
56
  const isVisitActive = !(typeof visit?.stopDatetime === 'string');
57
+
41
58
  if (!isMorgueVisit) {
42
59
  return 'awaiting';
43
60
  }
@@ -46,28 +63,33 @@ const getMorguePatientStatus = async (
46
63
  }
47
64
  return 'admitted';
48
65
  };
49
- export const useDeceasedPatient = () => {
66
+ export const useDeceasedPatient = (searchTerm?: string) => {
50
67
  const { morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid } = useConfig<ConfigObject>();
51
68
  const [deceasedPatient, setDeceasedPatient] = useState([]);
52
69
  const [isLoadingStatus, setIsLoadingStatus] = useState(false);
53
70
  const [statusError, setStatusError] = useState();
54
71
  const customRepresentation =
55
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)))';
56
- const url = `${restBaseUrl}/morgue/patient?v=${customRepresentation}&dead=true`;
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);
57
80
 
58
- const { data, error, isLoading } = useSWRImmutable<{ data: DeceasedPatientResponse }>(url, openmrsFetch);
59
81
  useEffect(() => {
60
- if (data?.data?.results?.length) {
82
+ if (paginatedData?.length) {
61
83
  (async () => {
62
84
  try {
63
85
  setIsLoadingStatus(true);
64
86
  const status = await Promise.all(
65
- data.data.results.map((data) => {
87
+ paginatedData?.map((data) => {
66
88
  return getMorguePatientStatus(data?.uuid, morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid);
67
89
  }),
68
90
  );
69
91
 
70
- setDeceasedPatient(data.data.results.map((patient, index) => ({ ...patient, status: status[index] })));
92
+ setDeceasedPatient(paginatedData?.map((patient, index) => ({ ...patient, status: status[index] })));
71
93
  } catch (error) {
72
94
  setStatusError(error);
73
95
  } finally {
@@ -75,18 +97,13 @@ export const useDeceasedPatient = () => {
75
97
  }
76
98
  })();
77
99
  }
78
- }, [morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid, data]);
100
+ }, [morgueVisitTypeUuid, morgueDischargeEncounterTypeUuid, paginatedData]);
79
101
 
80
- return { data: deceasedPatient, error: error ?? statusError, isLoading: isLoading || isLoadingStatus };
81
- };
82
-
83
- export const usePatientPaginatedEncounters = (patientUuid: string) => {
84
- const customRepresentation =
85
- 'custom:(visitType:(uuid,name,display),uuid,encounters:(uuid,diagnoses:(uuid,display,rank,diagnosis),form:(uuid,display),encounterDatetime,orders:full,obs:(uuid,concept:(uuid,display,conceptClass:(uuid,display)),display,groupMembers:(uuid,concept:(uuid,display),value:(uuid,display),display),value,obsDatetime),encounterType:(uuid,display,viewPrivilege,editPrivilege),encounterProviders:(uuid,display,encounterRole:(uuid,display),provider:(uuid,person:(uuid,display)))),visitType:(uuid,name,display),startDatetime,stopDatetime,patient,attributes:(attributeType:ref,display,uuid,value)';
86
- const url = makeUrlUrl(`${restBaseUrl}/encounter?patient=${patientUuid}&v=full`);
87
-
88
- const paginatedEncounter = useOpenmrsPagination(url, 10);
89
- return paginatedEncounter;
102
+ return {
103
+ data: deceasedPatient,
104
+ error: paginatedError ?? statusError,
105
+ isLoading: isPaginatedLoading || isLoadingStatus,
106
+ };
90
107
  };
91
108
 
92
109
  export const useVisitType = () => {
@@ -177,3 +194,74 @@ const usePerson = (uuid: string) => {
177
194
  };
178
195
 
179
196
  export default usePerson;
197
+
198
+ export function useVisitQueueEntry(patientUuid, visitUuid): UseVisitQueueEntries {
199
+ const apiUrl = `${restBaseUrl}/visit-queue-entry?v=full&patient=${patientUuid}`;
200
+ const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: Array<VisitQueueEntry> } }, Error>(
201
+ apiUrl,
202
+ openmrsFetch,
203
+ );
204
+ const mapVisitQueueEntryProperties = (visitQueueEntry: VisitQueueEntry): MappedVisitQueueEntry => ({
205
+ id: visitQueueEntry.uuid,
206
+ name: visitQueueEntry.queueEntry.queue.display,
207
+ patientUuid: visitQueueEntry.queueEntry.patient.uuid,
208
+ priority:
209
+ visitQueueEntry.queueEntry.priority.display === 'Urgent'
210
+ ? 'Priority'
211
+ : visitQueueEntry.queueEntry.priority.display,
212
+ priorityUuid: visitQueueEntry.queueEntry.priority.uuid,
213
+ service: visitQueueEntry.queueEntry.queue?.display,
214
+ status: visitQueueEntry.queueEntry.status.display,
215
+ statusUuid: visitQueueEntry.queueEntry.status.uuid,
216
+ visitUuid: visitQueueEntry.visit?.uuid,
217
+ visitType: visitQueueEntry.visit?.visitType?.display,
218
+ queue: visitQueueEntry.queueEntry.queue,
219
+ queueEntryUuid: visitQueueEntry.queueEntry.uuid,
220
+ });
221
+
222
+ const mappedVisitQueueEntry =
223
+ data?.data?.results
224
+ ?.map(mapVisitQueueEntryProperties)
225
+ .filter((visitQueueEntry) => visitUuid !== undefined && visitUuid === visitQueueEntry.visitUuid)
226
+ .shift() ?? null;
227
+ return {
228
+ queueEntry: mappedVisitQueueEntry,
229
+ isLoading,
230
+ error: error,
231
+ isValidating,
232
+ mutate,
233
+ };
234
+ }
235
+
236
+ export function removeQueuedPatient(
237
+ queueUuid: string,
238
+ queueEntryUuid: string,
239
+ abortController: AbortController,
240
+ endedAt?: Date,
241
+ ) {
242
+ return openmrsFetch(`${restBaseUrl}/queue/${queueUuid}/entry/${queueEntryUuid}`, {
243
+ method: 'POST',
244
+ headers: {
245
+ 'Content-Type': 'application/json',
246
+ },
247
+ body: {
248
+ endedAt: toDateObjectStrict(toOmrsIsoString(endedAt) ?? toOmrsIsoString(new Date())),
249
+ },
250
+ signal: abortController.signal,
251
+ });
252
+ }
253
+
254
+ export function updateVisit(
255
+ uuid: string,
256
+ payload: UpdateVisitPayload,
257
+ abortController: AbortController,
258
+ ): Observable<any> {
259
+ return openmrsObservableFetch(`${restBaseUrl}/visit/${uuid}`, {
260
+ signal: abortController.signal,
261
+ method: 'POST',
262
+ headers: {
263
+ 'Content-type': 'application/json',
264
+ },
265
+ body: payload,
266
+ });
267
+ }
@@ -6,6 +6,7 @@ import CompartmentView from '../card/compartment-view.compartment';
6
6
  import { useDeceasedPatient } from '../hook/useMorgue.resource';
7
7
  import { InlineLoading } from '@carbon/react';
8
8
  import { CardHeader, ErrorState } from '@openmrs/esm-patient-common-lib';
9
+ import EmptyMorgueAdmission from '../empty-state/empty-morgue-admission.component';
9
10
 
10
11
  export const AdmittedQueue: React.FC = () => {
11
12
  const { data: deceasedPatients, error, isLoading } = useDeceasedPatient();
@@ -17,7 +18,6 @@ export const AdmittedQueue: React.FC = () => {
17
18
  };
18
19
 
19
20
  const admittedPatients = deceasedPatients?.filter((patient) => patient.status === 'admitted') || [];
20
-
21
21
  if (isLoading) {
22
22
  return (
23
23
  <InlineLoading
@@ -32,14 +32,21 @@ export const AdmittedQueue: React.FC = () => {
32
32
  return <ErrorState error={error} headerTitle={t('allocation', 'Allocation')} />;
33
33
  }
34
34
 
35
+ if (admittedPatients?.length === 0) {
36
+ return (
37
+ <EmptyMorgueAdmission
38
+ title={t('allocations', 'Allocation')}
39
+ subTitle={t('noAdmittedBodies', 'There are no admitted bodies')}
40
+ />
41
+ );
42
+ }
43
+
35
44
  return (
36
45
  <div className={styles.layoutWrapper}>
37
46
  <CardHeader title={t('allocation', 'Allocation')} children={''} />
38
47
  <DeceasedFilter onSearchChange={handleSearchChange} />
39
48
  <div className={styles.patientCardContainer}>
40
- <div className={styles.patientCardContainer}>
41
- <CompartmentView patientVisit={{ results: admittedPatients }} searchQuery={searchQuery} />
42
- </div>
49
+ <CompartmentView patientVisit={{ results: admittedPatients }} searchQuery={searchQuery} />
43
50
  </div>
44
51
  </div>
45
52
  );
@@ -0,0 +1,73 @@
1
+ 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
+ import { useTranslation } from 'react-i18next';
8
+ import { useDeceasedPatient } from '../hook/useMorgue.resource';
9
+ import { formatDateTime } from '../utils/utils';
10
+
11
+ interface DischargedProps {
12
+ isLoading: boolean;
13
+ deceasedPatients: any;
14
+ error: any;
15
+ }
16
+
17
+ export const DischargedBodies: React.FC<DischargedProps> = ({ isLoading, deceasedPatients, error }) => {
18
+ const { t } = useTranslation();
19
+ const dischargedInLine = t('dischargeBodies', 'Discharged bodies');
20
+ const patientChartUrl = '${openmrsSpaBase}/patient/${patientUuid}/chart/deceased-panel';
21
+
22
+ const genericTableHeader = [
23
+ { header: 'Patient Name', key: 'name' },
24
+ { header: 'Gender', key: 'gender' },
25
+ { header: 'Identifier', key: 'identifier' },
26
+ { header: 'Age', key: 'age' },
27
+ { header: 'Date of Death', key: 'deathDate' },
28
+ { header: 'Cause of Death', key: 'causeOfDeath' },
29
+ { header: 'Status', key: 'status' },
30
+ ];
31
+
32
+ if (isLoading) {
33
+ return (
34
+ <div className={styles.table}>
35
+ <DataTableSkeleton
36
+ headers={genericTableHeader}
37
+ aria-label="discharged-datatable"
38
+ showToolbar={false}
39
+ showHeader={false}
40
+ rowCount={10}
41
+ zebra
42
+ columnCount={7}
43
+ />
44
+ </div>
45
+ );
46
+ }
47
+
48
+ if (error) {
49
+ return <ErrorState error={error} headerTitle={t('dischargeBodies', 'Discharged bodies')} />;
50
+ }
51
+
52
+ const dischargedDeceased = deceasedPatients?.filter((patient) => patient?.status === 'discharged') || [];
53
+
54
+ const rows = dischargedDeceased.map((patient) => ({
55
+ id: patient.uuid,
56
+ name: (
57
+ <ConfigurableLink
58
+ style={{ textDecoration: 'none', maxWidth: '50%' }}
59
+ to={patientChartUrl}
60
+ templateParams={{ patientUuid: patient?.person?.uuid }}>
61
+ {patient.person.display?.toUpperCase()}
62
+ </ConfigurableLink>
63
+ ),
64
+ gender: patient.person.gender,
65
+ age: patient?.person?.age,
66
+ identifier: patient?.identifiers[0]?.identifier,
67
+ deathDate: new Date(patient.person.deathDate).toLocaleString(),
68
+ causeOfDeath: patient.person.causeOfDeath?.display,
69
+ status: <Tag type="magenta">{patient.status}</Tag>,
70
+ }));
71
+
72
+ return <GenericTable rows={rows} headers={genericTableHeader} title={dischargedInLine} />;
73
+ };
@@ -15,7 +15,7 @@ import {
15
15
  import { CardHeader, PatientChartPagination } from '@openmrs/esm-patient-common-lib';
16
16
  import { useLayoutType, usePagination } from '@openmrs/esm-framework';
17
17
  import styles from './generic-table.scss';
18
- import EmptyDeceasedSearch from '../empty-state/empty-search-deceased.component';
18
+ import EmptyDeceasedSearch from '../empty-state/empty-morgue-admission.component';
19
19
 
20
20
  interface GenericTableProps {
21
21
  rows: any[];
@@ -6,9 +6,15 @@ import { Tag, Button, DataTableSkeleton, OverflowMenu, OverflowMenuItem } from '
6
6
  import styles from './generic-table.scss';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { useDeceasedPatient } from '../hook/useMorgue.resource';
9
+ import { formatDateTime } from '../utils/utils';
9
10
 
10
- export const WaitingQueue: React.FC = () => {
11
- const { data: deceasedPatients, error, isLoading } = useDeceasedPatient();
11
+ interface WaitingQueueProps {
12
+ isLoading: boolean;
13
+ deceasedPatients: any;
14
+ error: any;
15
+ }
16
+
17
+ export const WaitingQueue: React.FC<WaitingQueueProps> = ({ isLoading, deceasedPatients, error }) => {
12
18
  const { t } = useTranslation();
13
19
  const waitingInLine = t('waitingInLine', 'Waiting In Line');
14
20
 
@@ -46,11 +52,11 @@ export const WaitingQueue: React.FC = () => {
46
52
 
47
53
  const rows = awaitingPatients.map((patient) => ({
48
54
  id: patient.uuid,
49
- name: toUpperCase(patient.person.display),
55
+ name: patient.person.display?.toUpperCase(),
50
56
  gender: patient.person.gender,
51
57
  age: patient?.person?.age,
52
58
  identifier: patient?.identifiers[0]?.identifier,
53
- deathDate: formatDate(new Date(patient.person.deathDate)),
59
+ deathDate: patient.person.deathDate ? new Date(patient.person.deathDate).toLocaleString() : t('nullDate', '--'),
54
60
  causeOfDeath: patient.person.causeOfDeath?.display,
55
61
  status: <Tag type="magenta">{patient.status}</Tag>,
56
62
  }));
@@ -62,17 +68,9 @@ export const WaitingQueue: React.FC = () => {
62
68
  });
63
69
  };
64
70
 
65
- const handleDischargeForm = (patientUuid: string) => {
66
- launchWorkspace('discharge-body-form', {
67
- workspaceTitle: t('dischargeForm', 'Discharge form'),
68
- patientUuid,
69
- });
70
- };
71
-
72
71
  const actionColumn = (row) => (
73
72
  <OverflowMenu size="sm" flipped>
74
73
  <OverflowMenuItem itemText={t('admit', 'Admit')} onClick={() => handleAdmissionForm(row.id)} />
75
- <OverflowMenuItem isDelete itemText={t('release', 'Release')} onClick={() => handleDischargeForm(row.id)} />
76
74
  </OverflowMenu>
77
75
  );
78
76
 
@@ -1,42 +1,60 @@
1
- import React, { useState } from 'react';
2
- import { Tabs, Tab, TabList, TabPanel, TabPanels, Button, Layer } from '@carbon/react';
1
+ import React from 'react';
2
+ import { Tabs, Tab, TabList, TabPanel, TabPanels, InlineLoading, Layer } from '@carbon/react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import styles from './tabs.scss';
5
5
  import { WaitingQueue } from '../tables/waiting-queue.component';
6
6
  import { AdmittedQueue } from '../tables/admitted-queue.component';
7
- import { launchWorkspace } from '@openmrs/esm-framework';
8
- import { Touch_1Filled } from '@carbon/react/icons';
7
+ import { useDeceasedPatient } from '../hook/useMorgue.resource';
8
+ import { DischargedBodies } from '../tables/discharge-queue.component';
9
9
 
10
10
  export const MorgueTabs: React.FC = () => {
11
11
  const { t } = useTranslation();
12
+ const { data: deceasedPatients, error, isLoading } = useDeceasedPatient();
13
+
14
+ const awaitingCount = deceasedPatients?.filter((p) => p.status === 'awaiting').length || 0;
15
+ const admittedCount = deceasedPatients?.filter((p) => p.status === 'admitted').length || 0;
16
+ const dischargedCount = deceasedPatients?.filter((p) => p.status === 'discharged').length || 0;
17
+
18
+ const getTabLabel = (baseLabel: string, count: number | null) => (
19
+ <span className={styles.tabLabel}>
20
+ {baseLabel} {isLoading ? '' : `(${count})`}
21
+ </span>
22
+ );
12
23
 
13
24
  const tabPanels = [
14
- { name: t('waitQueue', 'Waiting queue'), component: <WaitingQueue /> },
15
- { name: t('admitted', 'Admitted'), component: <AdmittedQueue /> },
16
- { name: t('discharged', 'Discharged'), component: '' },
25
+ {
26
+ name: getTabLabel(t('waitQueue', 'Waiting queue'), awaitingCount),
27
+ component: <WaitingQueue isLoading={isLoading} deceasedPatients={deceasedPatients} error={error} />,
28
+ },
29
+ {
30
+ name: getTabLabel(t('admitted', 'Admitted'), admittedCount),
31
+ component: <AdmittedQueue />,
32
+ },
33
+ {
34
+ name: getTabLabel(t('discharged', 'Discharged'), dischargedCount),
35
+ component: <DischargedBodies isLoading={isLoading} deceasedPatients={deceasedPatients} error={error} />,
36
+ },
17
37
  ];
18
38
 
19
39
  return (
20
- <>
21
- <div className={styles.referralsList} data-testid="referralsList-list">
22
- <Tabs selected={0} role="navigation">
23
- <div className={styles.tabsContainer}>
24
- <TabList aria-label="Content Switcher as Tabs" contained>
25
- {tabPanels.map((tab, index) => (
26
- <Tab key={index}>{tab.name}</Tab>
27
- ))}
28
- </TabList>
29
- </div>
30
-
31
- <TabPanels>
40
+ <div className={styles.referralsList} data-testid="referralsList-list">
41
+ <Tabs selected={0} role="navigation">
42
+ <div className={styles.tabsContainer}>
43
+ <TabList aria-label="Content Switcher as Tabs" contained>
32
44
  {tabPanels.map((tab, index) => (
33
- <TabPanel key={index}>
34
- <Layer>{tab.component}</Layer>
35
- </TabPanel>
45
+ <Tab key={index}>{tab.name}</Tab>
36
46
  ))}
37
- </TabPanels>
38
- </Tabs>
39
- </div>
40
- </>
47
+ </TabList>
48
+ </div>
49
+
50
+ <TabPanels>
51
+ {tabPanels.map((tab, index) => (
52
+ <TabPanel key={index}>
53
+ <Layer>{tab.component}</Layer>
54
+ </TabPanel>
55
+ ))}
56
+ </TabPanels>
57
+ </Tabs>
58
+ </div>
41
59
  );
42
60
  };