@kenyaemr/esm-ward-app 7.0.3-pre.88 → 7.0.3-pre.94

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 (146) hide show
  1. package/.turbo/turbo-build.log +24 -16
  2. package/dist/130.js +1 -1
  3. package/dist/130.js.map +1 -1
  4. package/dist/169.js +1 -0
  5. package/dist/169.js.map +1 -0
  6. package/dist/269.js +1 -0
  7. package/dist/269.js.map +1 -0
  8. package/dist/346.js +1 -0
  9. package/dist/346.js.map +1 -0
  10. package/dist/348.js +1 -0
  11. package/dist/348.js.map +1 -0
  12. package/dist/466.js +1 -0
  13. package/dist/466.js.map +1 -0
  14. package/dist/501.js +1 -0
  15. package/dist/501.js.map +1 -0
  16. package/dist/574.js +1 -1
  17. package/dist/577.js +1 -0
  18. package/dist/577.js.map +1 -0
  19. package/dist/659.js +1 -0
  20. package/dist/659.js.map +1 -0
  21. package/dist/749.js +1 -0
  22. package/dist/749.js.map +1 -0
  23. package/dist/76.js +1 -0
  24. package/dist/76.js.map +1 -0
  25. package/dist/767.js +1 -0
  26. package/dist/767.js.map +1 -0
  27. package/dist/793.js +2 -0
  28. package/dist/793.js.map +1 -0
  29. package/dist/803.js +1 -0
  30. package/dist/803.js.map +1 -0
  31. package/dist/940.js +1 -0
  32. package/dist/940.js.map +1 -0
  33. package/dist/960.js +1 -0
  34. package/dist/960.js.map +1 -0
  35. package/dist/kenyaemr-esm-ward-app.js +1 -1
  36. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +330 -42
  37. package/dist/kenyaemr-esm-ward-app.js.map +1 -1
  38. package/dist/main.js +1 -1
  39. package/dist/main.js.map +1 -1
  40. package/dist/routes.json +1 -1
  41. package/package.json +2 -2
  42. package/src/action-menu-buttons/transfer-workspace-siderail.component.tsx +27 -0
  43. package/src/beds/empty-bed.component.tsx +1 -1
  44. package/src/beds/empty-bed.scss +6 -6
  45. package/src/beds/occupied-bed.component.tsx +5 -5
  46. package/src/beds/occupied-bed.scss +2 -3
  47. package/src/beds/occupied-bed.test.tsx +37 -21
  48. package/src/beds/unassigned-patient.component.tsx +20 -0
  49. package/src/beds/unassigned-patient.scss +6 -0
  50. package/src/config-schema-admission-request-note.ts +9 -0
  51. package/src/config-schema-extension-colored-obs-tags.ts +91 -0
  52. package/src/config-schema.ts +165 -231
  53. package/src/createDashboardLink.component.tsx +42 -0
  54. package/src/hooks/useAdmissionLocation.ts +12 -7
  55. package/src/hooks/useCurrentWardCardConfig.ts +32 -0
  56. package/src/hooks/useEmrConfiguration.ts +112 -0
  57. package/src/hooks/useInpatientAdmission.ts +28 -0
  58. package/src/hooks/useInpatientRequest.ts +39 -9
  59. package/src/hooks/useLocation.test.ts +38 -0
  60. package/src/hooks/useLocation.ts +9 -0
  61. package/src/hooks/useLocations.ts +54 -0
  62. package/src/hooks/useMostRecentObs.ts +27 -0
  63. package/src/hooks/useRestPatient.ts +18 -0
  64. package/src/hooks/useWardLocation.test.ts +108 -0
  65. package/src/hooks/useWardLocation.ts +26 -0
  66. package/src/index.ts +71 -4
  67. package/src/location-selector/location-selector.component.tsx +118 -0
  68. package/src/location-selector/location-selector.scss +48 -0
  69. package/src/root.component.tsx +2 -1
  70. package/src/routes.json +79 -12
  71. package/src/types/index.ts +87 -46
  72. package/src/ward-patient-card/card-rows/admission-request-note.extension.tsx +27 -0
  73. package/src/ward-patient-card/card-rows/colored-obs-tags-card-row.extension.tsx +13 -0
  74. package/src/ward-patient-card/row-elements/ward-patient-age.tsx +7 -13
  75. package/src/ward-patient-card/row-elements/ward-patient-bed-number.tsx +2 -2
  76. package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +51 -50
  77. package/src/ward-patient-card/row-elements/ward-patient-gender.component.tsx +27 -0
  78. package/src/ward-patient-card/row-elements/ward-patient-header-address.tsx +16 -15
  79. package/src/ward-patient-card/row-elements/ward-patient-identifier.tsx +53 -0
  80. package/src/ward-patient-card/row-elements/ward-patient-name.tsx +7 -7
  81. package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +4 -4
  82. package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +45 -44
  83. package/src/ward-patient-card/row-elements/ward-patient-time-on-ward.tsx +22 -0
  84. package/src/ward-patient-card/row-elements/ward-patient-time-since-admission.tsx +22 -0
  85. package/src/ward-patient-card/ward-patient-card-element.component.tsx +65 -0
  86. package/src/ward-patient-card/ward-patient-card.component.tsx +64 -0
  87. package/src/ward-patient-card/ward-patient-card.scss +61 -12
  88. package/src/ward-patient-workspace/ward-patient-action-button.extension.tsx +18 -0
  89. package/src/ward-patient-workspace/ward-patient.style.scss +11 -0
  90. package/src/ward-patient-workspace/ward-patient.workspace.tsx +51 -0
  91. package/src/ward-view/ward-bed.component.tsx +0 -1
  92. package/src/ward-view/ward-view.component.tsx +114 -76
  93. package/src/ward-view/ward-view.resource.ts +2 -2
  94. package/src/ward-view/ward-view.scss +4 -4
  95. package/src/ward-view/ward-view.test.tsx +76 -49
  96. package/src/ward-view-header/admission-requests-bar.component.tsx +29 -28
  97. package/src/ward-view-header/admission-requests-bar.test.tsx +11 -15
  98. package/src/ward-view-header/admission-requests.scss +20 -25
  99. package/src/ward-view-header/ward-view-header.component.tsx +7 -7
  100. package/src/ward-view-header/ward-view-header.scss +2 -2
  101. package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +29 -0
  102. package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +51 -0
  103. package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +16 -0
  104. package/src/ward-workspace/admission-request-card/admission-request-card.scss +49 -0
  105. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.scss +12 -0
  106. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +48 -0
  107. package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +61 -0
  108. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss +35 -0
  109. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +341 -0
  110. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +267 -0
  111. package/src/ward-workspace/admit-patient-form-workspace/types.ts +7 -0
  112. package/src/ward-workspace/patient-banner/patient-banner.component.tsx +29 -0
  113. package/src/ward-workspace/patient-banner/style.scss +23 -0
  114. package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +210 -0
  115. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +238 -0
  116. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.scss +73 -0
  117. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +44 -0
  118. package/src/ward-workspace/ward-patient-notes/form/notes-form.component.tsx +180 -0
  119. package/src/ward-workspace/ward-patient-notes/form/notes-form.scss +30 -0
  120. package/src/ward-workspace/ward-patient-notes/form/notes-form.test.tsx +116 -0
  121. package/src/ward-workspace/ward-patient-notes/history/note.component.tsx +53 -0
  122. package/src/ward-workspace/ward-patient-notes/history/notes-container.component.tsx +55 -0
  123. package/src/ward-workspace/ward-patient-notes/history/notes-container.test.tsx +84 -0
  124. package/src/ward-workspace/ward-patient-notes/history/styles.scss +61 -0
  125. package/src/ward-workspace/ward-patient-notes/notes-action-button.extension.tsx +18 -0
  126. package/src/ward-workspace/ward-patient-notes/notes.resource.ts +71 -0
  127. package/src/ward-workspace/ward-patient-notes/notes.workspace.tsx +25 -0
  128. package/src/ward-workspace/ward-patient-notes/types.ts +44 -0
  129. package/src/ward.resource.ts +25 -0
  130. package/translations/en.json +63 -2
  131. package/dist/443.js +0 -1
  132. package/dist/443.js.map +0 -1
  133. package/dist/589.js +0 -1
  134. package/dist/589.js.map +0 -1
  135. package/dist/695.js +0 -2
  136. package/dist/695.js.map +0 -1
  137. package/src/hooks/useAdmittedPatients.ts +0 -13
  138. package/src/ward-patient-card/row-elements/row-elements.scss +0 -16
  139. package/src/ward-patient-card/ward-patient-card-row.resources.tsx +0 -92
  140. package/src/ward-patient-card/ward-patient-card.tsx +0 -20
  141. package/src/ward-workspace/admission-request-card.component.tsx +0 -23
  142. package/src/ward-workspace/admission-request-card.scss +0 -34
  143. package/src/ward-workspace/admission-request-workspace.test.tsx +0 -38
  144. package/src/ward-workspace/admission-requests-workspace.component.tsx +0 -21
  145. package/src/ward-workspace/admission-requests-workspace.scss +0 -13
  146. /package/dist/{695.js.LICENSE.txt → 793.js.LICENSE.txt} +0 -0
@@ -1,58 +1,59 @@
1
- import { SkeletonText, Tag } from '@carbon/react';
2
- import { type OpenmrsResource, translateFrom } from '@openmrs/esm-framework';
1
+ import { SkeletonText } from '@carbon/react';
2
+ import { type OpenmrsResource, type Patient, translateFrom, type Visit } from '@openmrs/esm-framework';
3
3
  import React from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
- import { type PatientObsElementConfig } from '../../config-schema';
5
+ import { type ObsElementDefinition } from '../../config-schema';
6
6
  import { useObs } from '../../hooks/useObs';
7
- import { type WardPatientCardElement } from '../../types';
8
7
  import styles from '../ward-patient-card.scss';
9
8
  import { moduleName } from '../../constant';
10
9
  import { obsCustomRepresentation } from './ward-patient-obs.resource';
11
10
 
12
- const wardPatientObs = (config: PatientObsElementConfig) => {
13
- const WardPatientObs: WardPatientCardElement = ({ patient, visit }) => {
14
- const { conceptUuid, onlyWithinCurrentVisit, orderBy, limit, label, labelI18nModule: labelModule } = config;
15
- const { data, isLoading } = useObs({ patient: patient.uuid, concept: conceptUuid }, obsCustomRepresentation);
16
- const { t } = useTranslation();
11
+ export interface WardPatientObsProps {
12
+ config: ObsElementDefinition;
13
+ patient: Patient;
14
+ visit: Visit;
15
+ }
17
16
 
18
- if (isLoading) {
19
- return <SkeletonText />;
20
- } else {
21
- const obsToDisplay = data?.data?.results
22
- ?.filter((o) => {
23
- const matchVisit = !onlyWithinCurrentVisit || o.encounter.visit?.uuid == visit?.uuid;
24
- return matchVisit;
25
- })
26
- ?.sort((obsA, obsB) => {
27
- return (orderBy == 'descending' ? -1 : 1) * obsA.obsDatetime.localeCompare(obsB.obsDatetime);
28
- })
29
- ?.slice(0, limit ?? Number.MAX_VALUE);
17
+ const WardPatientObs: React.FC<WardPatientObsProps> = ({ config, patient, visit }) => {
18
+ const { conceptUuid, onlyWithinCurrentVisit, orderBy, limit, label, labelI18nModule: labelModule } = config;
19
+ const { data, isLoading } = useObs({ patient: patient.uuid, concept: conceptUuid }, obsCustomRepresentation);
20
+ const { t } = useTranslation();
30
21
 
31
- const labelToDisplay =
32
- label != null ? translateFrom(labelModule ?? moduleName, label) : obsToDisplay?.[0]?.concept?.display;
22
+ if (isLoading) {
23
+ return <SkeletonText />;
24
+ } else {
25
+ const obsToDisplay = data?.data?.results
26
+ ?.filter((o) => {
27
+ const matchVisit = !onlyWithinCurrentVisit || o.encounter.visit?.uuid == visit?.uuid;
28
+ return matchVisit;
29
+ })
30
+ ?.sort((obsA, obsB) => {
31
+ return (orderBy == 'descending' ? -1 : 1) * obsA.obsDatetime.localeCompare(obsB.obsDatetime);
32
+ })
33
+ ?.slice(0, limit == 0 ? Number.MAX_VALUE : limit);
33
34
 
34
- const obsNodes = obsToDisplay?.map((o) => {
35
- const { value } = o;
36
- const display: any = (value as OpenmrsResource)?.display ?? o.value;
37
- return <span key={o.uuid}> {display} </span>;
38
- });
35
+ const labelToDisplay =
36
+ label != null ? translateFrom(labelModule ?? moduleName, label) : obsToDisplay?.[0]?.concept?.display;
39
37
 
40
- if (obsNodes?.length > 0) {
41
- return (
42
- <div>
43
- <span className={styles.wardPatientObsLabel}>
44
- {labelToDisplay ? t('labelColon', '{{label}}:', { label: labelToDisplay }) : ''}
45
- </span>
46
- {obsNodes}
47
- </div>
48
- );
49
- } else {
50
- return null;
51
- }
52
- }
53
- };
38
+ const obsNodes = obsToDisplay?.map((o) => {
39
+ const { value } = o;
40
+ const display: any = (value as OpenmrsResource)?.display ?? o.value;
41
+ return <span key={o.uuid}> {display} </span>;
42
+ });
54
43
 
55
- return WardPatientObs;
44
+ if (obsNodes?.length > 0) {
45
+ return (
46
+ <div>
47
+ <span className={styles.wardPatientObsLabel}>
48
+ {labelToDisplay ? t('labelColon', '{{label}}:', { label: labelToDisplay }) : ''}
49
+ </span>
50
+ {obsNodes}
51
+ </div>
52
+ );
53
+ } else {
54
+ return null;
55
+ }
56
+ }
56
57
  };
57
58
 
58
- export default wardPatientObs;
59
+ export default WardPatientObs;
@@ -0,0 +1,22 @@
1
+ import { age } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { type Encounter } from '../../types';
5
+
6
+ export interface WardPatientTimeOnWardProps {
7
+ encounterAssigningToCurrentInpatientLocation: Encounter;
8
+ }
9
+
10
+ const WardPatientTimeOnWard: React.FC<WardPatientTimeOnWardProps> = ({
11
+ encounterAssigningToCurrentInpatientLocation,
12
+ }) => {
13
+ const { t } = useTranslation();
14
+ if (encounterAssigningToCurrentInpatientLocation) {
15
+ const timeOnWard = age(encounterAssigningToCurrentInpatientLocation.encounterDatetime);
16
+ return <div>{t('timeOnWard', 'Time on this ward: {{timeOnWard}}', { timeOnWard })}</div>;
17
+ } else {
18
+ return <></>;
19
+ }
20
+ };
21
+
22
+ export default WardPatientTimeOnWard;
@@ -0,0 +1,22 @@
1
+ import { age } from '@openmrs/esm-framework';
2
+ import React from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { type Encounter } from '../../types';
5
+
6
+ export interface WardPatientTimeSinceAdmissionProps {
7
+ firstAdmissionOrTransferEncounter: Encounter;
8
+ }
9
+
10
+ const WardPatientTimeSinceAdmission: React.FC<WardPatientTimeSinceAdmissionProps> = ({
11
+ firstAdmissionOrTransferEncounter,
12
+ }) => {
13
+ const { t } = useTranslation();
14
+ if (firstAdmissionOrTransferEncounter) {
15
+ const timeSinceAdmission = age(firstAdmissionOrTransferEncounter.encounterDatetime);
16
+ return <div>{t('timeSinceAdmission', 'Admitted: {{timeSinceAdmission}} ago', { timeSinceAdmission })}</div>;
17
+ } else {
18
+ return <></>;
19
+ }
20
+ };
21
+
22
+ export default WardPatientTimeSinceAdmission;
@@ -0,0 +1,65 @@
1
+ import { InlineNotification } from '@carbon/react';
2
+ import { useConfig } from '@openmrs/esm-framework';
3
+ import React from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { type WardConfigObject } from '../config-schema';
6
+ import { type WardPatient } from '../types';
7
+ import WardPatientAge from './row-elements/ward-patient-age';
8
+ import WardPatientAddress from './row-elements/ward-patient-header-address';
9
+ import WardPatientIdentifier from './row-elements/ward-patient-identifier';
10
+ import WardPatientObs from './row-elements/ward-patient-obs';
11
+ import WardPatientTimeOnWard from './row-elements/ward-patient-time-on-ward';
12
+ import WardPatientTimeSinceAdmission from './row-elements/ward-patient-time-since-admission';
13
+
14
+ export interface WardPatientCardElementProps extends WardPatient {
15
+ elementId: string;
16
+ }
17
+
18
+ export const WardPatientCardElement: React.FC<WardPatientCardElementProps> = ({
19
+ elementId,
20
+ patient,
21
+ visit,
22
+ inpatientAdmission,
23
+ }) => {
24
+ const { obsElementDefinitions, identifierElementDefinitions, addressElementDefinitions } =
25
+ useConfig<WardConfigObject>().wardPatientCards;
26
+ const { t } = useTranslation();
27
+ const { encounterAssigningToCurrentInpatientLocation, firstAdmissionOrTransferEncounter } = inpatientAdmission ?? {};
28
+
29
+ switch (elementId) {
30
+ case 'patient-age':
31
+ return <WardPatientAge patient={patient} />;
32
+ case 'time-on-ward': {
33
+ return (
34
+ <WardPatientTimeOnWard
35
+ encounterAssigningToCurrentInpatientLocation={encounterAssigningToCurrentInpatientLocation}
36
+ />
37
+ );
38
+ }
39
+ case 'time-since-admission': {
40
+ return <WardPatientTimeSinceAdmission firstAdmissionOrTransferEncounter={firstAdmissionOrTransferEncounter} />;
41
+ }
42
+ default: {
43
+ const obsConfig = obsElementDefinitions.find((elementDef) => elementDef.id === elementId);
44
+ const idConfig = identifierElementDefinitions.find((elementDef) => elementDef.id === elementId);
45
+ const addressConfig = addressElementDefinitions.find((elementDef) => elementDef.id === elementId);
46
+ if (obsConfig) {
47
+ return <WardPatientObs patient={patient} visit={visit} config={obsConfig} />;
48
+ } else if (idConfig) {
49
+ return <WardPatientIdentifier patient={patient} config={idConfig} />;
50
+ } else if (addressConfig) {
51
+ return <WardPatientAddress patient={patient} config={addressConfig} />;
52
+ } else {
53
+ return (
54
+ <InlineNotification kind="error">
55
+ {t(
56
+ 'invalidElementIdCopy',
57
+ 'The configuration provided is invalid. It contains the following unknown element ID:',
58
+ )}{' '}
59
+ {elementId}
60
+ </InlineNotification>
61
+ );
62
+ }
63
+ }
64
+ }
65
+ };
@@ -0,0 +1,64 @@
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';
9
+ import styles from './ward-patient-card.scss';
10
+
11
+ const WardPatientCard: WardPatientCard = (wardPatient) => {
12
+ const { patient, bed } = wardPatient;
13
+ const { id, headerRowElements, footerRowElements } = useCurrentWardCardConfig();
14
+
15
+ const headerExtensionSlotName =
16
+ id == 'default' ? 'ward-patient-card-header-slot' : `ward-patient-card-header-${id}-slot`;
17
+ const rowsExtensionSlotName = id == 'default' ? 'ward-patient-card-slot' : `ward-patient-card-${id}-slot`;
18
+ const footerExtensionSlotName =
19
+ id == 'default' ? 'ward-patient-card-footer-slot' : `ward-patient-card-footer-${id}-slot`;
20
+
21
+ return (
22
+ <div className={styles.wardPatientCard}>
23
+ <div className={classNames(styles.wardPatientCardRow, styles.wardPatientCardHeader)}>
24
+ {bed ? <WardPatientBedNumber bed={bed} /> : null}
25
+ <WardPatientName patient={patient} />
26
+ {headerRowElements.map((elementId, i) => (
27
+ <WardPatientCardElement
28
+ key={`ward-card-${patient.uuid}-header-${i}`}
29
+ elementId={elementId}
30
+ {...wardPatient}
31
+ />
32
+ ))}
33
+ <ExtensionSlot name={headerExtensionSlotName} state={wardPatient} />
34
+ </div>
35
+ <ExtensionSlot
36
+ name={rowsExtensionSlotName}
37
+ state={wardPatient}
38
+ className={classNames(styles.wardPatientCardRow, styles.wardPatientCardExtensionSlot)}
39
+ />
40
+ <div className={styles.wardPatientCardRow}>
41
+ {footerRowElements.map((elementId, i) => (
42
+ <WardPatientCardElement
43
+ key={`ward-card-${patient.uuid}-footer-${i}`}
44
+ elementId={elementId}
45
+ {...wardPatient}
46
+ />
47
+ ))}
48
+ <ExtensionSlot name={footerExtensionSlotName} state={wardPatient} />
49
+ </div>
50
+ <button
51
+ className={styles.wardPatientCardButton}
52
+ onClick={() => {
53
+ launchWorkspace<WardPatientWorkspaceProps>('ward-patient-workspace', {
54
+ wardPatient,
55
+ });
56
+ }}>
57
+ {/* Name will not be displayed; just there for a11y */}
58
+ {getPatientName(patient.person)}
59
+ </button>
60
+ </div>
61
+ );
62
+ };
63
+
64
+ export default WardPatientCard;
@@ -1,7 +1,7 @@
1
- @use '@carbon/styles/scss/spacing';
2
- @use '@carbon/styles/scss/type';
3
1
  @use '@carbon/colors';
4
- @import '~@openmrs/esm-styleguide/src/vars';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
5
 
6
6
  .wardPatientCard {
7
7
  @include type.type-style('body-compact-01');
@@ -10,23 +10,69 @@
10
10
  display: flex;
11
11
  flex-wrap: wrap;
12
12
  align-items: center;
13
- gap: spacing.$spacing-02;
13
+ gap: layout.$spacing-02;
14
14
  background-color: $ui-02;
15
15
 
16
+ position: relative; // this allows positioning the button correctly
17
+
16
18
  > .wardPatientCardRow:not(:first-child) {
17
19
  border-top: 1px colors.$gray-20 solid;
18
20
  }
19
21
  }
20
22
 
23
+ .wardPatientCardButton {
24
+ border: none;
25
+ padding: 0;
26
+
27
+ &::before {
28
+ content: '';
29
+ position: absolute;
30
+ inset: 0;
31
+ z-index: 1;
32
+ cursor: pointer;
33
+ border: 2px solid transparent;
34
+ transition: border-color 200ms;
35
+ }
36
+
37
+ &:hover::before,
38
+ &:focus::before {
39
+ border-color: $interactive-01;
40
+ }
41
+
42
+ &:focus {
43
+ outline: none;
44
+ }
45
+ }
46
+
47
+ .activeWardPatientCardButton {
48
+ &::before {
49
+ content: '';
50
+ position: absolute;
51
+ inset: 0;
52
+ z-index: 1;
53
+ cursor: pointer;
54
+ border: 2px solid $interactive-01;
55
+ transition: border-color 200ms;
56
+ }
57
+ }
58
+
21
59
  .wardPatientCardRow {
22
60
  width: 100%;
23
- padding: spacing.$spacing-04;
61
+ padding: layout.$spacing-04;
24
62
  }
25
63
 
26
64
  .wardPatientCardRow:empty {
27
65
  display: none;
28
66
  }
29
67
 
68
+ .wardPatientCardExtensionSlot {
69
+ display: none;
70
+
71
+ &:has(div:not(:empty)) {
72
+ display: block;
73
+ }
74
+ }
75
+
30
76
  .wardPatientCardHeader {
31
77
  @extend .dotSeparatedChildren;
32
78
  display: flex;
@@ -36,6 +82,7 @@
36
82
  .wardPatientName {
37
83
  @include type.type-style('heading-compact-02');
38
84
  color: $text-02;
85
+
39
86
  &::before {
40
87
  content: '' !important;
41
88
  }
@@ -46,12 +93,13 @@
46
93
  border-radius: 50%;
47
94
  color: $ui-02;
48
95
  background-color: $color-blue-60-2;
49
- padding: spacing.$spacing-04;
50
- width: spacing.$spacing-04;
51
- height: spacing.$spacing-04;
96
+ padding: layout.$spacing-04;
97
+ width: layout.$spacing-04;
98
+ height: layout.$spacing-04;
52
99
  display: flex;
53
100
  justify-content: center;
54
101
  align-items: center;
102
+
55
103
  &.empty {
56
104
  background-color: $color-blue-10;
57
105
  color: $color-blue-60-2;
@@ -69,20 +117,21 @@
69
117
  display: flex;
70
118
  flex-wrap: wrap;
71
119
  align-items: center;
72
- gap: spacing.$spacing-02;
120
+ gap: layout.$spacing-02;
73
121
  }
74
122
 
75
123
  .wardPatientObsLabel {
76
- padding-right: spacing.$spacing-02;
124
+ padding-right: layout.$spacing-02;
77
125
  }
78
126
 
79
127
  .dotSeparatedChildren {
80
- > div:not(div:first-of-type) {
128
+ > div:not(div:first-of-type):not(:empty) {
81
129
  display: flex;
82
130
  align-items: center;
131
+
83
132
  &::before {
84
133
  content: '·';
85
- padding: 0 spacing.$spacing-02;
134
+ padding: 0 layout.$spacing-02;
86
135
  }
87
136
  }
88
137
  }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { UserAvatarIcon } from '@openmrs/esm-framework';
4
+ import { ActionMenuButton, launchWorkspace } from '@openmrs/esm-framework';
5
+
6
+ export default function WardPatientActionButton() {
7
+ const { t } = useTranslation();
8
+
9
+ return (
10
+ <ActionMenuButton
11
+ getIcon={(props) => <UserAvatarIcon {...props} />}
12
+ label={t('Patient', 'patient')}
13
+ iconDescription={t('Patient', 'patient')}
14
+ handler={() => launchWorkspace('ward-patient-workspace')}
15
+ type={'ward'}
16
+ />
17
+ );
18
+ }
@@ -0,0 +1,11 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+
4
+ .workspaceContainer {
5
+ min-height: var(--desktop-workspace-window-height);
6
+ }
7
+
8
+ .headerPatientDetail {
9
+ @include type.type-style('body-compact-02');
10
+ margin: 0 layout.$spacing-02;
11
+ }
@@ -0,0 +1,51 @@
1
+ import { age, attach, ExtensionSlot, type Patient } from '@openmrs/esm-framework';
2
+ import React, { useEffect } from 'react';
3
+ import { type WardPatientWorkspaceProps } from '../types';
4
+ import styles from './ward-patient.style.scss';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { getGender } from '../ward-patient-card/row-elements/ward-patient-gender.component';
7
+
8
+ attach('ward-patient-workspace-header-slot', 'patient-vitals-info');
9
+
10
+ export default function WardPatientWorkspace({ setTitle, wardPatient: { patient } }: WardPatientWorkspaceProps) {
11
+ useEffect(() => {
12
+ setTitle(patient.person.display, <PatientWorkspaceTitle patient={patient} />);
13
+ }, []);
14
+
15
+ return (
16
+ <div className={styles.workspaceContainer}>
17
+ <WardPatientWorkspaceView patient={patient} />
18
+ </div>
19
+ );
20
+ }
21
+
22
+ interface WardPatientWorkspaceViewProps {
23
+ patient: Patient;
24
+ }
25
+
26
+ const WardPatientWorkspaceView: React.FC<WardPatientWorkspaceViewProps> = ({ patient }) => {
27
+ const extensionSlotState = { patient, patientUuid: patient.uuid };
28
+
29
+ return (
30
+ <>
31
+ <div>
32
+ <ExtensionSlot name="ward-patient-workspace-header-slot" state={extensionSlotState} />
33
+ </div>
34
+ <div>
35
+ <ExtensionSlot name="ward-patient-workspace-content-slot" state={extensionSlotState} />
36
+ </div>
37
+ </>
38
+ );
39
+ };
40
+
41
+ const PatientWorkspaceTitle: React.FC<WardPatientWorkspaceViewProps> = ({ patient }) => {
42
+ const { t } = useTranslation();
43
+
44
+ return (
45
+ <>
46
+ <div>{patient.person.display} &nbsp;</div>
47
+ <div className={styles.headerPatientDetail}>&middot; &nbsp; {getGender(t, patient.person?.gender)}</div>
48
+ <div className={styles.headerPatientDetail}>&middot; &nbsp; {age(patient.person?.birthdate)}</div>
49
+ </>
50
+ );
51
+ };
@@ -1,4 +1,3 @@
1
- import { type Visit, type Patient } from '@openmrs/esm-framework';
2
1
  import React from 'react';
3
2
  import EmptyBed from '../beds/empty-bed.component';
4
3
  import { type WardPatient, type Bed } from '../types';