@openmrs/esm-ward-app 9.2.1-pre.7387 → 9.2.1-pre.7405

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.
@@ -21,6 +21,7 @@ import type {
21
21
  WardPatientGroupDetails,
22
22
  } from '../types';
23
23
  import { useTranslation } from 'react-i18next';
24
+ import { filterFemale, filterNewborns, filterReproductiveAge } from '../utils';
24
25
 
25
26
  // the server side has 2 slightly incompatible types for Bed
26
27
  export function bedLayoutToBed(bedLayout: BedLayout): Bed {
@@ -46,14 +47,25 @@ export function filterBeds(admissionLocation: AdmissionLocationFetchResponse): B
46
47
  return bedLayouts;
47
48
  }
48
49
 
49
- export function getWardMetrics(bedLayouts: BedLayout[], wardPatientGroup: WardPatientGroupDetails): WardMetrics {
50
- const patients = wardPatientGroup?.totalPatientsCount ?? 0;
51
- const totalBeds = bedLayouts?.length ?? 0;
52
- const occupiedBeds = bedLayouts?.filter((bed) => bed.patients?.length > 0).length ?? 0;
50
+ export function getWardMetrics(wardPatientGroup: WardPatientGroupDetails): WardMetrics {
51
+ // pull all the patients out of the three constructs they are stored in: unadmitted but in a bed, admitted and in a bed, and admitted but not in a bed
52
+ const allPatients = [
53
+ ...wardPatientGroup.wardUnadmittedPatientsWithBed?.values(),
54
+ ...[...wardPatientGroup.wardAdmittedPatientsWithBed?.values()].map((admission) => admission.patient),
55
+ ...wardPatientGroup.wardUnassignedPatientsList?.map((admission) => admission.patient),
56
+ ];
57
+
58
+ const patientCount = allPatients?.length ?? 0;
59
+ const newborns = filterNewborns(allPatients)?.length ?? 0;
60
+ const femalesOfReproductiveAge = filterReproductiveAge(filterFemale(allPatients))?.length ?? 0;
61
+ const totalBeds = wardPatientGroup.bedLayouts?.length ?? 0;
62
+ const occupiedBeds = wardPatientGroup.bedLayouts?.filter((bed) => bed.patients?.length > 0).length ?? 0;
53
63
  return {
54
- patients: patients.toString(),
64
+ patients: patientCount.toString(),
55
65
  freeBeds: (totalBeds - occupiedBeds).toString(),
56
66
  totalBeds: totalBeds.toString(),
67
+ newborns: newborns.toString(), // used by maternal ward only
68
+ femalesOfReproductiveAge: femalesOfReproductiveAge.toString(), // used by maternal ward only
57
69
  };
58
70
  }
59
71
 
@@ -110,9 +122,6 @@ export function createAndGetWardPatientGrouping(
110
122
  );
111
123
  }) ?? [];
112
124
 
113
- //excluding inpatientRequests
114
- const totalPatientsCount = allWardPatientUuids.size;
115
-
116
125
  for (const inpatientRequest of inpatientRequests ?? []) {
117
126
  // TODO: inpatientRequest is undefined sometimes, why?
118
127
  if (inpatientRequest) {
@@ -127,7 +136,6 @@ export function createAndGetWardPatientGrouping(
127
136
  bedLayouts,
128
137
  wardUnassignedPatientsList,
129
138
  allWardPatientUuids,
130
- totalPatientsCount,
131
139
  inpatientAdmissionsByPatientUuid,
132
140
  };
133
141
  }
@@ -142,19 +150,10 @@ export function getWardMetricNameTranslation(name: string, t: TFunction) {
142
150
  return t('totalBeds', 'Total beds');
143
151
  case 'pendingOut':
144
152
  return t('pendingOut', 'Pending out');
145
- }
146
- }
147
-
148
- export function getWardMetricValueTranslation(name: string, t: TFunction, value: string) {
149
- switch (name) {
150
- case 'patients':
151
- return t('patientsMetricValue', '{{ metricValue }}', { metricValue: value });
152
- case 'freeBeds':
153
- return t('freeBedsMetricValue', '{{ metricValue }}', { metricValue: value });
154
- case 'totalBeds':
155
- return t('totalBedsMetricValue', '{{ metricValue }}', { metricValue: value });
156
- case 'pendingOut':
157
- return t('pendingOutMetricValue', '{{ metricValue }}', { metricValue: value });
153
+ case 'femalesOfReproductiveAge':
154
+ return t('mothers', 'Mothers');
155
+ case 'newborns':
156
+ return t('infants', 'Infants');
158
157
  }
159
158
  }
160
159
 
@@ -1,22 +1,22 @@
1
1
  import { showNotification, useAppContext, useFeatureFlag } from '@openmrs/esm-framework';
2
2
  import React from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
- import type { WardViewContext } from '../types';
5
- import {
6
- getWardMetricNameTranslation,
7
- getWardMetrics,
8
- getWardMetricValueTranslation,
9
- } from '../ward-view/ward-view.resource';
4
+ import { WardMetricType, type WardViewContext } from '../types';
5
+ import { getWardMetricNameTranslation, getWardMetrics } from '../ward-view/ward-view.resource';
10
6
  import WardMetric from './ward-metric.component';
11
- import styles from './ward-metrics.scss';
7
+ import styles from '../ward-view/ward-metrics.scss';
12
8
 
13
- const wardMetrics = [{ name: 'patients' }, { name: 'freeBeds' }, { name: 'totalBeds' }];
9
+ interface WardMetricsProps {
10
+ metrics?: WardMetricType[];
11
+ }
14
12
 
15
- const WardMetrics = () => {
13
+ const WardMetrics: React.FC<WardMetricsProps> = ({
14
+ metrics = [WardMetricType.PATIENTS, WardMetricType.FREE_BEDS, WardMetricType.TOTAL_BEDS, WardMetricType.PENDING_OUT],
15
+ }) => {
16
16
  const { t } = useTranslation();
17
17
  const isBedManagementModuleInstalled = useFeatureFlag('bedmanagement-module');
18
18
  const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
19
- const { admissionLocationResponse, inpatientAdmissionResponse, inpatientRequestResponse, bedLayouts } =
19
+ const { admissionLocationResponse, inpatientAdmissionResponse, inpatientRequestResponse } =
20
20
  wardPatientGroupDetails || {};
21
21
  const { isLoading, error } = admissionLocationResponse ?? {};
22
22
  const isDataLoading =
@@ -33,43 +33,64 @@ const WardMetrics = () => {
33
33
  });
34
34
  }
35
35
 
36
- const wardMetricValues = getWardMetrics(bedLayouts, wardPatientGroupDetails);
36
+ const wardMetricValues = getWardMetrics(wardPatientGroupDetails);
37
37
  return (
38
38
  <div className={styles.metricsContainer}>
39
- {isBedManagementModuleInstalled ? (
40
- wardMetrics.map((wardMetric) => {
41
- return (
42
- <WardMetric
43
- metricName={getWardMetricNameTranslation(wardMetric.name, t)}
44
- metricValue={getWardMetricValueTranslation(wardMetric.name, t, wardMetricValues[wardMetric.name])}
45
- isLoading={!!isLoading || !!isDataLoading}
46
- key={wardMetric.name}
47
- />
48
- );
49
- })
50
- ) : (
39
+ {metrics.includes(WardMetricType.PATIENTS) && (
51
40
  <WardMetric
52
41
  metricName={getWardMetricNameTranslation('patients', t)}
53
- metricValue={'--'}
42
+ metricValue={wardMetricValues['patients']}
54
43
  isLoading={false}
55
44
  key={'patients'}
56
45
  />
57
46
  )}
47
+ {metrics.includes(WardMetricType.FEMALE_OF_REPRODUCTIVE_AGE) &&
48
+ wardMetricValues['femalesOfReproductiveAge'] &&
49
+ wardMetricValues['femalesOfReproductiveAge'] !== '0' && (
50
+ <WardMetric
51
+ metricName={getWardMetricNameTranslation('femalesOfReproductiveAge', t)}
52
+ metricValue={wardMetricValues['femalesOfReproductiveAge']}
53
+ isLoading={!!isLoading || !!isDataLoading}
54
+ key={'femalesOfReproductiveAge'}
55
+ />
56
+ )}
57
+ {metrics.includes(WardMetricType.NEWBORNS) &&
58
+ wardMetricValues['newborns'] &&
59
+ wardMetricValues['newborns'] !== '0' && (
60
+ <WardMetric
61
+ metricName={getWardMetricNameTranslation('newborns', t)}
62
+ metricValue={wardMetricValues['newborns']}
63
+ isLoading={!!isLoading || !!isDataLoading}
64
+ key={'newborns'}
65
+ />
66
+ )}
58
67
  {isBedManagementModuleInstalled && (
59
- <WardMetric
60
- metricName={getWardMetricNameTranslation('pendingOut', t)}
61
- metricValue={
62
- error
63
- ? '--'
64
- : getWardMetricValueTranslation(
65
- 'pendingOut',
66
- t,
67
- wardPatientGroupDetails?.wardPatientPendingCount?.toString(),
68
- )
69
- }
70
- isLoading={!!isDataLoading}
71
- key="pending"
72
- />
68
+ <>
69
+ {metrics.includes(WardMetricType.FREE_BEDS) && (
70
+ <WardMetric
71
+ metricName={getWardMetricNameTranslation('freeBeds', t)}
72
+ metricValue={wardMetricValues['freeBeds']}
73
+ isLoading={!!isLoading || !!isDataLoading}
74
+ key={'freeBeds'}
75
+ />
76
+ )}
77
+ {metrics.includes(WardMetricType.TOTAL_BEDS) && (
78
+ <WardMetric
79
+ metricName={getWardMetricNameTranslation('totalBeds', t)}
80
+ metricValue={wardMetricValues['totalBeds']}
81
+ isLoading={!!isLoading || !!isDataLoading}
82
+ key={'totalBeds'}
83
+ />
84
+ )}
85
+ {metrics.includes(WardMetricType.PENDING_OUT) && (
86
+ <WardMetric
87
+ metricName={getWardMetricNameTranslation('pendingOut', t)}
88
+ metricValue={wardPatientGroupDetails?.wardPatientPendingCount?.toString()}
89
+ isLoading={!!isDataLoading}
90
+ key="pending"
91
+ />
92
+ )}
93
+ </>
73
94
  )}
74
95
  </div>
75
96
  );
@@ -1,29 +1,29 @@
1
1
  import { useAppContext } from '@openmrs/esm-framework';
2
2
  import { screen } from '@testing-library/react';
3
3
  import React from 'react';
4
- import { renderWithSwr } from '../../../../tools/test-utils';
4
+ import { renderWithSwr } from 'tools';
5
5
  import { mockWardViewContext } from '../../mock';
6
- import { type WardViewContext } from '../types';
7
- import { getWardMetrics } from '../ward-view/ward-view.resource';
6
+ import { WardMetricType, type WardViewContext } from '../types';
8
7
  import WardMetrics from './ward-metrics.component';
9
8
 
10
- const wardMetrics = [
11
- { name: 'patients', key: 'patients', defaultTranslation: 'Patients' },
12
- { name: 'freeBeds', key: 'freeBeds', defaultTranslation: 'Free beds' },
13
- { name: 'totalBeds', key: 'totalBeds', defaultTranslation: 'Total beds' },
14
- ];
15
-
16
9
  jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
17
10
 
18
11
  describe('Ward Metrics', () => {
19
- it('Should display metrics of in the ward ', () => {
20
- const mockWardPatientGroupDetails = mockWardViewContext.wardPatientGroupDetails;
21
- const { bedLayouts } = mockWardPatientGroupDetails;
22
- const bedMetrics = getWardMetrics(bedLayouts, mockWardPatientGroupDetails);
12
+ it('Should display standard metrics by default', () => {
23
13
  renderWithSwr(<WardMetrics />);
24
- for (let [key, value] of Object.entries(bedMetrics)) {
25
- const fieldName = wardMetrics.find((metric) => metric.name == key)?.defaultTranslation;
26
- expect(screen.getByText(fieldName!)).toBeInTheDocument();
27
- }
14
+ expect(screen.getByText('Patients')).toBeInTheDocument();
15
+ expect(screen.getByText('Free beds')).toBeInTheDocument();
16
+ expect(screen.getByText('Total beds')).toBeInTheDocument();
17
+ expect(screen.getByText('Pending out')).toBeInTheDocument();
18
+ expect(screen.queryByText('Mothers')).not.toBeInTheDocument();
19
+ expect(screen.queryByText('Infants')).not.toBeInTheDocument();
20
+ });
21
+ it('Should display extra metrics when configured', () => {
22
+ renderWithSwr(<WardMetrics metrics={[WardMetricType.FEMALE_OF_REPRODUCTIVE_AGE]} />);
23
+ expect(screen.getByText('Mothers')).toBeInTheDocument();
24
+ expect(screen.queryByText('Patients')).not.toBeInTheDocument();
25
+ expect(screen.queryByText('Free beds')).not.toBeInTheDocument();
26
+ expect(screen.queryByText('Total beds')).not.toBeInTheDocument();
27
+ expect(screen.queryByText('Pending out')).not.toBeInTheDocument();
28
28
  });
29
29
  });
@@ -2,19 +2,19 @@ import React, { type ReactNode } from 'react';
2
2
  import styles from './ward-view-header.scss';
3
3
  import AdmissionRequestsBar from './admission-requests-bar.component';
4
4
  import useWardLocation from '../hooks/useWardLocation';
5
- import WardMetrics from './ward-metrics.component';
6
5
 
7
6
  interface WardViewHeaderProps {
8
7
  wardPendingPatients: ReactNode;
8
+ wardMetrics: ReactNode;
9
9
  }
10
10
 
11
- const WardViewHeader: React.FC<WardViewHeaderProps> = ({ wardPendingPatients }) => {
11
+ const WardViewHeader: React.FC<WardViewHeaderProps> = ({ wardPendingPatients, wardMetrics }) => {
12
12
  const { location } = useWardLocation();
13
13
 
14
14
  return (
15
15
  <div className={styles.wardViewHeader}>
16
16
  <h4>{location?.display}</h4>
17
- <WardMetrics />
17
+ {wardMetrics}
18
18
  <AdmissionRequestsBar {...{ wardPendingPatients }} />
19
19
  </div>
20
20
  );
@@ -48,9 +48,9 @@
48
48
  "fetchingEmrConfigurationFailed": "Fetching EMR configuration failed. Try refreshing the page or contact your system administrator.",
49
49
  "fetchingPatientNotesFailed": "Fetching patient notes failed. Try refreshing the page or contact your system administrator.",
50
50
  "freeBeds": "Free beds",
51
- "freeBedsMetricValue": "{{ metricValue }}",
52
51
  "hours_one": "{{count}} hour",
53
52
  "hours_other": "{{count}} hours",
53
+ "infants": "Infants",
54
54
  "inpatientNotesWorkspaceTitle": "In-patient notes",
55
55
  "invalidLocationSpecified": "Invalid location specified",
56
56
  "invalidWardLocation": "Invalid ward location: {{location}}",
@@ -60,6 +60,7 @@
60
60
  "minutes_one": "{{count}} minutes",
61
61
  "minutes_other": "{{count}} minutes",
62
62
  "motherChildBedShare": "Mother / Child",
63
+ "mothers": "Mothers",
63
64
  "nextPage": "Next page",
64
65
  "noActiveVisit": "No active visit",
65
66
  "noAdmission": "No admission found",
@@ -91,7 +92,6 @@
91
92
  "patientNoteSaveError": "Error saving patient note",
92
93
  "patientNotesDidntLoad": "Patient notes didn't load",
93
94
  "patients": "Patients",
94
- "patientsMetricValue": "{{ metricValue }}",
95
95
  "patientTransferRequestCreated": "Patient transfer request created",
96
96
  "patientUnassignedFromBed": "Patient unassigned from bed",
97
97
  "patientUnassignedFromBedDetail": "{{patientName}} is now unassigned from bed",
@@ -99,7 +99,6 @@
99
99
  "pendingAdmissions": "Pending admissions",
100
100
  "pendingDischarge": "Pending Discharge",
101
101
  "pendingOut": "Pending out",
102
- "pendingOutMetricValue": "{{ metricValue }}",
103
102
  "pendingTransfer": "Pending Transfer",
104
103
  "pleaseSelectBed": "Please select a bed",
105
104
  "pleaseSelectTransferLocation": "Please select transfer location",
@@ -119,7 +118,6 @@
119
118
  "timeOnWard": "Time on this ward: {{timeOnWard}}",
120
119
  "timeSinceAdmission": "Admitted: {{timeSinceAdmission}}",
121
120
  "totalBeds": "Total beds",
122
- "totalBedsMetricValue": "{{ metricValue }}",
123
121
  "transfer": "Transfer",
124
122
  "transferElsewhere": "Transfer elsewhere",
125
123
  "transferPatient": "Transfer patient",