@kenyaemr/esm-ward-app 7.0.2-pre.66 → 7.0.2-pre.68

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 (61) hide show
  1. package/.turbo/turbo-build.log +23 -29
  2. package/dist/130.js +1 -1
  3. package/dist/130.js.map +1 -1
  4. package/dist/152.js +1 -0
  5. package/dist/152.js.map +1 -0
  6. package/dist/255.js +2 -0
  7. package/dist/{697.js.LICENSE.txt → 255.js.LICENSE.txt} +0 -6
  8. package/dist/255.js.map +1 -0
  9. package/dist/303.js +1 -0
  10. package/dist/303.js.map +1 -0
  11. package/dist/574.js +1 -1
  12. package/dist/589.js +1 -0
  13. package/dist/589.js.map +1 -0
  14. package/dist/695.js +2 -0
  15. package/dist/695.js.LICENSE.txt +5 -0
  16. package/dist/695.js.map +1 -0
  17. package/dist/kenyaemr-esm-ward-app.js +1 -1
  18. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +131 -35
  19. package/dist/kenyaemr-esm-ward-app.js.map +1 -1
  20. package/dist/main.js +1 -1
  21. package/dist/main.js.map +1 -1
  22. package/dist/routes.json +1 -1
  23. package/package.json +1 -1
  24. package/src/beds/occupied-bed.component.tsx +7 -12
  25. package/src/beds/occupied-bed.scss +1 -1
  26. package/src/beds/occupied-bed.test.tsx +14 -5
  27. package/src/config-schema.ts +173 -7
  28. package/src/constant.ts +1 -0
  29. package/src/hooks/useAdmittedPatients.ts +13 -0
  30. package/src/hooks/useConcept.ts +11 -0
  31. package/src/hooks/useInpatientRequest.ts +13 -0
  32. package/src/hooks/useObs.ts +21 -0
  33. package/src/index.ts +2 -0
  34. package/src/routes.json +10 -3
  35. package/src/types/index.ts +40 -2
  36. package/src/ward-patient-card/row-elements/ward-patient-bed-number.tsx +3 -0
  37. package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +80 -0
  38. package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +52 -0
  39. package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +58 -0
  40. package/src/ward-patient-card/ward-patient-card-row.resources.tsx +12 -5
  41. package/src/ward-patient-card/ward-patient-card.scss +17 -0
  42. package/src/ward-patient-card/ward-patient-card.tsx +2 -2
  43. package/src/ward-view/ward-bed.component.tsx +6 -9
  44. package/src/ward-view/ward-view.component.tsx +77 -31
  45. package/src/ward-view/ward-view.scss +0 -15
  46. package/src/ward-view/ward-view.test.tsx +12 -0
  47. package/src/ward-view-header/admission-requests-bar.component.tsx +46 -0
  48. package/src/ward-view-header/admission-requests-bar.test.tsx +42 -0
  49. package/src/ward-view-header/admission-requests.scss +42 -0
  50. package/src/ward-view-header/ward-view-header.component.tsx +18 -0
  51. package/src/ward-view-header/ward-view-header.scss +8 -0
  52. package/src/ward-workspace/admission-request-card.component.tsx +23 -0
  53. package/src/ward-workspace/admission-request-card.scss +34 -0
  54. package/src/ward-workspace/admission-request-workspace.test.tsx +38 -0
  55. package/src/ward-workspace/admission-requests-workspace.component.tsx +21 -0
  56. package/src/ward-workspace/admission-requests-workspace.scss +13 -0
  57. package/translations/en.json +1 -0
  58. package/dist/49.js +0 -1
  59. package/dist/49.js.map +0 -1
  60. package/dist/697.js +0 -2
  61. package/dist/697.js.map +0 -1
@@ -13,6 +13,8 @@ import wardPatientAddress from './row-elements/ward-patient-header-address';
13
13
  import WardPatientName from './row-elements/ward-patient-name';
14
14
  import React from 'react';
15
15
  import styles from './ward-patient-card.scss';
16
+ import wardPatientObs from './row-elements/ward-patient-obs';
17
+ import wardPatientCodedObsTags from './row-elements/ward-patient-coded-obs-tags';
16
18
 
17
19
  export function usePatientCardRows(location: string) {
18
20
  const { wardPatientCards } = useConfig<WardConfigObject>();
@@ -48,11 +50,11 @@ export function usePatientCardRows(location: string) {
48
50
  return slot;
49
51
  });
50
52
 
51
- const WardPatientCardRow: React.FC<WardPatientCardProps> = ({ patient, bed }) => {
53
+ const WardPatientCardRow: React.FC<WardPatientCardProps> = (props) => {
52
54
  return (
53
- <div className={rowType == 'header' ? styles.wardPatientCardHeader : ''}>
55
+ <div className={styles.wardPatientCardRow + ' ' + (rowType == 'header' ? styles.wardPatientCardHeader : '')}>
54
56
  {patientCardElements.map((PatientCardElement, i) => (
55
- <PatientCardElement patient={patient} bed={bed} key={i} />
57
+ <PatientCardElement {...props} key={i} />
56
58
  ))}
57
59
  </div>
58
60
  );
@@ -78,8 +80,13 @@ function getPatientCardElementFromDefinition(
78
80
  case 'patient-age':
79
81
  return WardPatientAge;
80
82
  case 'patient-address': {
81
- // TODO: configure address fields to pass in
82
- return wardPatientAddress(config);
83
+ return wardPatientAddress(config.address);
84
+ }
85
+ case 'patient-obs': {
86
+ return wardPatientObs(config.obs);
87
+ }
88
+ case 'patient-coded-obs-tags': {
89
+ return wardPatientCodedObsTags(config.codedObsTags);
83
90
  }
84
91
  }
85
92
  }
@@ -1,5 +1,6 @@
1
1
  @use '@carbon/styles/scss/spacing';
2
2
  @use '@carbon/styles/scss/type';
3
+ @use '@carbon/colors';
3
4
  @import '~@openmrs/esm-styleguide/src/vars';
4
5
 
5
6
  .wardPatientCard {
@@ -11,9 +12,21 @@
11
12
  align-items: center;
12
13
  gap: spacing.$spacing-02;
13
14
  background-color: $ui-02;
15
+
16
+ > .wardPatientCardRow:not(:first-child) {
17
+ border-top: 1px colors.$gray-20 solid;
18
+ }
19
+ }
20
+
21
+ .wardPatientCardRow {
22
+ width: 100%;
14
23
  padding: spacing.$spacing-04;
15
24
  }
16
25
 
26
+ .wardPatientCardRow:empty {
27
+ display: none;
28
+ }
29
+
17
30
  .wardPatientCardHeader {
18
31
  @extend .dotSeparatedChildren;
19
32
  display: flex;
@@ -59,6 +72,10 @@
59
72
  gap: spacing.$spacing-02;
60
73
  }
61
74
 
75
+ .wardPatientObsLabel {
76
+ padding-right: spacing.$spacing-02;
77
+ }
78
+
62
79
  .dotSeparatedChildren {
63
80
  > div:not(div:first-of-type) {
64
81
  display: flex;
@@ -10,8 +10,8 @@ const WardPatientCard: React.FC<WardPatientCardProps> = (props) => {
10
10
 
11
11
  return (
12
12
  <div className={styles.wardPatientCard}>
13
- {patientCardRows.map((WardPatientCardRow) => (
14
- <WardPatientCardRow {...props} />
13
+ {patientCardRows.map((WardPatientCardRow, i) => (
14
+ <WardPatientCardRow key={i} {...props} />
15
15
  ))}
16
16
  </div>
17
17
  );
@@ -1,18 +1,15 @@
1
- import { type Patient } from '@openmrs/esm-framework';
1
+ import { type Visit, type Patient } from '@openmrs/esm-framework';
2
2
  import React from 'react';
3
3
  import EmptyBed from '../beds/empty-bed.component';
4
- import { type Bed } from '../types';
4
+ import { type WardPatient, type Bed } from '../types';
5
5
  import OccupiedBed from '../beds/occupied-bed.component';
6
-
7
- interface WardBedProps {
6
+ export interface WardBedProps {
7
+ wardPatients: Array<WardPatient>;
8
8
  bed: Bed;
9
- patients: Patient[];
10
9
  }
11
10
 
12
- const WardBed: React.FC<WardBedProps> = ({ bed, patients }) => {
13
- return patients?.length > 0 ?
14
- <OccupiedBed bed={bed} patients={patients} />
15
- : <EmptyBed bed={bed} />;
11
+ const WardBed: React.FC<WardBedProps> = ({ bed, wardPatients }) => {
12
+ return wardPatients?.length > 0 ? <OccupiedBed bed={bed} wardPatients={wardPatients} /> : <EmptyBed bed={bed} />;
16
13
  };
17
14
 
18
15
  export default WardBed;
@@ -1,6 +1,6 @@
1
1
  import { InlineNotification } from '@carbon/react';
2
- import { useFeatureFlag, useLocations, useSession, type Location } from '@openmrs/esm-framework';
3
- import React from 'react';
2
+ import { WorkspaceContainer, useFeatureFlag, useLocations, useSession, type Location } from '@openmrs/esm-framework';
3
+ import React, { useMemo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { useParams } from 'react-router-dom';
6
6
  import EmptyBedSkeleton from '../beds/empty-bed-skeleton';
@@ -8,6 +8,9 @@ import { useAdmissionLocation } from '../hooks/useAdmissionLocation';
8
8
  import WardBed from './ward-bed.component';
9
9
  import { bedLayoutToBed, filterBeds } from './ward-view.resource';
10
10
  import styles from './ward-view.scss';
11
+ import WardViewHeader from '../ward-view-header/ward-view-header.component';
12
+ import { type AdmittedPatient, type WardPatient } from '../types';
13
+ import { useAdmittedPatients } from '../hooks/useAdmittedPatients';
11
14
 
12
15
  const WardView = () => {
13
16
  const { locationUuid: locationUuidFromUrl } = useParams();
@@ -23,46 +26,80 @@ const WardView = () => {
23
26
  return <></>;
24
27
  }
25
28
 
26
- return (
29
+ return invalidLocation ? (
30
+ <InlineNotification
31
+ kind="error"
32
+ lowContrast={true}
33
+ title={t('invalidLocationSpecified', 'Invalid location specified')}
34
+ subtitle={t('unknownLocationUuid', 'Unknown location uuid: {{locationUuidFromUrl}}', {
35
+ locationUuidFromUrl,
36
+ })}
37
+ />
38
+ ) : (
27
39
  <div className={styles.wardView}>
28
- <div className={styles.wardViewHeader}>
29
- <div className={styles.wardViewHeaderLocationDisplay}>
30
- <h4>{location?.display}</h4>
31
- </div>
32
- <div className={styles.wardViewHeaderAdmissionRequestMenuBar}>{/* TODO: Admission Request bar */}</div>
33
- </div>
40
+ <WardViewHeader location={location} />
34
41
  <div className={styles.wardViewMain}>
35
- {invalidLocation ? (
36
- <InlineNotification
37
- kind="error"
38
- lowContrast={true}
39
- title={t('invalidLocationSpecified', 'Invalid location specified')}
40
- subtitle={t('unknownLocationUuid', 'Unknown location uuid: {{locationUuidFromUrl}}', {
41
- locationUuidFromUrl,
42
- })}
43
- />
44
- ) : (
45
- <WardViewByLocation location={location} />
46
- )}
42
+ <WardViewByLocation location={location} />
47
43
  </div>
44
+ <WorkspaceContainer contextKey="ward" />
48
45
  </div>
49
46
  );
50
47
  };
51
48
 
52
49
  const WardViewByLocation = ({ location }: { location: Location }) => {
53
- const { admissionLocation, isLoading, error } = useAdmissionLocation(location.uuid);
50
+ const {
51
+ admissionLocation,
52
+ isLoading: isLoadingLocation,
53
+ error: errorLoadingLocation,
54
+ } = useAdmissionLocation(location.uuid);
55
+ const {
56
+ admittedPatients,
57
+ isLoading: isLoadingPatients,
58
+ error: errorLoadingPatients,
59
+ } = useAdmittedPatients(location.uuid);
54
60
  const { t } = useTranslation();
61
+ const admittedPatientsByUuid = useMemo(() => {
62
+ const map = new Map<string, AdmittedPatient>();
63
+ for (const admittedPatient of admittedPatients ?? []) {
64
+ map.set(admittedPatient.patient.uuid, admittedPatient);
65
+ }
66
+ return map;
67
+ }, [admittedPatients]);
55
68
 
56
- if (admissionLocation) {
69
+ if (admissionLocation != null && admittedPatients != null) {
57
70
  const bedLayouts = filterBeds(admissionLocation);
58
71
 
72
+ const wardBeds = bedLayouts.map((bedLayout, i) => {
73
+ const { patients } = bedLayout;
74
+ const bed = bedLayoutToBed(bedLayout);
75
+ const wardPatients: WardPatient[] = patients.map((patient) => {
76
+ const admittedPatient = admittedPatientsByUuid.get(patient.uuid);
77
+
78
+ if (admittedPatient) {
79
+ // ideally, we can just use the patient object within admittedPatient
80
+ // and not need the one from bedLayouts, however, the emr api
81
+ // does not respect custom representation right now and does not return
82
+ // all required fields for the patient object
83
+ return { ...admittedPatient, admitted: true };
84
+ }
85
+
86
+ // patient assigned a bed but *not* admitted
87
+ // TODO: get the patient's visit and current location
88
+ return {
89
+ patient,
90
+ visit: null,
91
+ admitted: true,
92
+ currentLocation: null,
93
+ timeSinceAdmissionInMinutes: null,
94
+ timeAtInpatientLocationInMinutes: null,
95
+ };
96
+ });
97
+ return <WardBed key={bed.uuid} bed={bed} wardPatients={wardPatients} />;
98
+ });
99
+
59
100
  return (
60
101
  <>
61
- {bedLayouts.map((bedLayout, i) => {
62
- const { patient } = bedLayout;
63
- const bed = bedLayoutToBed(bedLayout);
64
- return <WardBed key={bed.uuid} bed={bed} patients={patient ? [patient] : null} />;
65
- })}
102
+ {wardBeds}
66
103
  {bedLayouts.length == 0 && (
67
104
  <InlineNotification
68
105
  kind="warning"
@@ -72,7 +109,7 @@ const WardViewByLocation = ({ location }: { location: Location }) => {
72
109
  )}
73
110
  </>
74
111
  );
75
- } else if (isLoading) {
112
+ } else if (isLoadingLocation || isLoadingPatients) {
76
113
  return (
77
114
  <>
78
115
  {Array(20)
@@ -82,18 +119,27 @@ const WardViewByLocation = ({ location }: { location: Location }) => {
82
119
  ))}
83
120
  </>
84
121
  );
85
- } else {
122
+ } else if (errorLoadingLocation) {
86
123
  return (
87
124
  <InlineNotification
88
125
  kind="error"
89
126
  lowContrast={true}
90
127
  title={t('errorLoadingWardLocation', 'Error loading ward location')}
91
128
  subtitle={
92
- error?.message ??
129
+ errorLoadingLocation?.message ??
93
130
  t('invalidWardLocation', 'Invalid ward location: {{location}}', { location: location.display })
94
131
  }
95
132
  />
96
133
  );
134
+ } else {
135
+ return (
136
+ <InlineNotification
137
+ kind="error"
138
+ lowContrast={true}
139
+ title={t('errorLoadingPatients', 'Error loading admitted patients')}
140
+ subtitle={errorLoadingPatients?.message}
141
+ />
142
+ );
97
143
  }
98
144
  };
99
145
 
@@ -12,21 +12,6 @@
12
12
  padding: 0 spacing.$spacing-05;
13
13
  }
14
14
 
15
- .wardViewHeader {
16
- display: flex;
17
- margin: spacing.$spacing-05 0;
18
- }
19
-
20
- .wardViewHeaderLocationDisplay {
21
- flex: 1;
22
- }
23
-
24
- .wardViewHeaderAdmissionRequestMenuBar {
25
- background-color: black;
26
- color: white;
27
- padding: 4px 0 4px 12px;
28
- }
29
-
30
15
  .wardViewMain {
31
16
  background-color: #e4e4e4;
32
17
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
@@ -16,6 +16,7 @@ import { configSchema } from '../config-schema';
16
16
  import { useAdmissionLocation } from '../hooks/useAdmissionLocation';
17
17
  import WardView from './ward-view.component';
18
18
  import { mockPatientAlice } from '../../../../__mocks__/patient.mock';
19
+ import { useAdmittedPatients } from '../hooks/useAdmittedPatients';
19
20
 
20
21
  jest.replaceProperty(mockPatientAlice.person as Person, 'preferredName', {
21
22
  uuid: '',
@@ -52,6 +53,10 @@ const mockedUseParams = useParams as jest.Mock;
52
53
  jest.mock('../hooks/useAdmissionLocation', () => ({
53
54
  useAdmissionLocation: jest.fn(),
54
55
  }));
56
+ jest.mock('../hooks/useAdmittedPatients', () => ({
57
+ useAdmittedPatients: jest.fn(),
58
+ }));
59
+
55
60
  jest.mocked(useAdmissionLocation).mockReturnValue({
56
61
  error: undefined,
57
62
  mutate: jest.fn(),
@@ -59,6 +64,13 @@ jest.mocked(useAdmissionLocation).mockReturnValue({
59
64
  isLoading: false,
60
65
  admissionLocation: mockAdmissionLocation,
61
66
  });
67
+ jest.mocked(useAdmittedPatients).mockReturnValue({
68
+ error: undefined,
69
+ mutate: jest.fn(),
70
+ isValidating: false,
71
+ isLoading: false,
72
+ admittedPatients: [],
73
+ });
62
74
 
63
75
  describe('WardView:', () => {
64
76
  it('renders the session location when no location provided in URL', () => {
@@ -0,0 +1,46 @@
1
+ import { SkeletonIcon } from '@carbon/react';
2
+ import { Movement } from '@carbon/react/icons';
3
+ import { launchWorkspace, showNotification, type Location } from '@openmrs/esm-framework';
4
+ import React from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { useInpatientRequest } from '../hooks/useInpatientRequest';
7
+ import styles from './admission-requests.scss';
8
+
9
+ interface AdmissionRequestsBarProps {
10
+ location: Location;
11
+ }
12
+
13
+ const AdmissionRequestsBar: React.FC<AdmissionRequestsBarProps> = ({ location }) => {
14
+ const { inpatientRequests, isLoading, error } = useInpatientRequest(location.uuid);
15
+ const admissionRequests = inpatientRequests?.filter((request) => request.type == 'ADMISSION');
16
+ const { t } = useTranslation();
17
+
18
+ if (isLoading) {
19
+ return <SkeletonIcon className={styles.skeleton} />;
20
+ }
21
+
22
+ if (error) {
23
+ showNotification({
24
+ kind: 'error',
25
+ title: t('errorLoadingPatientAdmissionRequests', 'Error Loading Patient Admission Requests'),
26
+ description: error.message,
27
+ });
28
+ return <></>;
29
+ }
30
+
31
+ return admissionRequests.length > 0 ? (
32
+ <div className={styles.admissionRequestsContainer}>
33
+ <Movement className={styles.movementIcon} size="24" />
34
+ <span className={styles.content}>{admissionRequests.length} admission requests</span>
35
+ <button
36
+ className={styles.manageButton}
37
+ onClick={() => launchWorkspace('admission-requests-cards', { admissionRequests })}>
38
+ Manage
39
+ </button>
40
+ </div>
41
+ ) : (
42
+ <></>
43
+ );
44
+ };
45
+
46
+ export default AdmissionRequestsBar;
@@ -0,0 +1,42 @@
1
+ import userEvent from '@testing-library/user-event';
2
+ import { renderWithSwr } from '../../../../tools/test-utils';
3
+ import { screen } from '@testing-library/react';
4
+ import { launchWorkspace } from '@openmrs/esm-framework';
5
+ import React from 'react';
6
+ import AdmissionRequestsBar from './admission-requests-bar.component';
7
+ import { mockInpatientRequest } from '../../../../__mocks__/ward-patient';
8
+ import { useInpatientRequest } from '../hooks/useInpatientRequest';
9
+ import { mockLocationInpatientWard } from '../../../../__mocks__/locations.mock';
10
+
11
+ jest.mock('@openmrs/esm-framework', () => {
12
+ return {
13
+ ...jest.requireActual('@openmrs/esm-framework'),
14
+ launchWorkspace: jest.fn(),
15
+ };
16
+ });
17
+
18
+ jest.mock('../hooks/useInpatientRequest', () => ({
19
+ useInpatientRequest: jest.fn(),
20
+ }));
21
+ const mockInpatientRequestResponse = {
22
+ error: undefined,
23
+ mutate: jest.fn(),
24
+ isValidating: false,
25
+ isLoading: false,
26
+ inpatientRequests: [mockInpatientRequest],
27
+ };
28
+ jest.mocked(useInpatientRequest).mockReturnValue(mockInpatientRequestResponse);
29
+
30
+ describe('Admission Requests Button', () => {
31
+ it('call launch workspace when clicked on manage button', async () => {
32
+ const user = userEvent.setup();
33
+ renderWithSwr(<AdmissionRequestsBar location={mockLocationInpatientWard} />);
34
+ await user.click(screen.getByRole('button', { name: /manage/i }));
35
+ expect(launchWorkspace).toHaveBeenCalled();
36
+ });
37
+
38
+ it('there should be one admission request', () => {
39
+ const { getByText } = renderWithSwr(<AdmissionRequestsBar location={mockLocationInpatientWard} />);
40
+ expect(getByText('1 admission requests')).toBeInTheDocument();
41
+ });
42
+ });
@@ -0,0 +1,42 @@
1
+ @use '@carbon/styles/scss/type';
2
+ @use '@carbon/styles/scss/spacing';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .admissionRequestsContainer {
6
+ width: fit-content;
7
+ border-left: 2px solid $color-blue-60-2;
8
+ background-color: $color-gray-70;
9
+ display: flex;
10
+ align-items: center;
11
+ padding: spacing.$spacing-02;
12
+ }
13
+
14
+ .movementIcon {
15
+ padding: spacing.$spacing-02;
16
+ border-radius: 50%;
17
+ fill: $ui-03;
18
+ background-color: $color-blue-60-2;
19
+ }
20
+
21
+ .manageButton {
22
+ background-color: transparent;
23
+ border: none;
24
+ color: $inverse-link;
25
+ cursor: pointer;
26
+ margin-left: spacing.$spacing-04;
27
+ &::after {
28
+ content: '→';
29
+ padding: spacing.$spacing-02;
30
+ }
31
+ }
32
+
33
+ .content {
34
+ @include type.type-style('heading-compact-01');
35
+ color: $ui-02;
36
+ margin-left: spacing.$spacing-02;
37
+ }
38
+
39
+ .skeleton {
40
+ height: 20px;
41
+ width: 120px;
42
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import styles from './ward-view-header.scss';
3
+ import AdmissionRequestsBar from './admission-requests-bar.component';
4
+ import { type Location } from '@openmrs/esm-framework';
5
+
6
+ interface WardViewHeaderProps {
7
+ location: Location;
8
+ }
9
+ const WardViewHeader: React.FC<WardViewHeaderProps> = ({ location }) => {
10
+ return (
11
+ <div className={styles.wardViewHeader}>
12
+ <h4>{location.display}</h4>
13
+ <AdmissionRequestsBar location={location} />
14
+ </div>
15
+ );
16
+ };
17
+
18
+ export default WardViewHeader;
@@ -0,0 +1,8 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+
3
+ .wardViewHeader {
4
+ margin: spacing.$spacing-05 0;
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: space-between;
8
+ }
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import styles from './admission-request-card.scss';
3
+ import { useParams } from 'react-router-dom';
4
+ import { type Patient } from '@openmrs/esm-framework';
5
+ import { usePatientCardRows } from '../ward-patient-card/ward-patient-card-row.resources';
6
+
7
+ interface AdmissionRequestCardProps {
8
+ patient: Patient;
9
+ }
10
+
11
+ const AdmissionRequestCard: React.FC<AdmissionRequestCardProps> = ({ patient }) => {
12
+ const { locationUuid } = useParams();
13
+ const admissionPatientCardSlots = usePatientCardRows(locationUuid);
14
+ return (
15
+ <div className={styles.admissionRequestCardHeader}>
16
+ {admissionPatientCardSlots.map((AdmissionPatientCard, i) => (
17
+ <AdmissionPatientCard key={i} patient={patient} bed={null} visit={null} />
18
+ ))}
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default AdmissionRequestCard;
@@ -0,0 +1,34 @@
1
+ @use '@carbon/styles/scss/type';
2
+ @use '@carbon/styles/scss/spacing';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .admissionRequestCardHeader {
6
+ background-color: $ui-03;
7
+ padding: spacing.$spacing-03;
8
+ border: 1px solid $color-gray-30;
9
+ height: fit-content;
10
+ color: $color-gray-70;
11
+ display: flex;
12
+ align-items: center;
13
+ flex-wrap: wrap;
14
+ > div:not(:first-of-type)::before {
15
+ content: '·';
16
+ padding: spacing.$spacing-02;
17
+ }
18
+ }
19
+
20
+ .patientName,
21
+ .patientGender,
22
+ .patientAge {
23
+ @include type.type-style('heading-01');
24
+ }
25
+
26
+ .patientName {
27
+ color: $color-gray-100;
28
+ }
29
+
30
+ .time,
31
+ .city,
32
+ .ward {
33
+ @include type.type-style('body-compact-01');
34
+ }
@@ -0,0 +1,38 @@
1
+ import { renderWithSwr } from '../../../../tools/test-utils';
2
+ import React from 'react';
3
+ import AdmissionRequestsWorkspace from './admission-requests-workspace.component';
4
+ import {
5
+ type ConfigSchema,
6
+ type Person,
7
+ closeWorkspace,
8
+ getDefaultsFromConfigSchema,
9
+ useConfig,
10
+ } from '@openmrs/esm-framework';
11
+ import { configSchema } from '../config-schema';
12
+ import { useInpatientRequest } from '../hooks/useInpatientRequest';
13
+ import { mockInpatientRequest } from '../../../../__mocks__/ward-patient';
14
+
15
+ jest.replaceProperty(mockInpatientRequest.patient.person as Person, 'preferredName', {
16
+ uuid: '',
17
+ givenName: 'Alice',
18
+ familyName: 'Johnson',
19
+ });
20
+
21
+ jest.mocked(useConfig).mockReturnValue({
22
+ ...getDefaultsFromConfigSchema<ConfigSchema>(configSchema),
23
+ });
24
+
25
+ jest.mock('@openmrs/esm-framework', () => {
26
+ return {
27
+ ...jest.requireActual('@openmrs/esm-framework'),
28
+ closeWorkspace: jest.fn(),
29
+ };
30
+ });
31
+
32
+ describe('Admission Requests Workspace', () => {
33
+ it('should render a admission request card', () => {
34
+ const { getByText } = renderWithSwr(<AdmissionRequestsWorkspace admissionRequests={[mockInpatientRequest]} />);
35
+ const { givenName, familyName } = mockInpatientRequest.patient.person!.preferredName!;
36
+ expect(getByText(givenName + ' ' + familyName)).toBeInTheDocument();
37
+ });
38
+ });
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import styles from './admission-requests-workspace.scss';
3
+ import AdmissionRequestCard from './admission-request-card.component';
4
+ import { type InpatientRequest } from '../types';
5
+
6
+ interface AdmissionRequestsWorkspaceProps {
7
+ admissionRequests: InpatientRequest[];
8
+ }
9
+ const AdmissionRequestsWorkspace: React.FC<AdmissionRequestsWorkspaceProps> = ({ admissionRequests }) => {
10
+ return (
11
+ <div className={styles.admissionRequestsWorkspaceContainer}>
12
+ <div className={styles.admissionRequestsWorkspace}>
13
+ {admissionRequests.map((admissionRequest) => (
14
+ <AdmissionRequestCard patient={admissionRequest.patient} />
15
+ ))}
16
+ </div>
17
+ </div>
18
+ );
19
+ };
20
+
21
+ export default AdmissionRequestsWorkspace;
@@ -0,0 +1,13 @@
1
+ @use '@carbon/styles/scss/type';
2
+ @use '@carbon/styles/scss/spacing';
3
+ @import '~@openmrs/esm-styleguide/src/vars';
4
+
5
+ .admissionRequestsWorkspaceContainer {
6
+ min-height: var(--desktop-workspace-window-height);
7
+ }
8
+ .admissionRequestsWorkspace {
9
+ padding: spacing.$spacing-04;
10
+ display: grid;
11
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
12
+ gap: 1rem;
13
+ }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "bedShare": "Bed share",
3
3
  "emptyBed": "Empty bed",
4
+ "errorLoadingPatientAdmissionRequests": "Error Loading Patient Admission Requests",
4
5
  "errorLoadingWardLocation": "Error loading ward location",
5
6
  "invalidLocationSpecified": "Invalid location specified",
6
7
  "invalidWardLocation": "Invalid ward location: {{location}}",