@kenyaemr/esm-ward-app 8.1.1-pre.116 → 8.1.1-pre.119

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 (149) hide show
  1. package/.turbo/turbo-build.log +16 -20
  2. package/dist/124.js +1 -0
  3. package/dist/124.js.map +1 -0
  4. package/dist/125.js +1 -1
  5. package/dist/125.js.map +1 -1
  6. package/dist/130.js +1 -1
  7. package/dist/130.js.map +1 -1
  8. package/dist/153.js +1 -0
  9. package/dist/153.js.map +1 -0
  10. package/dist/372.js +1 -1
  11. package/dist/372.js.map +1 -1
  12. package/dist/471.js +1 -0
  13. package/dist/471.js.map +1 -0
  14. package/dist/481.js +1 -0
  15. package/dist/481.js.map +1 -0
  16. package/dist/53.js +1 -1
  17. package/dist/53.js.map +1 -1
  18. package/dist/559.js +1 -1
  19. package/dist/559.js.map +1 -1
  20. package/dist/574.js +1 -1
  21. package/dist/{500.js → 576.js} +1 -1
  22. package/dist/576.js.map +1 -0
  23. package/dist/577.js +1 -1
  24. package/dist/577.js.map +1 -1
  25. package/dist/649.js +2 -0
  26. package/dist/{161.js.LICENSE.txt → 649.js.LICENSE.txt} +0 -6
  27. package/dist/649.js.map +1 -0
  28. package/dist/{659.js → 662.js} +1 -1
  29. package/dist/662.js.map +1 -0
  30. package/dist/67.js +2 -0
  31. package/dist/67.js.LICENSE.txt +5 -0
  32. package/dist/67.js.map +1 -0
  33. package/dist/920.js +1 -0
  34. package/dist/920.js.map +1 -0
  35. package/dist/921.js +1 -0
  36. package/dist/921.js.map +1 -0
  37. package/dist/922.js +1 -1
  38. package/dist/922.js.map +1 -1
  39. package/dist/kenyaemr-esm-ward-app.js +1 -1
  40. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +148 -165
  41. package/dist/kenyaemr-esm-ward-app.js.map +1 -1
  42. package/dist/main.js +1 -1
  43. package/dist/main.js.LICENSE.txt +0 -10
  44. package/dist/main.js.map +1 -1
  45. package/dist/routes.json +1 -1
  46. package/mock.tsx +8 -0
  47. package/package.json +1 -1
  48. package/src/beds/empty-bed-skeleton.tsx +3 -3
  49. package/src/beds/empty-bed.component.tsx +3 -3
  50. package/src/beds/ward-bed.component.tsx +41 -0
  51. package/src/beds/ward-bed.scss +45 -0
  52. package/src/beds/{occupied-bed.test.tsx → ward-bed.test.tsx} +27 -16
  53. package/src/config-schema.ts +196 -75
  54. package/src/hooks/useAssignedBedByPatient.ts +9 -0
  55. package/src/hooks/useInpatientAdmission.ts +1 -1
  56. package/src/hooks/useMotherAndChildren.ts +2 -2
  57. package/src/hooks/useObs.ts +2 -2
  58. package/src/hooks/useWardPatientGrouping.ts +2 -0
  59. package/src/index.ts +10 -29
  60. package/src/root.component.tsx +3 -0
  61. package/src/routes.json +6 -11
  62. package/src/types/index.ts +29 -14
  63. package/src/ward-patient-card/card-rows/admission-request-note-row.component.tsx +38 -0
  64. package/src/ward-patient-card/card-rows/coded-obs-tags-row.component.tsx +108 -0
  65. package/src/ward-patient-card/card-rows/mother-child-row.component.tsx +84 -0
  66. package/src/ward-patient-card/card-rows/{pending-items-car-row.extension.tsx → pending-items-row.component.tsx} +12 -8
  67. package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +12 -7
  68. package/src/ward-patient-card/row-elements/ward-patient-header-address.tsx +5 -5
  69. package/src/ward-patient-card/row-elements/ward-patient-identifier.tsx +18 -40
  70. package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +2 -2
  71. package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +15 -8
  72. package/src/ward-patient-card/ward-patient-card.component.tsx +16 -54
  73. package/src/ward-patient-card/ward-patient-card.scss +7 -1
  74. package/src/ward-view/default-ward/default-ward-beds.component.tsx +42 -0
  75. package/src/ward-view/default-ward/default-ward-patient-card-header.component.tsx +32 -0
  76. package/src/ward-view/default-ward/default-ward-patient-card.component.tsx +31 -0
  77. package/src/ward-view/default-ward/default-ward-pending-patients.component.tsx +52 -0
  78. package/src/ward-view/default-ward/default-ward-unassigned-patients.component.tsx +32 -0
  79. package/src/ward-view/default-ward/default-ward-view.component.tsx +31 -0
  80. package/src/ward-view/materal-ward/maternal-ward-beds.component.tsx +65 -0
  81. package/src/ward-view/materal-ward/maternal-ward-patient-card-header.component.tsx +30 -0
  82. package/src/ward-view/materal-ward/maternal-ward-patient-card.component.tsx +93 -0
  83. package/src/{beds/occupied-bed.scss → ward-view/materal-ward/maternal-ward-patient-card.scss} +4 -10
  84. package/src/ward-view/materal-ward/maternal-ward-patient-card.test.tsx +47 -0
  85. package/src/ward-view/materal-ward/maternal-ward-pending-patients.component.tsx +48 -0
  86. package/src/ward-view/materal-ward/maternal-ward-unassigned-patients.component.tsx +33 -0
  87. package/src/ward-view/materal-ward/maternal-ward-view.component.tsx +38 -0
  88. package/src/ward-view/materal-ward/maternal-ward-view.resource.ts +89 -0
  89. package/src/ward-view/ward-view.component.tsx +11 -151
  90. package/src/ward-view/ward-view.resource.ts +78 -6
  91. package/src/ward-view/ward-view.scss +1 -0
  92. package/src/ward-view/ward-view.test.tsx +10 -8
  93. package/src/ward-view/ward.component.tsx +106 -0
  94. package/src/ward-view-header/admission-requests-bar.component.tsx +10 -6
  95. package/src/ward-view-header/admission-requests-bar.test.tsx +5 -4
  96. package/src/ward-view-header/ward-metrics.component.tsx +12 -11
  97. package/src/ward-view-header/ward-metrics.test.tsx +4 -58
  98. package/src/ward-view-header/ward-view-header.component.tsx +6 -4
  99. package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +7 -4
  100. package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +9 -21
  101. package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +9 -3
  102. package/src/ward-workspace/admission-request-card/admission-request-card.scss +6 -1
  103. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +11 -38
  104. package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +8 -38
  105. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +80 -89
  106. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +21 -13
  107. package/src/ward-workspace/patient-banner/patient-banner.component.tsx +5 -12
  108. package/src/ward-workspace/patient-details/ward-patient-action-button.extension.tsx +2 -2
  109. package/src/ward-workspace/patient-details/ward-patient.workspace.tsx +13 -6
  110. package/src/ward-workspace/patient-discharge/patient-discharge.workspace.tsx +6 -6
  111. package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +7 -7
  112. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +6 -6
  113. package/translations/en.json +7 -1
  114. package/dist/126.js +0 -1
  115. package/dist/126.js.map +0 -1
  116. package/dist/161.js +0 -2
  117. package/dist/161.js.map +0 -1
  118. package/dist/2.js +0 -1
  119. package/dist/2.js.map +0 -1
  120. package/dist/269.js +0 -1
  121. package/dist/269.js.map +0 -1
  122. package/dist/466.js +0 -1
  123. package/dist/466.js.map +0 -1
  124. package/dist/500.js.map +0 -1
  125. package/dist/557.js +0 -1
  126. package/dist/557.js.map +0 -1
  127. package/dist/659.js.map +0 -1
  128. package/dist/701.js +0 -1
  129. package/dist/701.js.map +0 -1
  130. package/dist/749.js +0 -1
  131. package/dist/749.js.map +0 -1
  132. package/dist/908.js +0 -1
  133. package/dist/908.js.map +0 -1
  134. package/src/beds/empty-bed.scss +0 -24
  135. package/src/beds/occupied-bed.component.tsx +0 -35
  136. package/src/beds/unassigned-patient.component.tsx +0 -20
  137. package/src/beds/unassigned-patient.scss +0 -6
  138. package/src/config-schema-admission-request-note.ts +0 -9
  139. package/src/config-schema-extension-colored-obs-tags.ts +0 -91
  140. package/src/config-schema-mother-child-row.ts +0 -26
  141. package/src/config-schema-pending-items-extension.ts +0 -29
  142. package/src/hooks/useCurrentWardCardConfig.ts +0 -32
  143. package/src/ward-patient-card/card-rows/admission-request-note.extension.tsx +0 -32
  144. package/src/ward-patient-card/card-rows/colored-obs-tags-card-row.extension.tsx +0 -13
  145. package/src/ward-patient-card/card-rows/mother-child-row.extension.tsx +0 -110
  146. package/src/ward-patient-card/ward-patient-card-element.component.tsx +0 -69
  147. package/src/ward-patient-card/ward-patient-resource.ts +0 -15
  148. package/src/ward-view/ward-bed.component.tsx +0 -14
  149. /package/src/ward-patient-card/row-elements/{ward-pateint-skeleton-text.tsx → ward-patient-skeleton-text.tsx} +0 -0
@@ -1,66 +1,28 @@
1
- import { ExtensionSlot, getPatientName, launchWorkspace } from '@openmrs/esm-framework';
2
- import classNames from 'classnames';
3
- import React from 'react';
4
- import { useCurrentWardCardConfig } from '../hooks/useCurrentWardCardConfig';
5
- import { type WardPatientCard, type WardPatientWorkspaceProps } from '../types';
6
- import WardPatientBedNumber from './row-elements/ward-patient-bed-number';
7
- import WardPatientName from './row-elements/ward-patient-name';
8
- import { WardPatientCardElement } from './ward-patient-card-element.component';
1
+ import { getPatientName, launchWorkspace, useAppContext } from '@openmrs/esm-framework';
2
+ import React, { type ReactNode } from 'react';
3
+ import { type WardViewContext, type WardPatient,type PatientWorkspaceAdditionalProps } from '../types';
9
4
  import styles from './ward-patient-card.scss';
10
- import { launchPatientWorkspace, setWardPatient } from './ward-patient-resource';
11
5
 
12
- const WardPatientCard: WardPatientCard = (wardPatient) => {
13
- const { patient, bed } = wardPatient;
14
- const { id, headerRowElements, footerRowElements } = useCurrentWardCardConfig();
6
+ interface Props {
7
+ children: ReactNode;
8
+ wardPatient: WardPatient;
9
+ }
15
10
 
16
- const headerExtensionSlotName =
17
- id == 'default' ? 'ward-patient-card-header-slot' : `ward-patient-card-header-${id}-slot`;
18
- const rowsExtensionSlotName = id == 'default' ? 'ward-patient-card-slot' : `ward-patient-card-${id}-slot`;
19
- const footerExtensionSlotName =
20
- id == 'default' ? 'ward-patient-card-footer-slot' : `ward-patient-card-footer-${id}-slot`;
11
+ const WardPatientCard: React.FC<Props> = ({ children, wardPatient }) => {
12
+ const { patient } = wardPatient;
13
+ const { WardPatientHeader } = useAppContext<WardViewContext>('ward-view-context') ?? {};
21
14
 
22
15
  return (
23
16
  <div className={styles.wardPatientCard}>
24
- <div className={classNames(styles.wardPatientCardRow, styles.wardPatientCardHeader)}>
25
- {bed ? <WardPatientBedNumber bed={bed} /> : null}
26
- <WardPatientName patient={patient} />
27
- {headerRowElements.map((elementId, i) => (
28
- <WardPatientCardElement
29
- key={`ward-card-${patient.uuid}-header-${i}`}
30
- elementId={elementId}
31
- {...wardPatient}
32
- />
33
- ))}
34
- <ExtensionSlot name={headerExtensionSlotName} state={wardPatient} />
35
- </div>
36
- {footerRowElements.length > 0 && (
37
- <div className={styles.wardPatientCardRow}>
38
- {footerRowElements.map((elementId, i) => (
39
- <WardPatientCardElement
40
- key={`ward-card-${patient.uuid}-footer-${i}`}
41
- elementId={elementId}
42
- {...wardPatient}
43
- />
44
- ))}
45
- <ExtensionSlot name={footerExtensionSlotName} state={wardPatient} />
46
- </div>
47
- )}
48
- <ExtensionSlot
49
- name="ward-patient-card-pending-items-slot"
50
- state={wardPatient}
51
- className={styles.wardPatientCardExtensionSlot}
52
- />
53
- <ExtensionSlot
54
- name={rowsExtensionSlotName}
55
- state={wardPatient}
56
- className={classNames(styles.wardPatientCardExtensionSlot)}
57
- />
17
+ {children}
58
18
  <button
59
19
  className={styles.wardPatientCardButton}
60
20
  onClick={() => {
61
- setWardPatient(wardPatient);
62
- launchPatientWorkspace();
63
- }}>
21
+ launchWorkspace<PatientWorkspaceAdditionalProps>('ward-patient-workspace', {
22
+ wardPatient,
23
+ WardPatientHeader
24
+ });
25
+ }}>
64
26
  {/* Name will not be displayed; just there for a11y */}
65
27
  {getPatientName(patient.person)}
66
28
  </button>
@@ -56,6 +56,7 @@
56
56
  width: 100%;
57
57
  padding: layout.$spacing-04;
58
58
  border-top: 1px colors.$gray-20 solid;
59
+ background-color: colors.$white;
59
60
 
60
61
  &:empty {
61
62
  border-top: 0px;
@@ -174,7 +175,7 @@
174
175
  display: flex;
175
176
  flex-wrap: wrap;
176
177
 
177
- > div:not(div:first-of-type):not(:empty) {
178
+ > *:not(*:first-of-type):not(:empty) {
178
179
  display: flex;
179
180
  align-items: center;
180
181
 
@@ -188,3 +189,8 @@
188
189
  .skeletonText {
189
190
  width: 6.25rem;
190
191
  }
192
+
193
+ .unassignedPatient {
194
+ display: flex;
195
+ flex-direction: column;
196
+ }
@@ -0,0 +1,42 @@
1
+ import { useAppContext } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import WardBed from '../../beds/ward-bed.component';
4
+ import { type WardPatient, type WardViewContext } from '../../types';
5
+ import { bedLayoutToBed } from '../ward-view.resource';
6
+ import DefaultWardPatientCard from './default-ward-patient-card.component';
7
+
8
+ function DefaultWardBeds() {
9
+ const {wardPatientGroupDetails} = useAppContext<WardViewContext>('ward-view-context') ?? {};
10
+ const { bedLayouts, wardAdmittedPatientsWithBed } = wardPatientGroupDetails ?? {};
11
+
12
+ const wardBeds = bedLayouts?.map((bedLayout) => {
13
+ const { patients } = bedLayout;
14
+ const bed = bedLayoutToBed(bedLayout);
15
+ const wardPatients: WardPatient[] = patients.map((patient): WardPatient => {
16
+ const inpatientAdmission = wardAdmittedPatientsWithBed?.get(patient.uuid);
17
+ if (inpatientAdmission) {
18
+ const { patient, visit, currentInpatientRequest } = inpatientAdmission;
19
+ return { patient, visit, bed, inpatientAdmission, inpatientRequest: currentInpatientRequest || null };
20
+ } else {
21
+ // for some reason this patient is in a bed but not in the list of admitted patients, so we need to use the patient data from the bed endpoint
22
+ return {
23
+ patient: patient,
24
+ visit: null,
25
+ bed,
26
+ inpatientAdmission: null, // populate after BED-13
27
+ inpatientRequest: null,
28
+ };
29
+ }
30
+ });
31
+ const patientCards = wardPatients.map(wardPatient => (
32
+ <DefaultWardPatientCard
33
+ key={wardPatient.patient.uuid}
34
+ {...wardPatient} />
35
+ ));
36
+ return <WardBed key={bed.uuid} bed={bed} patientCards={patientCards} />;
37
+ });
38
+
39
+ return <>{wardBeds}</>;
40
+ }
41
+
42
+ export default DefaultWardBeds;
@@ -0,0 +1,32 @@
1
+ import classNames from 'classnames';
2
+ import React from 'react';
3
+ import WardPatientAge from '../../ward-patient-card/row-elements/ward-patient-age';
4
+ import WardPatientBedNumber from '../../ward-patient-card/row-elements/ward-patient-bed-number';
5
+ import WardPatientIdentifier from '../../ward-patient-card/row-elements/ward-patient-identifier';
6
+ import WardPatientName from '../../ward-patient-card/row-elements/ward-patient-name';
7
+ import WardPatientTimeOnWard from '../../ward-patient-card/row-elements/ward-patient-time-on-ward';
8
+ import WardPatientTimeSinceAdmission from '../../ward-patient-card/row-elements/ward-patient-time-since-admission';
9
+ import styles from '../../ward-patient-card/ward-patient-card.scss';
10
+ import { type WardPatientCardType } from '../../types';
11
+ import WardPatientGender from '../../ward-patient-card/row-elements/ward-patient-gender.component';
12
+
13
+ const DefaultWardPatientCardHeader: WardPatientCardType = (wardPatient) => {
14
+ const { patient, bed, inpatientAdmission } = wardPatient;
15
+ const { encounterAssigningToCurrentInpatientLocation, firstAdmissionOrTransferEncounter } = inpatientAdmission ?? {};
16
+
17
+ return (
18
+ <div className={classNames(styles.wardPatientCardRow, styles.wardPatientCardHeader)}>
19
+ {bed ? <WardPatientBedNumber bed={bed} /> : null}
20
+ <WardPatientName patient={patient} />
21
+ <WardPatientIdentifier id="patient-identifier" patient={patient} />
22
+ <WardPatientGender patient={patient} />
23
+ <WardPatientAge patient={patient} />
24
+ <WardPatientTimeSinceAdmission firstAdmissionOrTransferEncounter={firstAdmissionOrTransferEncounter} />
25
+ <WardPatientTimeOnWard
26
+ encounterAssigningToCurrentInpatientLocation={encounterAssigningToCurrentInpatientLocation}
27
+ />
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export default DefaultWardPatientCardHeader;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { type WardPatientCardType } from '../../types';
3
+ import AdmissionRequestNoteRow from '../../ward-patient-card/card-rows/admission-request-note-row.component';
4
+ import PendingItemsRow from '../../ward-patient-card/card-rows/pending-items-row.component';
5
+ import WardPatientCard from '../../ward-patient-card/ward-patient-card.component';
6
+ import styles from '../../ward-patient-card/ward-patient-card.scss';
7
+ import DefaultWardPatientCardHeader from './default-ward-patient-card-header.component';
8
+
9
+ const DefaultWardPatientCard: WardPatientCardType = (wardPatient) => {
10
+ const { bed } = wardPatient;
11
+
12
+ const card = (
13
+ <WardPatientCard wardPatient={wardPatient}>
14
+ <DefaultWardPatientCardHeader {...wardPatient} />
15
+ <PendingItemsRow id={'pending-items'} wardPatient={wardPatient} />
16
+ <AdmissionRequestNoteRow id={'admission-request-note'} wardPatient={wardPatient} />
17
+ </WardPatientCard>
18
+ );
19
+
20
+ if (bed) {
21
+ return card;
22
+ } else {
23
+ return (
24
+ <div className={styles.unassignedPatient}>
25
+ <div key={'unassigned-bed-pt-' + wardPatient.patient.uuid}>{card}</div>
26
+ </div>
27
+ );
28
+ }
29
+ };
30
+
31
+ export default DefaultWardPatientCard;
@@ -0,0 +1,52 @@
1
+ import { ErrorState, useAppContext } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { type WardViewContext, type InpatientRequest } from '../../types';
5
+ import AdmissionRequestCard from '../../ward-workspace/admission-request-card/admission-request-card.component';
6
+ import WardPatientSkeletonText from '../../ward-patient-card/row-elements/ward-patient-skeleton-text';
7
+ import AdmissionRequestNoteRow from '../../ward-patient-card/card-rows/admission-request-note-row.component';
8
+
9
+ function DefaultWardPendingPatients() {
10
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
11
+ const { t } = useTranslation();
12
+ const { inpatientRequestResponse } = wardPatientGroupDetails ?? {};
13
+ const {
14
+ inpatientRequests,
15
+ isLoading: isLoadingInpatientRequests,
16
+ error: errorFetchingInpatientRequests,
17
+ } = inpatientRequestResponse ?? {};
18
+
19
+ return isLoadingInpatientRequests ? (
20
+ <WardPatientSkeletonText />
21
+ ) : errorFetchingInpatientRequests ? (
22
+ <ErrorState headerTitle={t('admissionRequests', 'Admission requests')} error={errorFetchingInpatientRequests} />
23
+ ) : (
24
+ <>
25
+ {inpatientRequests?.map((request: InpatientRequest, i) => {
26
+ const wardPatient = {
27
+ patient: request.patient,
28
+ visit: request.visit,
29
+ bed: null,
30
+ inpatientRequest: request,
31
+ inpatientAdmission: null,
32
+ };
33
+
34
+ return (
35
+ <AdmissionRequestCard
36
+ key={`admission-request-card-${i}`}
37
+ wardPatient={{
38
+ patient: request.patient,
39
+ visit: request.visit,
40
+ bed: null,
41
+ inpatientRequest: request,
42
+ inpatientAdmission: null,
43
+ }}>
44
+ <AdmissionRequestNoteRow id={'admission-request-note'} wardPatient={wardPatient} />
45
+ </AdmissionRequestCard>
46
+ );
47
+ })}
48
+ </>
49
+ );
50
+ }
51
+
52
+ export default DefaultWardPendingPatients;
@@ -0,0 +1,32 @@
1
+ import { useAppContext } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { type WardViewContext } from '../../types';
4
+ import DefaultWardPatientCard from './default-ward-patient-card.component';
5
+
6
+ /**
7
+ * Renders a list of patients in the ward that are admitted but not assigned a bed
8
+ * @returns
9
+ */
10
+ function DefaultWardUnassignedPatients() {
11
+ const {wardPatientGroupDetails} = useAppContext<WardViewContext>('ward-view-context') ?? {};
12
+ const { wardUnassignedPatientsList } = wardPatientGroupDetails ?? {};
13
+
14
+ const wardUnassignedPatients = wardUnassignedPatientsList?.map((inpatientAdmission) => {
15
+ return (
16
+ <DefaultWardPatientCard
17
+ {...{
18
+ patient: inpatientAdmission.patient,
19
+ visit: inpatientAdmission.visit,
20
+ bed: null,
21
+ inpatientAdmission,
22
+ inpatientRequest: inpatientAdmission.currentInpatientRequest,
23
+ }}
24
+ key={inpatientAdmission.patient.uuid}
25
+ />
26
+ );
27
+ });
28
+
29
+ return <>{wardUnassignedPatients}</>;
30
+ }
31
+
32
+ export default DefaultWardUnassignedPatients;
@@ -0,0 +1,31 @@
1
+ import { useDefineAppContext } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { useWardPatientGrouping } from '../../hooks/useWardPatientGrouping';
4
+ import { type WardViewContext } from '../../types';
5
+ import WardViewHeader from '../../ward-view-header/ward-view-header.component';
6
+ import Ward from '../ward.component';
7
+ import DefaultWardBeds from './default-ward-beds.component';
8
+ import DefaultWardPendingPatients from './default-ward-pending-patients.component';
9
+ import DefaultWardUnassignedPatients from './default-ward-unassigned-patients.component';
10
+ import DefaultWardPatientCardHeader from './default-ward-patient-card-header.component';
11
+
12
+ const DefaultWardView = () => {
13
+ const wardPatientGroupDetails = useWardPatientGrouping();
14
+ useDefineAppContext<WardViewContext>('ward-view-context', {
15
+ wardPatientGroupDetails,
16
+ WardPatientHeader: DefaultWardPatientCardHeader
17
+ });
18
+
19
+ const wardBeds = <DefaultWardBeds />;
20
+ const wardUnassignedPatients = <DefaultWardUnassignedPatients />;
21
+ const wardPendingPatients = <DefaultWardPendingPatients />;
22
+
23
+ return (
24
+ <>
25
+ <WardViewHeader {...{ wardPendingPatients }} />
26
+ <Ward {...{ wardBeds, wardUnassignedPatients }} />
27
+ </>
28
+ );
29
+ };
30
+
31
+ export default DefaultWardView;
@@ -0,0 +1,65 @@
1
+ import { useAppContext } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import WardBed from '../../beds/ward-bed.component';
4
+ import { type MotherChildRelationships, type WardPatient, type WardViewContext } from '../../types';
5
+ import { bedLayoutToBed } from '../ward-view.resource';
6
+ import MaternalWardPatientCard from './maternal-ward-patient-card.component';
7
+
8
+ const MaternalWardBeds: React.FC<MotherChildRelationships> = (motherChildRelationships) => {
9
+ const { motherByChildUuid } = motherChildRelationships ?? {};
10
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
11
+ const { bedLayouts, wardAdmittedPatientsWithBed } = wardPatientGroupDetails ?? {};
12
+
13
+ const wardBeds = bedLayouts?.map((bedLayout) => {
14
+ const { patients: patientsInCurrentBed } = bedLayout;
15
+ const bed = bedLayoutToBed(bedLayout);
16
+ const childrenInSameBedByMotherUuid = new Map<string, WardPatient[]>();
17
+
18
+ const wardPatients: WardPatient[] = patientsInCurrentBed
19
+ .map((patient): WardPatient => {
20
+ const inpatientAdmission = wardAdmittedPatientsWithBed?.get(patient.uuid);
21
+ if (inpatientAdmission) {
22
+ const { patient, visit, currentInpatientRequest } = inpatientAdmission;
23
+ return { patient, visit, bed, inpatientAdmission, inpatientRequest: currentInpatientRequest || null };
24
+ } else {
25
+ // for some reason this patient is in a bed but not in the list of admitted patients,
26
+ // so we need to use the patient data from the bed endpoint
27
+ return {
28
+ patient: patient,
29
+ visit: null,
30
+ bed,
31
+ inpatientAdmission: null,
32
+ inpatientRequest: null,
33
+ };
34
+ }
35
+ })
36
+ .filter((wardPatient) => {
37
+ // filter out any child patient whose mother is also assigned to the same bed
38
+ // and put the child in childrenInSameBedByMotherUuid
39
+ const patientUuid = wardPatient.patient.uuid;
40
+ const { patient: mother } = motherByChildUuid?.get(patientUuid) ?? {};
41
+ const motherInSameBed = patientsInCurrentBed.some((p) => p.uuid == mother?.uuid);
42
+ if (motherInSameBed) {
43
+ if (!childrenInSameBedByMotherUuid.has(mother.uuid)) {
44
+ childrenInSameBedByMotherUuid.set(mother.uuid, []);
45
+ }
46
+ childrenInSameBedByMotherUuid.get(mother.uuid).push(wardPatient);
47
+ }
48
+ return !motherInSameBed;
49
+ });
50
+
51
+ const patientCards = wardPatients.map((wardPatient) => (
52
+ <MaternalWardPatientCard
53
+ key={wardPatient.patient.uuid}
54
+ wardPatient={wardPatient}
55
+ childrenOfWardPatientInSameBed={childrenInSameBedByMotherUuid.get(wardPatient.patient.uuid)}
56
+ />
57
+ ));
58
+
59
+ return <WardBed key={bed.uuid} bed={bed} patientCards={patientCards} />;
60
+ });
61
+
62
+ return <>{wardBeds}</>;
63
+ };
64
+
65
+ export default MaternalWardBeds;
@@ -0,0 +1,30 @@
1
+ import classNames from 'classnames';
2
+ import React from 'react';
3
+ import { type WardPatientCardType } from '../../types';
4
+ import WardPatientAge from '../../ward-patient-card/row-elements/ward-patient-age';
5
+ import WardPatientBedNumber from '../../ward-patient-card/row-elements/ward-patient-bed-number';
6
+ import WardPatientAddress from '../../ward-patient-card/row-elements/ward-patient-header-address';
7
+ import WardPatientIdentifier from '../../ward-patient-card/row-elements/ward-patient-identifier';
8
+ import WardPatientName from '../../ward-patient-card/row-elements/ward-patient-name';
9
+ import WardPatientObs from '../../ward-patient-card/row-elements/ward-patient-obs';
10
+ import WardPatientTimeSinceAdmission from '../../ward-patient-card/row-elements/ward-patient-time-since-admission';
11
+ import styles from '../../ward-patient-card/ward-patient-card.scss';
12
+
13
+ const MaternalWardPatientCardHeader: WardPatientCardType = (wardPatient) => {
14
+ const { patient, bed, visit, inpatientAdmission } = wardPatient;
15
+ const { firstAdmissionOrTransferEncounter } = inpatientAdmission ?? {};
16
+
17
+ return (
18
+ <div className={classNames(styles.wardPatientCardRow, styles.wardPatientCardHeader)}>
19
+ {bed ? <WardPatientBedNumber bed={bed} /> : null}
20
+ <WardPatientName patient={patient} />
21
+ <WardPatientIdentifier id="patient-identifier" patient={patient} />
22
+ <WardPatientAge patient={patient} />
23
+ <WardPatientAddress id={'patient-address'} patient={patient} />
24
+ <WardPatientObs id={'admission-reason'} patient={patient} visit={visit} />
25
+ <WardPatientTimeSinceAdmission firstAdmissionOrTransferEncounter={firstAdmissionOrTransferEncounter} />
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export default MaternalWardPatientCardHeader;
@@ -0,0 +1,93 @@
1
+ import { Tag } from '@carbon/react';
2
+ import classNames from 'classnames';
3
+ import React from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { type WardPatient } from '../../types';
6
+ import CodedObsTagsRow from '../../ward-patient-card/card-rows/coded-obs-tags-row.component';
7
+ import MotherChildRow from '../../ward-patient-card/card-rows/mother-child-row.component';
8
+ import PendingItemsRow from '../../ward-patient-card/card-rows/pending-items-row.component';
9
+ import WardPatientObs from '../../ward-patient-card/row-elements/ward-patient-obs';
10
+ import WardPatientTimeOnWard from '../../ward-patient-card/row-elements/ward-patient-time-on-ward';
11
+ import WardPatientCard from '../../ward-patient-card/ward-patient-card.component';
12
+ import styles from '../../ward-patient-card/ward-patient-card.scss';
13
+ import MaternalWardPatientCardHeader from './maternal-ward-patient-card-header.component';
14
+ import maternalWardPatientCardStyles from './maternal-ward-patient-card.scss';
15
+
16
+ export interface MaternalWardPatientCardProps {
17
+ /**
18
+ * the patient to render. Note that this patient can be a mother or a child
19
+ */
20
+ wardPatient: WardPatient;
21
+
22
+ /**
23
+ * Children of the wardPatient occupying the same bed. A non-empty array implies that the wardPatient
24
+ * is a mother.
25
+ */
26
+ childrenOfWardPatientInSameBed: WardPatient[];
27
+ }
28
+
29
+ /**
30
+ * One major different between MaternalWardPatientCard vs DefaultWardPatientCard, besides the
31
+ * different elements being rendered, is that it renders just not the patient card for the patient,
32
+ * but also those of the the patient's children in same bed. This is done to ensure that the children's
33
+ * patient cards are always rendered right below the mother's.
34
+ *
35
+ * @param param0
36
+ * @returns
37
+ */
38
+ const MaternalWardPatientCard: React.FC<MaternalWardPatientCardProps> = (props) => {
39
+ const { wardPatient, childrenOfWardPatientInSameBed } = props;
40
+ const { patient, visit, bed, inpatientAdmission } = wardPatient;
41
+ const { encounterAssigningToCurrentInpatientLocation } = inpatientAdmission ?? {};
42
+
43
+ const card = (
44
+ <>
45
+ <WardPatientCard wardPatient={wardPatient}>
46
+ <MaternalWardPatientCardHeader {...wardPatient} />
47
+ <div className={classNames(styles.wardPatientCardRow, styles.dotSeparatedChildren)}>
48
+ <WardPatientTimeOnWard
49
+ encounterAssigningToCurrentInpatientLocation={encounterAssigningToCurrentInpatientLocation}
50
+ />
51
+ <WardPatientObs id={'gravida'} patient={patient} visit={visit} />
52
+ </div>
53
+ <PendingItemsRow id={'pending-items'} wardPatient={wardPatient} />
54
+ <CodedObsTagsRow id="pregnancy-complications" {...wardPatient} />
55
+ <MotherChildRow {...props} />
56
+ </WardPatientCard>
57
+ {childrenOfWardPatientInSameBed?.map((childWardPatient) => {
58
+ return (
59
+ <React.Fragment key={childWardPatient.patient.uuid}>
60
+ <MotherChildBedShareDivider />
61
+ <WardPatientCard wardPatient={childWardPatient}>
62
+ <MaternalWardPatientCardHeader {...childWardPatient} />
63
+ <PendingItemsRow id={'pending-items'} wardPatient={childWardPatient} />
64
+ </WardPatientCard>
65
+ </React.Fragment>
66
+ );
67
+ })}
68
+ </>
69
+ );
70
+
71
+ if (bed) {
72
+ return card;
73
+ } else {
74
+ return (
75
+ <div className={styles.unassignedPatient}>
76
+ <div key={'unassigned-bed-pt-' + wardPatient.patient.uuid}>{card}</div>
77
+ </div>
78
+ );
79
+ }
80
+ };
81
+
82
+ const MotherChildBedShareDivider = () => {
83
+ const { t } = useTranslation();
84
+ return (
85
+ <div className={maternalWardPatientCardStyles.motherChildBedDivider}>
86
+ <div className={maternalWardPatientCardStyles.motherChildBedDividerLine}></div>
87
+ <Tag type="purple">{t('motherChildBedShare', 'Mother / Child')}</Tag>
88
+ <div className={maternalWardPatientCardStyles.motherChildBedDividerLine}></div>
89
+ </div>
90
+ );
91
+ };
92
+
93
+ export default MaternalWardPatientCard;
@@ -1,14 +1,8 @@
1
1
  @use '@carbon/layout';
2
2
  @use '@openmrs/esm-styleguide/src/vars';
3
+ @use '@carbon/type';
3
4
 
4
- .occupiedBed {
5
- display: flex;
6
- flex-direction: column;
7
- background-color: vars.$ui-02;
8
- height: fit-content;
9
- }
10
-
11
- .bedDivider {
5
+ .motherChildBedDivider {
12
6
  background-color: vars.$ui-02;
13
7
  color: vars.$text-02;
14
8
  padding: layout.$spacing-01;
@@ -17,8 +11,8 @@
17
11
  justify-content: space-between;
18
12
  }
19
13
 
20
- .bedDividerLine {
14
+ .motherChildBedDividerLine {
21
15
  height: 1px;
22
- background-color: vars.$ui-05;
23
16
  width: 30%;
17
+ border-top: 1px dashed vars.$ui-05;
24
18
  }
@@ -0,0 +1,47 @@
1
+ import { getDefaultsFromConfigSchema, useAppContext, useConfig } from '@openmrs/esm-framework';
2
+ import { screen } from '@testing-library/react';
3
+ import { mockPatientAlice, mockVisitAlice } from '__mocks__';
4
+ import React from 'react';
5
+ import { renderWithSwr } from 'tools';
6
+ import { mockInpatientAdmissionAlice } from '../../../../../__mocks__/inpatient-admission';
7
+ import { mockWardBeds } from '../../../../../__mocks__/wardBeds.mock';
8
+ import { mockWardViewContext } from '../../../mock';
9
+ import { configSchema, type WardConfigObject } from '../../config-schema';
10
+ import { type WardPatient, type WardViewContext } from '../../types';
11
+ import MaternalWardPatientCard from './maternal-ward-patient-card.component';
12
+
13
+ jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);
14
+
15
+ const defaultConfig: WardConfigObject = getDefaultsFromConfigSchema(configSchema);
16
+
17
+ jest.mocked(useConfig).mockReturnValue(defaultConfig);
18
+
19
+ describe('MaternalWardPatientCard', () => {
20
+ it('renders a patient with no child', () => {
21
+ const alice: WardPatient = {
22
+ patient: mockPatientAlice,
23
+ bed: mockWardBeds[0],
24
+ inpatientAdmission: mockInpatientAdmissionAlice,
25
+ visit: mockVisitAlice,
26
+ inpatientRequest: null,
27
+ };
28
+ renderWithSwr(<MaternalWardPatientCard wardPatient={alice} childrenOfWardPatientInSameBed={[]} />);
29
+
30
+ const patientName = screen.queryByText('Alice Johnson');
31
+ expect(patientName).toBeInTheDocument();
32
+ });
33
+
34
+ it('renders a patient with another child in same bed', () => {
35
+ const alice: WardPatient = {
36
+ patient: mockPatientAlice,
37
+ bed: mockWardBeds[0],
38
+ inpatientAdmission: mockInpatientAdmissionAlice,
39
+ visit: mockVisitAlice,
40
+ inpatientRequest: null,
41
+ };
42
+ renderWithSwr(<MaternalWardPatientCard wardPatient={alice} childrenOfWardPatientInSameBed={[alice]} />);
43
+
44
+ const bedDivider = screen.queryByText('Mother / Child');
45
+ expect(bedDivider).toBeInTheDocument();
46
+ });
47
+ });
@@ -0,0 +1,48 @@
1
+ import { ErrorState, useAppContext } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { type InpatientRequest, type WardViewContext } from '../../types';
5
+ import AdmissionRequestNoteRow from '../../ward-patient-card/card-rows/admission-request-note-row.component';
6
+ import CodedObsTagsRow from '../../ward-patient-card/card-rows/coded-obs-tags-row.component';
7
+ import MotherChildRow from '../../ward-patient-card/card-rows/mother-child-row.component';
8
+ import WardPatientSkeletonText from '../../ward-patient-card/row-elements/ward-patient-skeleton-text';
9
+ import AdmissionRequestCard from '../../ward-workspace/admission-request-card/admission-request-card.component';
10
+
11
+ function MaternalWardPendingPatients() {
12
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
13
+ const { t } = useTranslation();
14
+ const { inpatientRequestResponse } = wardPatientGroupDetails ?? {};
15
+ const {
16
+ inpatientRequests,
17
+ isLoading: isLoadingInpatientRequests,
18
+ error: errorFetchingInpatientRequests,
19
+ } = inpatientRequestResponse ?? {};
20
+
21
+ return isLoadingInpatientRequests ? (
22
+ <WardPatientSkeletonText />
23
+ ) : errorFetchingInpatientRequests ? (
24
+ <ErrorState headerTitle={t('admissionRequests', 'Admission requests')} error={errorFetchingInpatientRequests} />
25
+ ) : (
26
+ <>
27
+ {inpatientRequests?.map((request: InpatientRequest, i) => {
28
+ const wardPatient = {
29
+ patient: request.patient,
30
+ visit: request.visit,
31
+ bed: null,
32
+ inpatientRequest: request,
33
+ inpatientAdmission: null,
34
+ };
35
+
36
+ return (
37
+ <AdmissionRequestCard key={`admission-request-card-${i}`} wardPatient={wardPatient}>
38
+ <CodedObsTagsRow id="pregnancy-complications" {...wardPatient} />
39
+ <MotherChildRow wardPatient={wardPatient} childrenOfWardPatientInSameBed={[]} />
40
+ <AdmissionRequestNoteRow id={'admission-request-note'} wardPatient={wardPatient} />
41
+ </AdmissionRequestCard>
42
+ );
43
+ })}
44
+ </>
45
+ );
46
+ }
47
+
48
+ export default MaternalWardPendingPatients;