@openmrs/esm-ward-app 9.2.1-pre.7385 → 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.
Files changed (114) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/dist/1119.js +1 -1
  3. package/dist/1197.js +1 -1
  4. package/dist/1326.js +1 -1
  5. package/dist/1326.js.map +1 -1
  6. package/dist/2146.js +1 -1
  7. package/dist/2690.js +1 -1
  8. package/dist/3099.js +1 -1
  9. package/dist/3584.js +1 -1
  10. package/dist/380.js +1 -1
  11. package/dist/380.js.map +1 -1
  12. package/dist/4055.js +1 -1
  13. package/dist/4098.js +1 -0
  14. package/dist/4098.js.map +1 -0
  15. package/dist/4132.js +1 -1
  16. package/dist/4300.js +1 -1
  17. package/dist/4335.js +1 -1
  18. package/dist/439.js +1 -1
  19. package/dist/4618.js +1 -1
  20. package/dist/4652.js +1 -1
  21. package/dist/4944.js +1 -1
  22. package/dist/5173.js +1 -1
  23. package/dist/5241.js +1 -1
  24. package/dist/5442.js +1 -1
  25. package/dist/5661.js +1 -1
  26. package/dist/5885.js +1 -1
  27. package/dist/5885.js.map +1 -1
  28. package/dist/6022.js +1 -1
  29. package/dist/6468.js +1 -1
  30. package/dist/6589.js +1 -1
  31. package/dist/6679.js +1 -1
  32. package/dist/6840.js +1 -1
  33. package/dist/6859.js +1 -1
  34. package/dist/7097.js +1 -1
  35. package/dist/7159.js +1 -1
  36. package/dist/723.js +1 -1
  37. package/dist/7512.js +1 -1
  38. package/dist/7524.js +1 -1
  39. package/dist/7524.js.map +1 -1
  40. package/dist/7617.js +1 -1
  41. package/dist/795.js +1 -1
  42. package/dist/8163.js +1 -1
  43. package/dist/8349.js +1 -1
  44. package/dist/8371.js +1 -1
  45. package/dist/8618.js +1 -1
  46. package/dist/890.js +1 -1
  47. package/dist/9214.js +1 -1
  48. package/dist/9538.js +1 -1
  49. package/dist/9569.js +1 -1
  50. package/dist/9775.js +1 -1
  51. package/dist/9775.js.map +1 -1
  52. package/dist/986.js +1 -1
  53. package/dist/9879.js +1 -1
  54. package/dist/9895.js +1 -1
  55. package/dist/9900.js +1 -1
  56. package/dist/9913.js +1 -1
  57. package/dist/main.js +1 -1
  58. package/dist/openmrs-esm-ward-app.js.buildmanifest.json +166 -166
  59. package/dist/routes.json +1 -1
  60. package/package.json +1 -1
  61. package/src/types/index.ts +11 -0
  62. package/src/utils.ts +15 -0
  63. package/src/ward-view/default-ward/default-ward-view.component.tsx +3 -1
  64. package/src/ward-view/materal-ward/maternal-ward-view.component.tsx +15 -2
  65. package/src/ward-view/ward-view.resource.test.ts +140 -0
  66. package/src/ward-view/ward-view.resource.ts +21 -22
  67. package/src/ward-view-header/ward-metrics.component.tsx +59 -38
  68. package/src/ward-view-header/ward-metrics.test.tsx +17 -17
  69. package/src/ward-view-header/ward-view-header.component.tsx +3 -3
  70. package/translations/am.json +2 -2
  71. package/translations/ar.json +2 -2
  72. package/translations/ar_SY.json +2 -2
  73. package/translations/bn.json +2 -2
  74. package/translations/cs.json +2 -2
  75. package/translations/de.json +2 -2
  76. package/translations/en.json +2 -4
  77. package/translations/en_US.json +2 -2
  78. package/translations/es.json +2 -2
  79. package/translations/es_MX.json +2 -2
  80. package/translations/fr.json +2 -2
  81. package/translations/he.json +2 -2
  82. package/translations/hi.json +2 -2
  83. package/translations/hi_IN.json +2 -2
  84. package/translations/id.json +2 -2
  85. package/translations/it.json +2 -2
  86. package/translations/ka.json +2 -2
  87. package/translations/km.json +2 -2
  88. package/translations/ku.json +2 -2
  89. package/translations/ky.json +2 -2
  90. package/translations/lg.json +2 -2
  91. package/translations/ne.json +2 -2
  92. package/translations/pl.json +2 -2
  93. package/translations/pt.json +2 -2
  94. package/translations/pt_BR.json +2 -2
  95. package/translations/qu.json +2 -2
  96. package/translations/ro_RO.json +2 -2
  97. package/translations/ru_RU.json +2 -2
  98. package/translations/si.json +2 -2
  99. package/translations/sq.json +2 -2
  100. package/translations/sw.json +2 -2
  101. package/translations/sw_KE.json +2 -2
  102. package/translations/tr.json +2 -2
  103. package/translations/tr_TR.json +2 -2
  104. package/translations/uk.json +2 -2
  105. package/translations/uz.json +2 -2
  106. package/translations/uz@Latn.json +2 -2
  107. package/translations/uz_UZ.json +2 -2
  108. package/translations/vi.json +2 -2
  109. package/translations/zh.json +2 -2
  110. package/translations/zh_CN.json +2 -2
  111. package/translations/zh_TW.json +2 -2
  112. package/dist/8610.js +0 -1
  113. package/dist/8610.js.map +0 -1
  114. /package/src/{ward-view-header → ward-view}/ward-metrics.scss +0 -0
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":">=2.2.0","emrapi":">=2.0.0 <4.0.0"},"optionalBackendDependencies":{"bedmanagement":{"version":"^6.0.0 || 6.0.0-SNAPSHOT","feature":{"flagName":"bedmanagement-module","label":"Ward App Patient Service","description":"This module, if installed, provides services for managing patients admitted to the ward."}}},"extensions":[{"name":"ward-dashboard-link","component":"wardDashboardLink","slot":"homepage-dashboard-slot","meta":{"name":"ward","slot":"ward-dashboard-slot","title":"Wards"}},{"component":"root","name":"ward-dashboard","slot":"ward-dashboard-slot"},{"component":"wardView","name":"ward-view","slot":"ward-view-slot"},{"component":"wardPatientActionButtonExtension","name":"ward-patient-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"coloredObsTagCardRowExtension","name":"colored-obs-tags-card-row","slot":"ward-patient-card-slot"},{"name":"patient-discharge-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientDischargeWorkspaceSideRailIcon"},{"component":"defaultWardView","name":"default-ward","slot":"default-ward"},{"component":"maternalWardView","name":"maternal-ward","slot":"maternal-ward"},{"component":"wardPatientWorkspaceBanner","name":"ward-patient-workspace-banner","slot":"ward-workspace-patient-banner-slot"}],"workspaces2":[{"name":"admission-requests-workspace","component":"admissionRequestWorkspace","window":"pending-admissions"},{"name":"transfer-elsewhere-workspace","component":"patientTransferRequestWorkspace","window":"pending-admissions"},{"name":"cancel-admission-request-workspace","component":"cancelAdmissionRequestWorkspace","window":"pending-admissions"},{"name":"admit-patient-form-workspace","component":"admitPatientFormWorkspace","window":"pending-admissions"},{"name":"ward-app-patient-search-workspace","component":"@openmrs/esm-patient-search-app#patientSearchWorkspace2","window":"add-patient-to-ward"},{"name":"ward-app-start-visit-workspace","component":"@openmrs/esm-patient-chart-app#exportedVisitForm","window":"add-patient-to-ward"},{"name":"create-admission-encounter-workspace","component":"createAdmissionEncounterWorkspace","window":"add-patient-to-ward"},{"name":"ward-patient-workspace","component":"wardPatientWorkspace","window":"ward-patient"},{"name":"ward-patient-vitals-workspace","component":"@openmrs/esm-patient-vitals-app#exportedVitalsBiometricsFormWorkspace","window":"ward-patient"},{"name":"ward-patient-notes-workspace","component":"wardPatientNotesWorkspace","window":"ward-patient-notes"},{"name":"ward-patient-transfer-swap-workspace","component":"patientTransferAndSwapWorkspace","window":"ward-patient-transfer-swap"},{"name":"patient-discharge-workspace","component":"patientDischargeWorkspace","window":"ward-patient-discharge"},{"name":"ward-patient-clinical-forms-workspace","component":"@openmrs/esm-patient-forms-app#exportedClinicalFormsWorkspace","window":"ward-patient-clinical-forms"},{"name":"ward-patient-form-entry-workspace","component":"@openmrs/esm-patient-forms-app#exportedPatientFormEntryWorkspace","window":"ward-patient-clinical-forms"},{"name":"ward-patient-cancel-admission-request-workspace","component":"wardPatientCancelAdmissionRequestWorkspace","window":"ward-patient-cancel-admission-request"},{"name":"ward-patient-order-basket-workspace","component":"@openmrs/esm-patient-orders-app#exportedOrderBasketWorkspace","window":"ward-patient-order-basket"},{"name":"ward-patient-order-basket-add-drug-order-workspace","component":"@openmrs/esm-patient-medications-app#exportedAddDrugOrderWorkspace","window":"ward-patient-order-basket"},{"name":"ward-patient-order-basket-add-lab-order-workspace","component":"@openmrs/esm-patient-tests-app#exportedAddLabOrderWorkspace","window":"ward-patient-order-basket"},{"name":"ward-patient-order-basket-add-general-order-workspace","component":"@openmrs/esm-patient-orders-app#exportedOrderBasketWorkspace","window":"ward-patient-order-basket"}],"workspaceWindows2":[{"name":"pending-admissions","group":"ward-manage-admissions","icon":"admissionRequestsWorkspaceSideRailIcon","width":"wider"},{"name":"add-patient-to-ward","group":"ward-manage-admissions","icon":"createAdmissionEncounterWorkspaceSideRailIcon"},{"name":"ward-patient","group":"ward-patient","icon":"wardPatientActionButton","width":"wider"},{"name":"ward-patient-notes","group":"ward-patient","icon":"wardPatientNotesActionButton"},{"name":"ward-patient-transfer-swap","group":"ward-patient","icon":"patientTransferAndSwapWorkspaceSiderailIcon"},{"name":"ward-patient-discharge","group":"ward-patient","icon":"patientDischargeWorkspaceSideRailIcon"},{"name":"ward-patient-clinical-forms","group":"ward-patient","icon":"clinicalFormWorkspaceSideRailIcon"},{"name":"ward-patient-order-basket","group":"ward-patient","icon":"orderBasketWorkspaceSideRailIcon"},{"name":"ward-patient-cancel-admission-request","group":"ward-patient"}],"workspaceGroups2":[{"name":"ward-manage-admissions","overlay":true,"persistence":"closable"},{"name":"ward-patient","overlay":true,"persistence":"closable"}],"version":"9.2.1-pre.7385"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":">=2.2.0","emrapi":">=2.0.0 <4.0.0"},"optionalBackendDependencies":{"bedmanagement":{"version":"^6.0.0 || 6.0.0-SNAPSHOT","feature":{"flagName":"bedmanagement-module","label":"Ward App Patient Service","description":"This module, if installed, provides services for managing patients admitted to the ward."}}},"extensions":[{"name":"ward-dashboard-link","component":"wardDashboardLink","slot":"homepage-dashboard-slot","meta":{"name":"ward","slot":"ward-dashboard-slot","title":"Wards"}},{"component":"root","name":"ward-dashboard","slot":"ward-dashboard-slot"},{"component":"wardView","name":"ward-view","slot":"ward-view-slot"},{"component":"wardPatientActionButtonExtension","name":"ward-patient-action-button","slot":"action-menu-ward-patient-items-slot"},{"component":"coloredObsTagCardRowExtension","name":"colored-obs-tags-card-row","slot":"ward-patient-card-slot"},{"name":"patient-discharge-siderail-button","slot":"action-menu-ward-patient-items-slot","component":"patientDischargeWorkspaceSideRailIcon"},{"component":"defaultWardView","name":"default-ward","slot":"default-ward"},{"component":"maternalWardView","name":"maternal-ward","slot":"maternal-ward"},{"component":"wardPatientWorkspaceBanner","name":"ward-patient-workspace-banner","slot":"ward-workspace-patient-banner-slot"}],"workspaces2":[{"name":"admission-requests-workspace","component":"admissionRequestWorkspace","window":"pending-admissions"},{"name":"transfer-elsewhere-workspace","component":"patientTransferRequestWorkspace","window":"pending-admissions"},{"name":"cancel-admission-request-workspace","component":"cancelAdmissionRequestWorkspace","window":"pending-admissions"},{"name":"admit-patient-form-workspace","component":"admitPatientFormWorkspace","window":"pending-admissions"},{"name":"ward-app-patient-search-workspace","component":"@openmrs/esm-patient-search-app#patientSearchWorkspace2","window":"add-patient-to-ward"},{"name":"ward-app-start-visit-workspace","component":"@openmrs/esm-patient-chart-app#exportedVisitForm","window":"add-patient-to-ward"},{"name":"create-admission-encounter-workspace","component":"createAdmissionEncounterWorkspace","window":"add-patient-to-ward"},{"name":"ward-patient-workspace","component":"wardPatientWorkspace","window":"ward-patient"},{"name":"ward-patient-vitals-workspace","component":"@openmrs/esm-patient-vitals-app#exportedVitalsBiometricsFormWorkspace","window":"ward-patient"},{"name":"ward-patient-notes-workspace","component":"wardPatientNotesWorkspace","window":"ward-patient-notes"},{"name":"ward-patient-transfer-swap-workspace","component":"patientTransferAndSwapWorkspace","window":"ward-patient-transfer-swap"},{"name":"patient-discharge-workspace","component":"patientDischargeWorkspace","window":"ward-patient-discharge"},{"name":"ward-patient-clinical-forms-workspace","component":"@openmrs/esm-patient-forms-app#exportedClinicalFormsWorkspace","window":"ward-patient-clinical-forms"},{"name":"ward-patient-form-entry-workspace","component":"@openmrs/esm-patient-forms-app#exportedPatientFormEntryWorkspace","window":"ward-patient-clinical-forms"},{"name":"ward-patient-cancel-admission-request-workspace","component":"wardPatientCancelAdmissionRequestWorkspace","window":"ward-patient-cancel-admission-request"},{"name":"ward-patient-order-basket-workspace","component":"@openmrs/esm-patient-orders-app#exportedOrderBasketWorkspace","window":"ward-patient-order-basket"},{"name":"ward-patient-order-basket-add-drug-order-workspace","component":"@openmrs/esm-patient-medications-app#exportedAddDrugOrderWorkspace","window":"ward-patient-order-basket"},{"name":"ward-patient-order-basket-add-lab-order-workspace","component":"@openmrs/esm-patient-tests-app#exportedAddLabOrderWorkspace","window":"ward-patient-order-basket"},{"name":"ward-patient-order-basket-add-general-order-workspace","component":"@openmrs/esm-patient-orders-app#exportedOrderBasketWorkspace","window":"ward-patient-order-basket"}],"workspaceWindows2":[{"name":"pending-admissions","group":"ward-manage-admissions","icon":"admissionRequestsWorkspaceSideRailIcon","width":"wider"},{"name":"add-patient-to-ward","group":"ward-manage-admissions","icon":"createAdmissionEncounterWorkspaceSideRailIcon"},{"name":"ward-patient","group":"ward-patient","icon":"wardPatientActionButton","width":"wider"},{"name":"ward-patient-notes","group":"ward-patient","icon":"wardPatientNotesActionButton"},{"name":"ward-patient-transfer-swap","group":"ward-patient","icon":"patientTransferAndSwapWorkspaceSiderailIcon"},{"name":"ward-patient-discharge","group":"ward-patient","icon":"patientDischargeWorkspaceSideRailIcon"},{"name":"ward-patient-clinical-forms","group":"ward-patient","icon":"clinicalFormWorkspaceSideRailIcon"},{"name":"ward-patient-order-basket","group":"ward-patient","icon":"orderBasketWorkspaceSideRailIcon"},{"name":"ward-patient-cancel-admission-request","group":"ward-patient"}],"workspaceGroups2":[{"name":"ward-manage-admissions","overlay":true,"persistence":"closable"},{"name":"ward-patient","overlay":true,"persistence":"closable"}],"version":"9.2.1-pre.7405"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-ward-app",
3
- "version": "9.2.1-pre.7385",
3
+ "version": "9.2.1-pre.7405",
4
4
  "description": "Ward and bed management module for O3",
5
5
  "browser": "dist/openmrs-esm-ward-app.js",
6
6
  "main": "src/index.ts",
@@ -230,6 +230,17 @@ export interface WardMetrics {
230
230
  patients: string;
231
231
  freeBeds: string;
232
232
  totalBeds: string;
233
+ femalesOfReproductiveAge?: string; // used in Maternal Ward View
234
+ newborns?: string; // used in Maternal Ward View
235
+ }
236
+
237
+ export enum WardMetricType {
238
+ PATIENTS = 'patients',
239
+ FREE_BEDS = 'freeBeds',
240
+ TOTAL_BEDS = 'totalBeds',
241
+ PENDING_OUT = 'pendingOut',
242
+ FEMALE_OF_REPRODUCTIVE_AGE = 'femalesOfReproductiveAge',
243
+ NEWBORNS = 'newborns',
233
244
  }
234
245
 
235
246
  export interface EncounterPayload {
package/src/utils.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { type Patient } from '@openmrs/esm-framework';
2
+
3
+ // TODO: potentially move these rules into config so they can be customized and defined in a centralized place
4
+
5
+ export function filterNewborns(patients: Patient[]) {
6
+ return patients.filter((patient) => patient.person.age < 1);
7
+ }
8
+
9
+ export function filterReproductiveAge(patients: Patient[]) {
10
+ return patients.filter((patient) => patient.person.age >= 10);
11
+ }
12
+
13
+ export function filterFemale(patients: Patient[]) {
14
+ return patients.filter((patient) => patient.person.gender === 'F');
15
+ }
@@ -8,6 +8,7 @@ import DefaultWardPendingPatients from './default-ward-pending-patients.componen
8
8
  import DefaultWardUnassignedPatients from './default-ward-unassigned-patients.component';
9
9
  import Ward from '../ward.component';
10
10
  import WardViewHeader from '../../ward-view-header/ward-view-header.component';
11
+ import WardMetrics from '../../ward-view-header/ward-metrics.component';
11
12
 
12
13
  const DefaultWardView = () => {
13
14
  const wardPatientGroupDetails = useWardPatientGrouping();
@@ -17,12 +18,13 @@ const DefaultWardView = () => {
17
18
  });
18
19
 
19
20
  const wardBeds = <DefaultWardBeds />;
21
+ const wardMetrics = <WardMetrics />;
20
22
  const wardUnassignedPatients = <DefaultWardUnassignedPatients />;
21
23
  const wardPendingPatients = <DefaultWardPendingPatients />;
22
24
 
23
25
  return (
24
26
  <>
25
- <WardViewHeader {...{ wardPendingPatients }} />
27
+ <WardViewHeader {...{ wardPendingPatients, wardMetrics }} />
26
28
  <Ward {...{ wardBeds, wardUnassignedPatients }} />
27
29
  </>
28
30
  );
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { useDefineAppContext } from '@openmrs/esm-framework';
3
3
  import { useWardPatientGrouping } from '../../hooks/useWardPatientGrouping';
4
- import { type MaternalWardViewContext, type WardViewContext } from '../../types';
4
+ import { type MaternalWardViewContext, WardMetricType, type WardViewContext } from '../../types';
5
5
  import { useMotherChildrenRelationshipsByPatient } from './maternal-ward-view.resource';
6
6
  import MaternalWardBeds from './maternal-ward-beds.component';
7
7
  import MaternalWardPatientCardHeader from './maternal-ward-patient-card-header.component';
@@ -9,6 +9,7 @@ import MaternalWardPendingPatients from './maternal-ward-pending-patients.compon
9
9
  import MaternalWardUnassignedPatients from './maternal-ward-unassigned-patients.component';
10
10
  import Ward from '../ward.component';
11
11
  import WardViewHeader from '../../ward-view-header/ward-view-header.component';
12
+ import WardMetrics from '../../ward-view-header/ward-metrics.component';
12
13
 
13
14
  const MaternalWardView = () => {
14
15
  const wardPatientGroupDetails = useWardPatientGrouping();
@@ -24,12 +25,24 @@ const MaternalWardView = () => {
24
25
  });
25
26
 
26
27
  const wardBeds = <MaternalWardBeds {...motherChildRelationships} />;
28
+ const wardMetrics = (
29
+ <WardMetrics
30
+ metrics={[
31
+ WardMetricType.PATIENTS,
32
+ WardMetricType.FEMALE_OF_REPRODUCTIVE_AGE,
33
+ WardMetricType.NEWBORNS,
34
+ WardMetricType.FREE_BEDS,
35
+ WardMetricType.TOTAL_BEDS,
36
+ WardMetricType.PENDING_OUT,
37
+ ]}
38
+ />
39
+ );
27
40
  const wardUnassignedPatients = <MaternalWardUnassignedPatients />;
28
41
  const wardPendingPatients = <MaternalWardPendingPatients />;
29
42
 
30
43
  return (
31
44
  <>
32
- <WardViewHeader {...{ wardPendingPatients }} />
45
+ <WardViewHeader {...{ wardPendingPatients, wardMetrics }} />
33
46
  <Ward {...{ wardBeds, wardUnassignedPatients }} />
34
47
  </>
35
48
  );
@@ -0,0 +1,140 @@
1
+ import { createAndGetWardPatientGrouping, getWardMetrics } from './ward-view.resource';
2
+ import { type AdmissionLocationFetchResponse, type InpatientAdmission, type WardPatientGroupDetails } from '../types';
3
+ import type { Location } from '@openmrs/esm-framework';
4
+
5
+ describe('Ward View Resource', () => {
6
+ const patient1 = {
7
+ uuid: 'patient-uuid-1',
8
+ person: {
9
+ uuid: 'some-person-uuid-1',
10
+ age: 24,
11
+ gender: 'F',
12
+ },
13
+ };
14
+
15
+ const patient2 = {
16
+ uuid: 'patient-uuid-2',
17
+ person: {
18
+ uuid: 'some-person-uuid-2',
19
+ age: 0.5,
20
+ gender: 'M',
21
+ },
22
+ };
23
+
24
+ const currentWardLocation: Location = {
25
+ uuid: 'some-location-uuid',
26
+ };
27
+
28
+ const inpatientAdmissionsAtCurrentLocation: InpatientAdmission[] = [
29
+ {
30
+ patient: patient1,
31
+ visit: undefined,
32
+ encounterAssigningToCurrentInpatientLocation: undefined,
33
+ firstAdmissionOrTransferEncounter: undefined,
34
+ currentInpatientRequest: undefined,
35
+ currentInpatientLocation: currentWardLocation,
36
+ },
37
+ {
38
+ patient: patient2,
39
+ visit: undefined,
40
+ encounterAssigningToCurrentInpatientLocation: undefined,
41
+ firstAdmissionOrTransferEncounter: undefined,
42
+ currentInpatientRequest: undefined,
43
+ currentInpatientLocation: currentWardLocation,
44
+ },
45
+ ];
46
+
47
+ const admissionLocationFetchResponse: AdmissionLocationFetchResponse = {
48
+ bedLayouts: [
49
+ {
50
+ rowNumber: 1,
51
+ columnNumber: 1,
52
+ bedNumber: '1',
53
+ bedId: 1,
54
+ bedUuid: 'some-bed-uuid',
55
+ status: 'OCCUPIED',
56
+ bedType: undefined,
57
+ location: '',
58
+ patients: [patient1],
59
+ bedTagMaps: [],
60
+ },
61
+ ],
62
+ occupiedBeds: 1,
63
+ totalBeds: 1,
64
+ ward: currentWardLocation,
65
+ };
66
+
67
+ describe('createAndGetWardPatientGrouping', () => {
68
+ it('should handle null data', () => {
69
+ const wardPatientGrouping = createAndGetWardPatientGrouping(null, null, null, null, null);
70
+ expect(wardPatientGrouping.allWardPatientUuids.size).toBe(0);
71
+ });
72
+ it('should include inpatient admissions at current location when counting total patients', () => {
73
+ const wardPatientGrouping = createAndGetWardPatientGrouping(
74
+ inpatientAdmissionsAtCurrentLocation,
75
+ null,
76
+ null,
77
+ null,
78
+ null,
79
+ );
80
+ expect(wardPatientGrouping.allWardPatientUuids.size).toBe(2);
81
+ });
82
+ it('should return admitted patient with bed', () => {
83
+ const wardPatientGrouping = createAndGetWardPatientGrouping(
84
+ inpatientAdmissionsAtCurrentLocation,
85
+ admissionLocationFetchResponse,
86
+ null,
87
+ null,
88
+ currentWardLocation,
89
+ );
90
+ expect(wardPatientGrouping.wardAdmittedPatientsWithBed.size).toBe(1);
91
+ expect(wardPatientGrouping.wardAdmittedPatientsWithBed.has('patient-uuid-1')).toBeTruthy();
92
+ });
93
+ it('should return admitted patient without bed', () => {
94
+ const wardPatientGrouping = createAndGetWardPatientGrouping(
95
+ inpatientAdmissionsAtCurrentLocation,
96
+ admissionLocationFetchResponse,
97
+ null,
98
+ null,
99
+ currentWardLocation,
100
+ );
101
+ expect(wardPatientGrouping.wardUnassignedPatientsList.length).toBe(1);
102
+ expect(wardPatientGrouping.wardUnassignedPatientsList.at(0).patient.uuid).toBe('patient-uuid-2');
103
+ });
104
+ });
105
+
106
+ describe('getWardMetrics', () => {
107
+ const createAndGetWardPatientGroupingResults = createAndGetWardPatientGrouping(
108
+ inpatientAdmissionsAtCurrentLocation,
109
+ admissionLocationFetchResponse,
110
+ null,
111
+ null,
112
+ currentWardLocation,
113
+ );
114
+ const wardPatientGrouping: WardPatientGroupDetails = {
115
+ allWardPatientUuids: createAndGetWardPatientGroupingResults.allWardPatientUuids,
116
+ bedLayouts: createAndGetWardPatientGroupingResults.bedLayouts,
117
+ inpatientAdmissionsByPatientUuid: createAndGetWardPatientGroupingResults.inpatientAdmissionsByPatientUuid,
118
+ wardAdmittedPatientsWithBed: createAndGetWardPatientGroupingResults.wardAdmittedPatientsWithBed,
119
+ wardPatientPendingCount: createAndGetWardPatientGroupingResults.wardPatientPendingCount,
120
+ wardUnadmittedPatientsWithBed: createAndGetWardPatientGroupingResults.wardUnadmittedPatientsWithBed,
121
+ wardUnassignedPatientsList: createAndGetWardPatientGroupingResults.wardUnassignedPatientsList,
122
+ inpatientRequestResponse: undefined,
123
+ inpatientAdmissionResponse: undefined,
124
+ admissionLocationResponse: undefined,
125
+ isLoading: false,
126
+ async mutate(): Promise<void> {
127
+ return Promise.resolve(undefined);
128
+ },
129
+ };
130
+
131
+ it('should return metrics', () => {
132
+ const metrics = getWardMetrics(wardPatientGrouping);
133
+ expect(metrics.patients).toBe('2');
134
+ expect(metrics.freeBeds).toBe('0');
135
+ expect(metrics.totalBeds).toBe('1');
136
+ expect(metrics.newborns).toBe('1');
137
+ expect(metrics.femalesOfReproductiveAge).toBe('1');
138
+ });
139
+ });
140
+ });
@@ -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
  );
@@ -17,8 +17,6 @@
17
17
  "bedSwap": "Bed swap",
18
18
  "cancel": "Cancel",
19
19
  "cancelAdmissionRequest": "Cancel admission request",
20
- "capacity": "Capacity",
21
- "capacityMetricValue": "{{ metricValue }} %",
22
20
  "chooseAnOption": "Choose an option",
23
21
  "clinicalForms": "Clinical forms",
24
22
  "clinicalNoteLabel": "Write your notes",
@@ -120,6 +118,8 @@
120
118
  "somePartsOfTheFormDidntLoad": "Some parts of the form didn't load",
121
119
  "timeOnWard": "Time on this ward: {{timeOnWard}}",
122
120
  "timeSinceAdmission": "Admitted: {{timeSinceAdmission}}",
121
+ "totalBeds": "Total beds",
122
+ "totalBedsMetricValue": "{{ metricValue }}",
123
123
  "transfer": "Transfer",
124
124
  "transferElsewhere": "Transfer elsewhere",
125
125
  "transferPatient": "Transfer patient",
@@ -17,8 +17,6 @@
17
17
  "bedSwap": "Bed swap",
18
18
  "cancel": "Cancel",
19
19
  "cancelAdmissionRequest": "Cancel admission request",
20
- "capacity": "Capacity",
21
- "capacityMetricValue": "{{ metricValue }} %",
22
20
  "chooseAnOption": "Choose an option",
23
21
  "clinicalForms": "Clinical forms",
24
22
  "clinicalNoteLabel": "Write your notes",
@@ -120,6 +118,8 @@
120
118
  "somePartsOfTheFormDidntLoad": "Some parts of the form didn't load",
121
119
  "timeOnWard": "Time on this ward: {{timeOnWard}}",
122
120
  "timeSinceAdmission": "Admitted: {{timeSinceAdmission}}",
121
+ "totalBeds": "Total beds",
122
+ "totalBedsMetricValue": "{{ metricValue }}",
123
123
  "transfer": "Transfer",
124
124
  "transferElsewhere": "Transfer elsewhere",
125
125
  "transferPatient": "Transfer patient",
@@ -17,8 +17,6 @@
17
17
  "bedSwap": "Bed swap",
18
18
  "cancel": "Cancel",
19
19
  "cancelAdmissionRequest": "Cancel admission request",
20
- "capacity": "Capacity",
21
- "capacityMetricValue": "{{ metricValue }} %",
22
20
  "chooseAnOption": "Choose an option",
23
21
  "clinicalForms": "Clinical forms",
24
22
  "clinicalNoteLabel": "Write your notes",
@@ -120,6 +118,8 @@
120
118
  "somePartsOfTheFormDidntLoad": "Some parts of the form didn't load",
121
119
  "timeOnWard": "Time on this ward: {{timeOnWard}}",
122
120
  "timeSinceAdmission": "Admitted: {{timeSinceAdmission}}",
121
+ "totalBeds": "Total beds",
122
+ "totalBedsMetricValue": "{{ metricValue }}",
123
123
  "transfer": "Transfer",
124
124
  "transferElsewhere": "Transfer elsewhere",
125
125
  "transferPatient": "Transfer patient",
@@ -17,8 +17,6 @@
17
17
  "bedSwap": "Bed swap",
18
18
  "cancel": "Cancel",
19
19
  "cancelAdmissionRequest": "Cancel admission request",
20
- "capacity": "Capacity",
21
- "capacityMetricValue": "{{ metricValue }} %",
22
20
  "chooseAnOption": "Choose an option",
23
21
  "clinicalForms": "Clinical forms",
24
22
  "clinicalNoteLabel": "Write your notes",
@@ -120,6 +118,8 @@
120
118
  "somePartsOfTheFormDidntLoad": "Some parts of the form didn't load",
121
119
  "timeOnWard": "Time on this ward: {{timeOnWard}}",
122
120
  "timeSinceAdmission": "Admitted: {{timeSinceAdmission}}",
121
+ "totalBeds": "Total beds",
122
+ "totalBedsMetricValue": "{{ metricValue }}",
123
123
  "transfer": "Transfer",
124
124
  "transferElsewhere": "Transfer elsewhere",
125
125
  "transferPatient": "Transfer patient",