@kenyaemr/esm-morgue-app 5.4.2-pre.2283 → 5.4.2-pre.2291

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 (193) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/dist/109.js +2 -0
  3. package/dist/109.js.map +1 -0
  4. package/dist/201.js +1 -0
  5. package/dist/201.js.map +1 -0
  6. package/dist/293.js +1 -0
  7. package/dist/293.js.map +1 -0
  8. package/dist/300.js +1 -1
  9. package/dist/347.js +1 -2
  10. package/dist/347.js.map +1 -1
  11. package/dist/373.js +2 -0
  12. package/dist/373.js.map +1 -0
  13. package/dist/38.js +1 -0
  14. package/dist/38.js.map +1 -0
  15. package/dist/389.js +1 -0
  16. package/dist/389.js.map +1 -0
  17. package/dist/398.js +1 -0
  18. package/dist/398.js.map +1 -0
  19. package/dist/4.js +2 -0
  20. package/dist/4.js.map +1 -0
  21. package/dist/410.js +1 -0
  22. package/dist/410.js.map +1 -0
  23. package/dist/420.js +2 -0
  24. package/dist/420.js.map +1 -0
  25. package/dist/467.js +1 -0
  26. package/dist/467.js.map +1 -0
  27. package/dist/632.js +1 -0
  28. package/dist/632.js.map +1 -0
  29. package/dist/798.js +1 -0
  30. package/dist/798.js.map +1 -0
  31. package/dist/811.js +1 -0
  32. package/dist/811.js.map +1 -0
  33. package/dist/824.js +1 -0
  34. package/dist/824.js.map +1 -0
  35. package/dist/827.js +1 -0
  36. package/dist/827.js.map +1 -0
  37. package/dist/842.js +2 -0
  38. package/dist/842.js.LICENSE.txt +5 -0
  39. package/dist/842.js.map +1 -0
  40. package/dist/918.js +1 -1
  41. package/dist/918.js.map +1 -1
  42. package/dist/kenyaemr-esm-morgue-app.js +1 -1
  43. package/dist/kenyaemr-esm-morgue-app.js.buildmanifest.json +218 -291
  44. package/dist/kenyaemr-esm-morgue-app.js.map +1 -1
  45. package/dist/main.js +2 -1
  46. package/dist/main.js.LICENSE.txt +15 -0
  47. package/dist/main.js.map +1 -1
  48. package/dist/routes.json +1 -1
  49. package/package.json +1 -1
  50. package/src/bed/bed.component.tsx +164 -0
  51. package/src/bed/bed.scss +192 -0
  52. package/src/bed/divider/divider.component.tsx +18 -0
  53. package/src/bed/empty-bed.component.tsx +47 -0
  54. package/src/bed-layout/admitted/admitted-bed-layout.component.tsx +189 -0
  55. package/src/bed-layout/awaiting/awaiting-bed-layout.component.tsx +86 -0
  56. package/src/bed-layout/bed-layout.resource.ts +72 -0
  57. package/src/bed-layout/bed-layout.scss +55 -0
  58. package/src/bed-layout/discharged/discharged-bed-layout.component.tsx +109 -0
  59. package/src/bed-layout/discharged/discharged-bed-layout.resource.ts +99 -0
  60. package/src/bed-linelist-view/admitted/admitted-bed-linelist-view.component.tsx +420 -0
  61. package/src/bed-linelist-view/awaiting/awaiting-bed-linelist-view.component.tsx +224 -0
  62. package/src/bed-linelist-view/bed-linelist-view.scss +5 -0
  63. package/src/bed-linelist-view/discharged/discharged-bed-line-view.component.tsx +256 -0
  64. package/src/config-schema.ts +41 -9
  65. package/src/constants.ts +57 -0
  66. package/src/deceased-patient-header/deceased-patient-header.component.tsx +31 -0
  67. package/src/deceased-patient-header/deceased-patient-header.scss +50 -0
  68. package/src/{component → deceased-patient-header}/deceasedInfo/deceased-info.component.tsx +1 -1
  69. package/src/deceased-patient-header/deceasedInfo/deceased-info.resource.ts +11 -0
  70. package/src/extension/actionButton.component.tsx +5 -59
  71. package/src/extension/deceasedInfoBanner.component.tsx +5 -9
  72. package/src/{hook/useAdmitPatient.ts → forms/admit-deceased-person-workspace/admit-deceased-person.resource.ts} +177 -46
  73. package/src/forms/admit-deceased-person-workspace/admit-deceased-person.scss +143 -0
  74. package/src/forms/admit-deceased-person-workspace/admit-deceased-person.workspace.tsx +648 -0
  75. package/src/{hook/usePersonAttributes.ts → forms/discharge-deceased-person-workspace/discharge-body.resource.ts} +1 -1
  76. package/src/forms/discharge-deceased-person-workspace/discharge-body.scss +56 -0
  77. package/src/forms/discharge-deceased-person-workspace/discharge-body.workspace.tsx +362 -0
  78. package/src/forms/dispose-deceased-person-workspace/dispose-deceased-person.resource.ts +18 -0
  79. package/src/{workspaces/patientAdditionalInfoForm.scss → forms/dispose-deceased-person-workspace/dispose-deceased-person.scss} +46 -66
  80. package/src/forms/dispose-deceased-person-workspace/dispose-deceased-person.workspace.tsx +401 -0
  81. package/src/forms/form-entry-workspace/form-entry-workspace.workspace.tsx +62 -0
  82. package/src/forms/swap-compartment-workspace/swap-unit.scss +144 -0
  83. package/src/forms/swap-compartment-workspace/swap-unit.workspace.tsx +280 -0
  84. package/src/header/header.component.tsx +41 -0
  85. package/src/header/header.scss +58 -0
  86. package/src/home/home.component.tsx +87 -0
  87. package/src/home/home.resource.ts +261 -0
  88. package/src/home/home.scss +5 -0
  89. package/src/index.ts +18 -12
  90. package/src/metrics/metrics-card.component.tsx +31 -0
  91. package/src/metrics/metrics-card.scss +51 -0
  92. package/src/root.component.tsx +7 -3
  93. package/src/routes.json +25 -14
  94. package/src/schemas/index.ts +66 -0
  95. package/src/summary/summary.component.tsx +42 -0
  96. package/src/summary/summary.scss +10 -0
  97. package/src/switcher/content-switcher.component.tsx +220 -0
  98. package/src/switcher/content-switcher.scss +30 -0
  99. package/src/types/index.ts +336 -359
  100. package/src/utils/utils.ts +20 -2
  101. package/src/view-details/main/main.component.tsx +34 -0
  102. package/src/view-details/main/main.scss +45 -0
  103. package/src/view-details/panels/attachement.component.tsx +21 -0
  104. package/src/view-details/panels/autopsy.component.tsx +215 -0
  105. package/src/view-details/panels/billing-history.component.tsx +13 -0
  106. package/src/view-details/panels/observations/observation.component.tsx +57 -0
  107. package/src/view-details/panels/observations/observation.scss +24 -0
  108. package/src/view-details/panels/panels.scss +46 -0
  109. package/src/view-details/view-details.component.tsx +65 -0
  110. package/src/view-details/view-details.resource.ts +65 -0
  111. package/src/view-details/views-details.scss +82 -0
  112. package/translations/en.json +74 -21
  113. package/tsconfig.json +1 -1
  114. package/dist/113.js +0 -1
  115. package/dist/113.js.map +0 -1
  116. package/dist/160.js +0 -1
  117. package/dist/160.js.map +0 -1
  118. package/dist/299.js +0 -1
  119. package/dist/299.js.map +0 -1
  120. package/dist/433.js +0 -2
  121. package/dist/433.js.map +0 -1
  122. package/dist/441.js +0 -1
  123. package/dist/441.js.map +0 -1
  124. package/dist/496.js +0 -1
  125. package/dist/496.js.map +0 -1
  126. package/dist/511.js +0 -1
  127. package/dist/511.js.map +0 -1
  128. package/dist/603.js +0 -1
  129. package/dist/603.js.map +0 -1
  130. package/dist/610.js +0 -1
  131. package/dist/610.js.map +0 -1
  132. package/dist/612.js +0 -1
  133. package/dist/612.js.map +0 -1
  134. package/dist/656.js +0 -2
  135. package/dist/656.js.map +0 -1
  136. package/dist/752.js +0 -1
  137. package/dist/752.js.map +0 -1
  138. package/dist/754.js +0 -1
  139. package/dist/754.js.map +0 -1
  140. package/dist/781.js +0 -1
  141. package/dist/781.js.map +0 -1
  142. package/dist/801.js +0 -2
  143. package/dist/801.js.map +0 -1
  144. package/dist/817.js +0 -1
  145. package/dist/817.js.map +0 -1
  146. package/dist/877.js +0 -1
  147. package/dist/877.js.map +0 -1
  148. package/dist/924.js +0 -1
  149. package/dist/924.js.map +0 -1
  150. package/src/autosuggest/autosuggest.component.tsx +0 -162
  151. package/src/autosuggest/autosuggest.scss +0 -61
  152. package/src/autosuggest/patient-search-info.component.tsx +0 -75
  153. package/src/autosuggest/patient-search-info.scss +0 -62
  154. package/src/autosuggest/search-empty-state.component.tsx +0 -21
  155. package/src/autosuggest/search-empty-state.scss +0 -18
  156. package/src/card/avail-compartment.compartment.tsx +0 -94
  157. package/src/card/compartment-view.compartment.tsx +0 -62
  158. package/src/card/compartment.scss +0 -128
  159. package/src/card/compartmentSharing.component.tsx +0 -21
  160. package/src/card/compartmentSharing.scss +0 -24
  161. package/src/card/empty-compartment.component.tsx +0 -28
  162. package/src/card/empty-compartment.scss +0 -61
  163. package/src/component/main.component.tsx +0 -17
  164. package/src/component/next-of-kin-details/nextOfKinDetails.component.tsx +0 -50
  165. package/src/component/next-of-kin-details/nextOfKinDetails.scss +0 -37
  166. package/src/header/admitted-queue-header.component.tsx +0 -30
  167. package/src/header/admitted-queue-header.scss +0 -32
  168. package/src/header/morgue-header.component.tsx +0 -38
  169. package/src/header/morgue-header.scss +0 -95
  170. package/src/header/morgue-illustration.component.tsx +0 -13
  171. package/src/hook/useDeceasedPatients.ts +0 -12
  172. package/src/hook/useDischargedPatient.ts +0 -55
  173. package/src/hook/useMorgue.resource.ts +0 -163
  174. package/src/hook/useMortuaryAdmissionLocation.ts +0 -64
  175. package/src/tables/admitted-queue.component.tsx +0 -54
  176. package/src/tables/admitted-queue.scss +0 -62
  177. package/src/tables/discharge-queue.component.tsx +0 -87
  178. package/src/tables/generic-table.component.tsx +0 -140
  179. package/src/tables/generic-table.scss +0 -37
  180. package/src/tabs/tabs.component.tsx +0 -82
  181. package/src/tabs/tabs.scss +0 -15
  182. package/src/workspaces/admit-body.scss +0 -46
  183. package/src/workspaces/admit-body.workspace.tsx +0 -79
  184. package/src/workspaces/discharge-body.scss +0 -67
  185. package/src/workspaces/discharge-body.workspace.tsx +0 -329
  186. package/src/workspaces/patientAdditionalInfoForm.workspace.tsx +0 -562
  187. package/src/workspaces/swap-unit.scss +0 -46
  188. package/src/workspaces/swap-unit.workspace.tsx +0 -168
  189. /package/dist/{347.js.LICENSE.txt → 109.js.LICENSE.txt} +0 -0
  190. /package/dist/{656.js.LICENSE.txt → 373.js.LICENSE.txt} +0 -0
  191. /package/dist/{801.js.LICENSE.txt → 4.js.LICENSE.txt} +0 -0
  192. /package/dist/{433.js.LICENSE.txt → 420.js.LICENSE.txt} +0 -0
  193. /package/src/{component → deceased-patient-header}/deceasedInfo/deceased-info.scss +0 -0
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":"^2.24.0"},"extensions":[{"component":"morgueDashboardLink","name":"morgue-dashboard-link","slot":"homepage-dashboard-slot","featureFlag":"mortuaryFeatureFlag","meta":{"name":"morgue","title":"morgue","slot":"morgue-dashboard-slot","privileges":["o3 : View Mortuary Dashboard"]}},{"name":"action-buttons","component":"actionBarButtons","slot":"mortuary-action-buttons-slot","meta":{"fullWidth":false,"privileges":["o3 : View Mortuary Dashboard","o3 : Admit Body to Mortuary","o3 : Edit Mortuary Records","o3 : Discharge Body from Mortuary","o3 : View Mortuary Compartments","o3 : Swap Mortuary Compartments","o3 : View Mortuary Billing","o3 : Process Mortuary Payments","o3 : View Mortuary Reports","o3 : Generate Mortuary Reports"]}},{"name":"deceased-banner-info","component":"bannerInfo","slot":"deceased-banner-info-slot","meta":{"fullWidth":false}},{"component":"root","name":"morgue-dashboard-root","slot":"morgue-dashboard-slot","meta":{"privileges":["o3 : View Mortuary Dashboard"]}}],"workspaces":[{"name":"patient-additional-info-form","component":"patientAdditionalInfoForm","title":"add more details","type":"other-form"},{"name":"discharge-body-form","component":"dischargeBodyForm","title":"discharge body form","type":"other-form"},{"name":"admit-body-form","component":"admitBodyForm","title":"Admission form","type":"other-form"},{"name":"swap-unit-form","component":"swapForm","title":"Swap form","type":"other-form"}],"pages":[{"component":"root","route":"morgue"}],"version":"5.4.2-pre.2283"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":"^2.24.0"},"extensions":[{"name":"action-buttons","component":"actionBarButtons","slot":"mortuary-action-buttons-slot","meta":{"fullWidth":false,"privileges":["o3 : View Mortuary Dashboard","o3 : Admit Body to Mortuary","o3 : Edit Mortuary Records","o3 : Discharge Body from Mortuary","o3 : View Mortuary Compartments","o3 : Swap Mortuary Compartments","o3 : View Mortuary Billing","o3 : Process Mortuary Payments","o3 : View Mortuary Reports","o3 : Generate Mortuary Reports"]}},{"name":"deceased-banner-info","component":"bannerInfo","slot":"deceased-banner-info-slot","meta":{"fullWidth":false}},{"name":"mortuary-summary-info","component":"mortuarySummary","slot":"mortuary-summary-info-slot"},{"component":"root","name":"morgue-dashboard-root","slot":"morgue-dashboard-slot","meta":{"privileges":["o3 : View Mortuary Dashboard"]}}],"workspaces":[{"name":"patient-additional-info-form","component":"patientAdditionalInfoForm","title":"add more details","type":"other-form"},{"name":"discharge-body-form","component":"dischargeBodyForm","title":"discharge body form","type":"other-form"},{"name":"admit-body-form","component":"admitBodyForm","title":"Admission form","type":"other-form"},{"name":"swap-unit-form","component":"swapForm","title":"Swap form","type":"other-form"},{"name":"admit-deceased-person-form","component":"admitDeceasedPersonForm","title":"Admit Deceased Person","type":"other-form"},{"name":"mortuary-form-entry","component":"mortuaryFormEntry","title":"Mortuary Form Entry","type":"form","width":"extra-wide","canMaximize":true,"canHide":true},{"name":"dispose-deceased-person-form","component":"disposeDeceasedPersonForm","title":"Dispose Deceased Person","type":"other-form"}],"pages":[{"component":"root","route":"morgue"}],"version":"5.4.2-pre.2291"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-morgue-app",
3
- "version": "5.4.2-pre.2283",
3
+ "version": "5.4.2-pre.2291",
4
4
  "description": "morgue app for KenyaEMR",
5
5
  "browser": "dist/kenyaemr-esm-morgue-app.js",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,164 @@
1
+ import React from 'react';
2
+ import { Layer, OverflowMenu, OverflowMenuItem, Tag, Tile } from '@carbon/react';
3
+ import styles from './bed.scss';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Tag as TagIcon } from '@carbon/react/icons';
6
+ import capitalize from 'lodash-es/capitalize';
7
+ import startCase from 'lodash-es/startCase';
8
+ import { formatDateTime, convertDateToDays } from '../utils/utils';
9
+ import { useVisit } from '@openmrs/esm-framework';
10
+
11
+ interface BedProps {
12
+ bedNumber?: string;
13
+ patientName: string;
14
+ gender: string;
15
+ age: number;
16
+ bedType?: string;
17
+ causeOfDeath: string;
18
+ dateOfDeath: string;
19
+ patientUuid: string;
20
+ onAdmit?: () => void;
21
+ onPostmortem?: () => void;
22
+ onDischarge?: () => void;
23
+ onSwapCompartment?: () => void;
24
+ onDispose?: () => void;
25
+ onPrintGatePass?: () => void;
26
+ onPrintPostmortem?: () => void;
27
+ onViewDetails?: () => void;
28
+ isDischarged?: boolean;
29
+ }
30
+
31
+ const BedCard: React.FC<BedProps> = ({
32
+ bedNumber,
33
+ patientName,
34
+ gender,
35
+ age,
36
+ causeOfDeath,
37
+ bedType,
38
+ dateOfDeath,
39
+ patientUuid,
40
+ onAdmit,
41
+ onPostmortem,
42
+ onDischarge,
43
+ onSwapCompartment,
44
+ onDispose,
45
+ onPrintGatePass,
46
+ onPrintPostmortem,
47
+ onViewDetails,
48
+ isDischarged = false,
49
+ }) => {
50
+ const { t } = useTranslation();
51
+ const { activeVisit } = useVisit(patientUuid);
52
+
53
+ const lengthOfStay = activeVisit?.startDatetime
54
+ ? convertDateToDays(activeVisit.startDatetime)
55
+ : calculateDaysInMortuary(dateOfDeath);
56
+
57
+ const isAdmitted = !!activeVisit;
58
+ const timeSpentTagType = lengthOfStay > 7 ? 'red' : lengthOfStay > 3 ? 'magenta' : 'green';
59
+
60
+ function calculateDaysInMortuary(dateOfDeath: string): number {
61
+ if (!dateOfDeath) {
62
+ return 0;
63
+ }
64
+ const deathDate = new Date(dateOfDeath);
65
+ const currentDate = new Date();
66
+ const timeDiff = currentDate.getTime() - deathDate.getTime();
67
+ return Math.floor(timeDiff / (1000 * 3600 * 24));
68
+ }
69
+
70
+ return (
71
+ <Layer className={`${styles.cardWithChildren} ${styles.container}`}>
72
+ <Tile className={styles.tileContainer}>
73
+ <div className={styles.tileHeader}>
74
+ <div className={styles.tagContainer}>
75
+ <div className={styles.tagContainer}>
76
+ {bedNumber ? (
77
+ <Tag type="cool-gray" className={styles.bedNumberTag}>
78
+ {bedNumber}
79
+ </Tag>
80
+ ) : (
81
+ !isDischarged && (
82
+ <Tag type="cool-gray" className={styles.bedNumberTag}>
83
+ {t('awaiting', 'Awaiting')}
84
+ </Tag>
85
+ )
86
+ )}
87
+ {isDischarged && (
88
+ <Tag type="blue" className={styles.statusTag}>
89
+ {t('discharged', 'Discharged')}
90
+ </Tag>
91
+ )}
92
+ <TagIcon className={styles.tagIcon} />
93
+ </div>
94
+ </div>
95
+ <div>
96
+ {bedType ? <Tag type="green">{startCase(bedType)}</Tag> : null}
97
+ <OverflowMenu flipped>
98
+ {onAdmit && <OverflowMenuItem onClick={onAdmit} itemText={t('admit', 'Admit')} />}
99
+ {onViewDetails && (
100
+ <OverflowMenuItem onClick={() => onViewDetails()} itemText={t('viewDetails', 'View details')} />
101
+ )}
102
+ {onPostmortem && <OverflowMenuItem onClick={onPostmortem} itemText={t('postmortem', 'Postmortem')} />}
103
+ {onDischarge && <OverflowMenuItem onClick={onDischarge} itemText={t('discharge', 'Discharge')} />}
104
+ {onSwapCompartment && (
105
+ <OverflowMenuItem onClick={onSwapCompartment} itemText={t('swapCompartment', 'Swap Compartment')} />
106
+ )}
107
+ {onDispose && <OverflowMenuItem onClick={onDispose} itemText={t('dispose', 'Dispose')} />}
108
+ {onPrintGatePass && (
109
+ <OverflowMenuItem onClick={onPrintGatePass} itemText={t('printGatePass', 'Gate Pass')} />
110
+ )}
111
+ {onPrintPostmortem && (
112
+ <OverflowMenuItem
113
+ onClick={onPrintPostmortem}
114
+ itemText={t('printPostmortemReport', 'Postmortem Report')}
115
+ />
116
+ )}
117
+ </OverflowMenu>
118
+ </div>
119
+ </div>
120
+
121
+ <div className={styles.patientInfoRow}>
122
+ <span className={styles.patientName}>{capitalize(patientName)}</span>
123
+ <span className={styles.middot}>•</span>
124
+ <span className={styles.gender}>{gender}</span>
125
+ <span className={styles.middot}>•</span>
126
+ <span className={styles.age}>{age}</span>
127
+ <span className={styles.ageUnit}>{t('yearsOld', 'Yrs old')}</span>
128
+ </div>
129
+
130
+ <div className={styles.causeOfDeathRow}>
131
+ <span className={styles.causeLabel}>{t('causeOfDeath', 'Cause of death')}</span>
132
+ <span className={styles.causeValue}>{startCase(causeOfDeath)}</span>
133
+ </div>
134
+ <div className={styles.patientInfoRow}>
135
+ <span className={styles.causeLabel}>{t('dateOfDeath', 'Date of death')}</span>
136
+ <span className={styles.causeValue}>{formatDateTime(dateOfDeath)}</span>
137
+ </div>
138
+ {isAdmitted && (
139
+ <div className={styles.patientInfoRow}>
140
+ <span className={styles.causeLabel}>{t('admissionDate', 'Admission Date')}</span>
141
+ <span className={styles.causeValue}>
142
+ {activeVisit?.startDatetime ? formatDateTime(activeVisit.startDatetime) : '-'}
143
+ </span>
144
+ </div>
145
+ )}
146
+ <div className={styles.borderLine}></div>
147
+ <div className={styles.cardRow}>
148
+ <span className={styles.causeLabel}>
149
+ {isAdmitted ? t('timeSpent', 'Time spent') : t('daysInMortuary', 'Days in mortuary')}
150
+ </span>
151
+ <div className={styles.tagsContainer}>
152
+ <Tag size="md" type={timeSpentTagType}>
153
+ <span className={styles.causeLabel}>{lengthOfStay}</span>{' '}
154
+ {lengthOfStay === 1 ? t('day', 'Day') : t('days', 'Days')}
155
+ </Tag>
156
+ {isAdmitted && !isDischarged && <Tag type="green">{t('admitted', 'Admitted')}</Tag>}
157
+ </div>
158
+ </div>
159
+ </Tile>
160
+ </Layer>
161
+ );
162
+ };
163
+
164
+ export default BedCard;
@@ -0,0 +1,192 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+
5
+ .container {
6
+ flex-grow: 1;
7
+ }
8
+
9
+ .tileContainer {
10
+ border: 1px solid colors.$gray-20;
11
+ min-height: 2.875rem;
12
+ padding: layout.$spacing-05;
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: layout.$spacing-03;
16
+ }
17
+
18
+ .tileHeader {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+
23
+ > :last-child {
24
+ display: flex;
25
+ align-items: center;
26
+ gap: layout.$spacing-03;
27
+ }
28
+ }
29
+
30
+ .tagContainer {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: layout.$spacing-02;
34
+ }
35
+
36
+ .tagIcon {
37
+ width: 1rem;
38
+ height: 1rem;
39
+ color: colors.$gray-70;
40
+ margin-left: -#{layout.$spacing-02};
41
+ }
42
+
43
+ .patientInfoRow {
44
+ display: flex;
45
+ align-items: center;
46
+ gap: layout.$spacing-04;
47
+ flex-wrap: wrap;
48
+ }
49
+
50
+ .patientName {
51
+ @include type.type-style('body-01');
52
+ color: colors.$gray-100;
53
+ font-weight: 500;
54
+ text-transform: capitalize;
55
+ }
56
+
57
+ .middot {
58
+ color: colors.$gray-70;
59
+ font-weight: bold;
60
+ }
61
+
62
+ .gender {
63
+ @include type.type-style('body-01');
64
+ color: colors.$gray-70;
65
+ font-weight: 500;
66
+ }
67
+
68
+ .age {
69
+ @include type.type-style('body-01');
70
+ color: colors.$gray-70;
71
+ font-weight: 500;
72
+ }
73
+
74
+ .ageUnit {
75
+ @include type.type-style('body-01');
76
+ color: colors.$gray-70;
77
+ margin-left: layout.$spacing-01;
78
+ }
79
+
80
+ .causeOfDeathRow {
81
+ display: flex;
82
+ align-items: center;
83
+ gap: layout.$spacing-01;
84
+ }
85
+
86
+ .causeLabel {
87
+ @include type.type-style('label-01');
88
+ color: colors.$gray-70;
89
+ font-weight: 500;
90
+ }
91
+
92
+ .causeValue {
93
+ @include type.type-style('label-01');
94
+ color: colors.$gray-100;
95
+ }
96
+
97
+ .additionalContentRow {
98
+ border-top: 1px solid colors.$gray-20;
99
+ padding-top: layout.$spacing-03;
100
+ margin-top: layout.$spacing-02;
101
+ }
102
+
103
+ .cardWithChildren {
104
+ .tileContainer {
105
+ min-height: auto;
106
+ }
107
+ }
108
+
109
+ .headerLabel {
110
+ @include type.type-style('heading-compact-01');
111
+ color: colors.$gray-70;
112
+ }
113
+
114
+ .totalsLabel {
115
+ @include type.type-style('label-01');
116
+ color: colors.$gray-70;
117
+ }
118
+
119
+ .totalsValue {
120
+ @include type.type-style('heading-04');
121
+ color: colors.$gray-100;
122
+ }
123
+
124
+ .headerLabelContainer {
125
+ margin-bottom: layout.$spacing-05;
126
+ }
127
+
128
+ .link {
129
+ text-decoration: none;
130
+ display: flex;
131
+ align-items: center;
132
+ color: colors.$blue-60;
133
+
134
+ svg {
135
+ margin-left: layout.$spacing-03;
136
+ }
137
+ }
138
+
139
+ .emptyBedText {
140
+ @include type.type-style('body-01');
141
+ color: colors.$gray-70;
142
+ margin-top: layout.$spacing-02;
143
+ text-align: center;
144
+ font-weight: 500;
145
+ }
146
+
147
+ .borderLine {
148
+ width: 100%;
149
+ height: 1px;
150
+ background-color: colors.$gray-30;
151
+ margin: layout.$spacing-01 0;
152
+ }
153
+
154
+ .cardRow {
155
+ display: flex;
156
+ justify-content: space-between;
157
+ align-items: center;
158
+ gap: 1rem;
159
+ width: 100%;
160
+
161
+ .causeLabel {
162
+ margin-right: auto;
163
+ }
164
+
165
+ .tagsContainer {
166
+ display: flex;
167
+ gap: 0.5rem;
168
+ align-items: center;
169
+ }
170
+ }
171
+
172
+ .dividerContainer {
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ padding: 0 layout.$spacing-03;
177
+ background-color: colors.$gray-80;
178
+ }
179
+
180
+ .dividerLine {
181
+ flex-grow: 1;
182
+ height: 1px;
183
+ background-color: colors.$gray-30;
184
+ }
185
+
186
+ .dividerText {
187
+ @include type.type-style('label-01');
188
+ color: colors.$white;
189
+ font-weight: 500;
190
+ padding: 0 layout.$spacing-03;
191
+ white-space: nowrap;
192
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Layer, Tile } from '@carbon/react';
4
+ import styles from '../bed.scss';
5
+
6
+ const Divider: React.FC = () => {
7
+ const { t } = useTranslation();
8
+
9
+ return (
10
+ <div className={styles.dividerContainer}>
11
+ <div className={styles.dividerLine}></div>
12
+ <span className={styles.dividerText}>{t('dividerText', 'Shared')}</span>
13
+ <div className={styles.dividerLine}></div>
14
+ </div>
15
+ );
16
+ };
17
+
18
+ export default Divider;
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import { Layer, OverflowMenu, OverflowMenuItem, Tag, Tile } from '@carbon/react';
3
+ import styles from './bed.scss';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Tag as TagIcon } from '@carbon/react/icons';
6
+ import capitalize from 'lodash-es/capitalize';
7
+ import startCase from 'lodash-es/startCase';
8
+ import { formatDateTime } from '../utils/utils';
9
+
10
+ interface EmptyBedProps {
11
+ bedNumber?: string;
12
+ bedType?: string;
13
+ onAdmit?: () => void;
14
+ isEmpty?: boolean;
15
+ }
16
+
17
+ const EmptyBedCard: React.FC<EmptyBedProps> = ({ bedNumber, onAdmit, bedType, isEmpty }) => {
18
+ const { t } = useTranslation();
19
+
20
+ return (
21
+ <Layer className={`${styles.cardWithChildren} ${styles.container}`}>
22
+ <Tile className={styles.tileContainer}>
23
+ <div className={styles.tileHeader}>
24
+ <div className={styles.tagContainer}>
25
+ <Tag type="cool-gray">{bedNumber}</Tag>
26
+ <TagIcon className={styles.tagIcon} />
27
+ </div>
28
+ <div>
29
+ {!isEmpty && (
30
+ <>
31
+ <Tag type="green">{startCase(bedType)}</Tag>
32
+ <OverflowMenu flipped>
33
+ <OverflowMenuItem onClick={onAdmit} itemText={t('admitBody', 'Admit')} disabled={!onAdmit} />
34
+ </OverflowMenu>
35
+ </>
36
+ )}
37
+ </div>
38
+ </div>
39
+ <span>
40
+ <h4 className={styles.emptyBedText}>{t('emptyCompartment', 'Empty compartment')}</h4>
41
+ </span>
42
+ </Tile>
43
+ </Layer>
44
+ );
45
+ };
46
+
47
+ export default EmptyBedCard;
@@ -0,0 +1,189 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { InlineLoading } from '@carbon/react';
4
+ import { launchWorkspace, navigate, useConfig } from '@openmrs/esm-framework';
5
+ import styles from '../bed-layout.scss';
6
+ import BedCard from '../../bed/bed.component';
7
+ import { type MortuaryLocationResponse } from '../../types';
8
+ import EmptyBedCard from '../../bed/empty-bed.component';
9
+ import Divider from '../../bed/divider/divider.component';
10
+ import { ConfigObject } from '../../config-schema';
11
+ import { mutate as mutateSWR } from 'swr';
12
+
13
+ interface BedLayoutProps {
14
+ AdmittedDeceasedPatient: MortuaryLocationResponse | null;
15
+ isLoading: boolean;
16
+ onAdmit?: (patientUuid: string) => void;
17
+ onPostmortem?: (patientUuid: string) => void;
18
+ onDischarge?: (patientUuid: string) => void;
19
+ onSwapCompartment?: (patientUuid: string, bedId: string) => void;
20
+ onDispose?: (patientUuid: string) => void;
21
+ mutate?: () => void;
22
+ }
23
+
24
+ const BedLayout: React.FC<BedLayoutProps> = ({
25
+ AdmittedDeceasedPatient,
26
+ isLoading,
27
+ onPostmortem,
28
+ onDischarge,
29
+ onSwapCompartment,
30
+ onDispose,
31
+ mutate,
32
+ }) => {
33
+ const { t } = useTranslation();
34
+ const { autopsyFormUuid } = useConfig<ConfigObject>();
35
+
36
+ const handlePostmortem = (patientUuid: string) => {
37
+ if (onPostmortem) {
38
+ onPostmortem(patientUuid);
39
+ } else {
40
+ launchWorkspace('mortuary-form-entry', {
41
+ formUuid: autopsyFormUuid,
42
+ workspaceTitle: t('postmortemForm', 'Postmortem form'),
43
+
44
+ patientUuid: patientUuid,
45
+ encounterUuid: '',
46
+ mutateForm: () => {
47
+ mutateSWR((key) => true, undefined, {
48
+ revalidate: true,
49
+ });
50
+ },
51
+ });
52
+ }
53
+ };
54
+
55
+ const handleDischarge = (patientUuid: string, bedId: number) => {
56
+ if (onDischarge) {
57
+ onDischarge(patientUuid);
58
+ } else {
59
+ launchWorkspace('discharge-body-form', {
60
+ workspaceTitle: t('dischargeForm', 'Discharge form'),
61
+ patientUuid: patientUuid,
62
+ bedId,
63
+ mutate,
64
+ });
65
+ }
66
+ };
67
+
68
+ const handleSwapCompartment = (patientUuid: string, bedId: number) => {
69
+ if (onSwapCompartment) {
70
+ onSwapCompartment(patientUuid, bedId.toString());
71
+ } else {
72
+ launchWorkspace('swap-unit-form', {
73
+ workspaceTitle: t('swapCompartment', 'Swap compartment'),
74
+ patientUuid: patientUuid,
75
+ bedId,
76
+ mortuaryLocation: AdmittedDeceasedPatient,
77
+ mutate,
78
+ });
79
+ }
80
+ };
81
+
82
+ const handleDispose = (patientUuid: string, bedId: number) => {
83
+ if (onDispose) {
84
+ onDispose(patientUuid);
85
+ } else {
86
+ launchWorkspace('dispose-deceased-person-form', {
87
+ workspaceTitle: t('disposeForm', 'Dispose form'),
88
+ patientUuid: patientUuid,
89
+ bedId,
90
+ mutate,
91
+ });
92
+ }
93
+ };
94
+
95
+ if (isLoading) {
96
+ return (
97
+ <div className={styles.loadingContainer}>
98
+ <InlineLoading description={t('loadingPatients', 'Loading patients...')} />
99
+ </div>
100
+ );
101
+ }
102
+
103
+ const bedLayouts = AdmittedDeceasedPatient?.bedLayouts || [];
104
+
105
+ return (
106
+ <div className={styles.bedLayoutWrapper}>
107
+ <div className={styles.bedLayoutContainer}>
108
+ {bedLayouts.map((bedLayout, index) => {
109
+ const patients = bedLayout.patients || [];
110
+ const isEmpty = bedLayout.status === 'AVAILABLE' || patients.length === 0;
111
+
112
+ if (isEmpty) {
113
+ return (
114
+ <EmptyBedCard
115
+ key={bedLayout.bedUuid || `empty-bed-${bedLayout.bedId}-${index}`}
116
+ bedNumber={bedLayout.bedNumber}
117
+ bedType={bedLayout.bedType?.displayName}
118
+ isEmpty={isEmpty}
119
+ />
120
+ );
121
+ }
122
+
123
+ return (
124
+ <div
125
+ key={bedLayout.bedUuid}
126
+ className={`${styles.bedContainer} ${patients.length > 1 ? styles.sharedBedContainer : ''}`}>
127
+ {patients.length > 1 ? (
128
+ <div className={styles.horizontalLayout}>
129
+ {patients.map((patient, patientIndex) => (
130
+ <React.Fragment key={patient.uuid}>
131
+ <BedCard
132
+ patientUuid={patient.uuid}
133
+ patientName={patient.person?.display}
134
+ gender={patient.person?.gender}
135
+ age={patient.person?.age}
136
+ causeOfDeath={patient.person?.causeOfDeath?.display}
137
+ dateOfDeath={patient.person?.deathDate}
138
+ bedNumber={bedLayout.bedNumber}
139
+ bedType={bedLayout.bedType?.displayName}
140
+ onPostmortem={() => handlePostmortem(patient.uuid)}
141
+ onDischarge={() => handleDischarge(patient.uuid, bedLayout.bedId)}
142
+ onSwapCompartment={() => handleSwapCompartment(patient.uuid, bedLayout.bedId)}
143
+ onDispose={() => handleDispose(patient.uuid, bedLayout.bedId)}
144
+ onViewDetails={() => {
145
+ const hasBedInfo = bedLayout.bedNumber && bedLayout.bedId;
146
+ const base = `${window.getOpenmrsSpaBase()}home/morgue/patient/${patient.uuid}`;
147
+ const to = hasBedInfo
148
+ ? `${base}/compartment/${bedLayout.bedNumber}/${bedLayout.bedId}/mortuary-chart`
149
+ : `${base}/mortuary-chart`;
150
+ navigate({ to });
151
+ }}
152
+ />
153
+ {patientIndex < patients.length - 1 && <Divider />}
154
+ </React.Fragment>
155
+ ))}
156
+ </div>
157
+ ) : (
158
+ <BedCard
159
+ patientUuid={patients[0].uuid}
160
+ patientName={patients[0].person?.display}
161
+ gender={patients[0].person?.gender}
162
+ age={patients[0].person?.age}
163
+ causeOfDeath={patients[0].person?.causeOfDeath?.display}
164
+ dateOfDeath={patients[0].person?.deathDate}
165
+ bedNumber={bedLayout.bedNumber}
166
+ bedType={bedLayout.bedType?.displayName}
167
+ onPostmortem={() => handlePostmortem(patients[0].uuid)}
168
+ onDischarge={() => handleDischarge(patients[0].uuid, bedLayout.bedId)}
169
+ onSwapCompartment={() => handleSwapCompartment(patients[0].uuid, bedLayout.bedId)}
170
+ onDispose={() => handleDispose(patients[0].uuid, bedLayout.bedId)}
171
+ onViewDetails={() => {
172
+ const hasBedInfo = bedLayout.bedNumber && bedLayout.bedId;
173
+ const base = `${window.getOpenmrsSpaBase()}home/morgue/patient/${patients[0].uuid}`;
174
+ const to = hasBedInfo
175
+ ? `${base}/compartment/${bedLayout.bedNumber}/${bedLayout.bedId}/mortuary-chart`
176
+ : `${base}/mortuary-chart`;
177
+ navigate({ to });
178
+ }}
179
+ />
180
+ )}
181
+ </div>
182
+ );
183
+ })}
184
+ </div>
185
+ </div>
186
+ );
187
+ };
188
+
189
+ export default BedLayout;