@kenyaemr/esm-morgue-app 5.4.2-pre.2279 → 5.4.2-pre.2288

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 -1
  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 +67 -25
  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
@@ -59,8 +59,13 @@ export const formatDateTime = (date) => {
59
59
  return dayjs(date).format('DD-MMM-YYYY, hh:mm A');
60
60
  };
61
61
 
62
- // Utility functions to get the current date and time
63
-
62
+ /**
63
+ * Returns the current time in 12-hour format as an object with `time` and `period` properties.
64
+ *
65
+ * @returns {Object} - An object with `time` and `period` properties.
66
+ * @property {string} time - The current time in HH:MM format, e.g. "12:34".
67
+ * @property {string} period - The period of the day, either "AM" or "PM".
68
+ */
64
69
  export const getCurrentTime = () => {
65
70
  const now = new Date();
66
71
  const hours = now.getHours() % 12 || 12;
@@ -69,6 +74,19 @@ export const getCurrentTime = () => {
69
74
  return { time: `${hours}:${minutes}`, period };
70
75
  };
71
76
 
77
+ export function parseDisplayText(displayText: string): { name: string; openmrsId: string } | null {
78
+ const regex = /(.*) \(OpenMRS ID: (.*)\)/;
79
+ const match = displayText.match(regex);
80
+
81
+ if (match && match.length === 3) {
82
+ const name = match[1].trim();
83
+ const openmrsId = match[2].trim();
84
+ return { name, openmrsId };
85
+ } else {
86
+ return null;
87
+ }
88
+ }
89
+
72
90
  export const patientInfoSchema = z.object({
73
91
  dateOfAdmission: z
74
92
  .date({ coerce: true })
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { useParams } from 'react-router-dom';
3
+ import { Layer, Tile } from '@carbon/react';
4
+ import { ExtensionSlot } from '@openmrs/esm-framework';
5
+ import styles from './main.scss';
6
+ import MortuarySummary from '../view-details.component';
7
+ import Header from '../../header/header.component';
8
+
9
+ const DeceasedDetailsView: React.FC = () => {
10
+ const { patientUuid, bedNumber } = useParams<{ patientUuid: string; bedNumber?: string }>();
11
+
12
+ return (
13
+ <>
14
+ <Header title="Mortuary chart" />
15
+ <div className={styles.deceasedDetailsContainer}>
16
+ <Layer className={styles.container}>
17
+ <Tile>
18
+ <div className={styles.headingContainer}>
19
+ <div className={styles.desktopHeading}>
20
+ <h4>Mortuary view</h4>
21
+ </div>
22
+ <div className={styles.actionBtn}>
23
+ <ExtensionSlot name="mortuary-action-buttons-slot" state={{ patientUuid }} />
24
+ </div>
25
+ </div>
26
+ </Tile>
27
+ <MortuarySummary bedNumber={bedNumber} />
28
+ </Layer>
29
+ </div>
30
+ </>
31
+ );
32
+ };
33
+
34
+ export default DeceasedDetailsView;
@@ -0,0 +1,45 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
+
6
+ .container {
7
+ background-color: colors.$white;
8
+ }
9
+
10
+ .tabs {
11
+ margin: 0 layout.$spacing-05;
12
+ }
13
+
14
+ .headingContainer {
15
+ display: flex;
16
+ justify-content: space-between;
17
+ align-items: center;
18
+ }
19
+
20
+ .desktopHeading,
21
+ .tabletHeading {
22
+ text-align: left;
23
+ text-transform: capitalize;
24
+
25
+ h4 {
26
+ @include type.type-style('heading-compact-02');
27
+
28
+ &:after {
29
+ content: '';
30
+ display: block;
31
+ width: 2rem;
32
+ padding-top: 3px;
33
+ border-bottom: 0.375rem solid var(--brand-03);
34
+ }
35
+ }
36
+ }
37
+
38
+ .actionBtn {
39
+ display: flex;
40
+ gap: 0.5rem;
41
+ margin-left: auto;
42
+ }
43
+ .ghostButton {
44
+ color: colors.$gray-60;
45
+ }
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { ExtensionSlot, navigate, usePatient } from '@openmrs/esm-framework';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { EmptyState, getPatientUuidFromStore } from '@openmrs/esm-patient-common-lib';
5
+ import styles from './panels.scss';
6
+
7
+ const AttachmentView: React.FC = () => {
8
+ const { t } = useTranslation();
9
+ const patientUuid = getPatientUuidFromStore();
10
+
11
+ return (
12
+ <ExtensionSlot
13
+ name="patient-chart-attachments-dashboard-slot"
14
+ state={{
15
+ patientUuid,
16
+ }}
17
+ />
18
+ );
19
+ };
20
+
21
+ export default AttachmentView;
@@ -0,0 +1,215 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import {
4
+ formatDate,
5
+ isDesktop,
6
+ launchWorkspace,
7
+ parseDate,
8
+ useConfig,
9
+ useLayoutType,
10
+ usePagination,
11
+ } from '@openmrs/esm-framework';
12
+ import {
13
+ CardHeader,
14
+ EmptyState,
15
+ ErrorState,
16
+ getPatientUuidFromStore,
17
+ usePaginationInfo,
18
+ } from '@openmrs/esm-patient-common-lib';
19
+ import {
20
+ Button,
21
+ DataTable,
22
+ TableContainer,
23
+ Table,
24
+ TableHead,
25
+ TableRow,
26
+ TableHeader,
27
+ TableBody,
28
+ TableCell,
29
+ OverflowMenu,
30
+ OverflowMenuItem,
31
+ TableExpandHeader,
32
+ DataTableSkeleton,
33
+ TableExpandRow,
34
+ TableExpandedRow,
35
+ Pagination,
36
+ } from '@carbon/react';
37
+ import { Add } from '@carbon/react/icons';
38
+ import styles from './panels.scss';
39
+ import { ConfigObject } from '../../config-schema';
40
+ import EncounterObservations from './observations/observation.component';
41
+ import { Observation } from '../../types';
42
+ import { useAutospyEncounter } from '../view-details.resource';
43
+
44
+ const AutopsyView: React.FC = () => {
45
+ const { t } = useTranslation();
46
+ const patientUuid = getPatientUuidFromStore();
47
+ const { autopsyFormUuid, autopsyEncounterFormUuid } = useConfig<ConfigObject>();
48
+ const { encounters, isLoading, error, mutate, isValidating } = useAutospyEncounter(
49
+ patientUuid,
50
+ autopsyEncounterFormUuid,
51
+ );
52
+ const headerTitle = t('autopsyReport', 'Autopsy report');
53
+ const layout = useLayoutType();
54
+ const [pageSize, setPageSize] = React.useState(10);
55
+ const responsiveSize = isDesktop(layout) ? 'sm' : 'lg';
56
+ const { paginated, goTo, results, currentPage } = usePagination(encounters, pageSize);
57
+ const { pageSizes } = usePaginationInfo(pageSize, encounters?.length, currentPage, results?.length);
58
+
59
+ const handleLaunchAutopsyForm = (encounterUUID = '') => {
60
+ launchWorkspace('patient-form-entry-workspace', {
61
+ workspaceTitle: t('autopsyReport', 'Autopsy report'),
62
+ mutateForm: () => mutate(),
63
+ formInfo: {
64
+ encounterUuid: encounterUUID,
65
+ formUuid: autopsyFormUuid,
66
+ patientUuid,
67
+ visitTypeUuid: '',
68
+ visitUuid: '',
69
+ },
70
+ });
71
+ };
72
+
73
+ const tableHeader = [
74
+ {
75
+ key: 'date',
76
+ header: t('dateTime', 'Date & time'),
77
+ },
78
+ {
79
+ key: 'visitType',
80
+ header: t('visitType', 'Visit type'),
81
+ },
82
+ {
83
+ key: 'encounterType',
84
+ header: t('encounterType', 'Encounter type'),
85
+ },
86
+ {
87
+ key: 'formName',
88
+ header: t('formName', 'Form name'),
89
+ },
90
+ {
91
+ key: 'mortician',
92
+ header: t('mortician', 'Mortician name'),
93
+ },
94
+ {
95
+ key: 'action',
96
+ header: t('action', 'Action'),
97
+ },
98
+ ];
99
+
100
+ if (isLoading) {
101
+ return <DataTableSkeleton headers={tableHeader} aria-label={headerTitle} />;
102
+ }
103
+
104
+ if (error) {
105
+ return <ErrorState error={error} headerTitle={headerTitle} />;
106
+ }
107
+
108
+ if (!encounters || encounters.length === 0) {
109
+ return <EmptyState displayText={headerTitle} headerTitle={headerTitle} launchForm={handleLaunchAutopsyForm} />;
110
+ }
111
+
112
+ const tableRows = encounters.map((encounter) => ({
113
+ id: `${encounter.uuid}`,
114
+ date: formatDate(parseDate(encounter?.encounterDatetime)),
115
+ visitType: encounter?.visit?.visitType?.display,
116
+ encounterType: encounter?.encounterType?.display,
117
+ formName: encounter?.form?.name,
118
+ mortician: encounter?.encounterProviders[0]?.provider?.name,
119
+ action: (
120
+ <OverflowMenu aria-label="overflow-menu" flipped={false}>
121
+ <OverflowMenuItem onClick={() => handleLaunchAutopsyForm(encounter.uuid)} itemText={t('edit', 'Edit')} />
122
+ </OverflowMenu>
123
+ ),
124
+ obs: encounter?.obs,
125
+ }));
126
+
127
+ return (
128
+ <div className={styles.widgetCard}>
129
+ <CardHeader title={headerTitle}>
130
+ <Button
131
+ size="md"
132
+ kind="ghost"
133
+ onClick={() => handleLaunchAutopsyForm()}
134
+ renderIcon={(props) => <Add size={24} {...props} />}
135
+ iconDescription={t('add', 'Add')}>
136
+ {t('add', 'Add')}
137
+ </Button>
138
+ </CardHeader>
139
+ <DataTable isSortable rows={tableRows} headers={tableHeader} size={responsiveSize} useZebraStyles>
140
+ {({
141
+ rows,
142
+ headers,
143
+ getExpandHeaderProps,
144
+ getTableProps,
145
+ getTableContainerProps,
146
+ getHeaderProps,
147
+ getRowProps,
148
+ }) => (
149
+ <TableContainer {...getTableContainerProps}>
150
+ <Table className={styles.table} {...getTableProps()} aria-label="Bill list">
151
+ <TableHead>
152
+ <TableRow>
153
+ <TableExpandHeader enableToggle {...getExpandHeaderProps()} />
154
+ {headers.map((header, i) => (
155
+ <TableHeader
156
+ key={i}
157
+ {...getHeaderProps({
158
+ header,
159
+ })}>
160
+ {header.header}
161
+ </TableHeader>
162
+ ))}
163
+ </TableRow>
164
+ </TableHead>
165
+ <TableBody>
166
+ {rows.map((row, i) => {
167
+ const encounter = encounters.find((enc) => enc.uuid === row.id);
168
+
169
+ return (
170
+ <React.Fragment key={row.id}>
171
+ <TableExpandRow {...getRowProps({ row })}>
172
+ {row.cells.map((cell, index) => (
173
+ <TableCell key={cell.id}>{cell.value}</TableCell>
174
+ ))}
175
+ </TableExpandRow>
176
+ {row.isExpanded && encounter ? (
177
+ <TableExpandedRow className={styles.expandedRow} colSpan={headers.length + 1}>
178
+ <div className={styles.container} key={i}>
179
+ <EncounterObservations observations={encounter.obs as Observation[]} />
180
+ </div>
181
+ </TableExpandedRow>
182
+ ) : (
183
+ <TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 2} />
184
+ )}
185
+ </React.Fragment>
186
+ );
187
+ })}
188
+ </TableBody>
189
+ </Table>
190
+ </TableContainer>
191
+ )}
192
+ </DataTable>
193
+ {paginated && (
194
+ <Pagination
195
+ forwardText={t('nextPage', 'Next page')}
196
+ backwardText={t('previousPage', 'Previous page')}
197
+ page={currentPage}
198
+ pageSize={pageSize}
199
+ pageSizes={pageSizes}
200
+ totalItems={encounters.length}
201
+ className={styles.pagination}
202
+ size={responsiveSize}
203
+ onChange={({ page: newPage, pageSize }) => {
204
+ if (newPage !== currentPage) {
205
+ goTo(newPage);
206
+ }
207
+ setPageSize(pageSize);
208
+ }}
209
+ />
210
+ )}
211
+ </div>
212
+ );
213
+ };
214
+
215
+ export default AutopsyView;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { ExtensionSlot, navigate, usePatient } from '@openmrs/esm-framework';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { EmptyState, getPatientUuidFromStore } from '@openmrs/esm-patient-common-lib';
5
+ import styles from './panels.scss';
6
+
7
+ const BillingHistoryView: React.FC = () => {
8
+ const { t } = useTranslation();
9
+ const patientUuid = getPatientUuidFromStore();
10
+ return <ExtensionSlot name="patient-chart-billing-dashboard-slot" state={{ patientUuid }} />;
11
+ };
12
+
13
+ export default BillingHistoryView;
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { SkeletonText } from '@carbon/react';
4
+ import { useConfig } from '@openmrs/esm-framework';
5
+ import styles from './observation.scss';
6
+ import { Observation } from '../../../types';
7
+
8
+ interface EncounterObservationsProps {
9
+ observations: Array<Observation>;
10
+ }
11
+
12
+ const EncounterObservations: React.FC<EncounterObservationsProps> = ({ observations }) => {
13
+ const { t } = useTranslation();
14
+ const { obsConceptUuidsToHide = [] } = useConfig();
15
+
16
+ const filterObservations = (obsList: Array<Observation>) =>
17
+ obsConceptUuidsToHide.length ? obsList.filter((obs) => !obsConceptUuidsToHide.includes(obs.concept.uuid)) : obsList;
18
+
19
+ const renderAnswer = (value: any) => (typeof value === 'object' ? value?.name?.name || '' : value);
20
+
21
+ if (!observations) {
22
+ return <SkeletonText />;
23
+ }
24
+
25
+ const filteredObservations = filterObservations(observations);
26
+
27
+ return (
28
+ <div className={styles.observation}>
29
+ {filteredObservations.length > 0 ? (
30
+ filteredObservations.map((obs) =>
31
+ obs.groupMembers ? (
32
+ <React.Fragment key={obs.uuid}>
33
+ <span className={styles.parentConcept}>{obs.concept?.name?.name}</span>
34
+ {obs.groupMembers.map((member) => (
35
+ <React.Fragment key={member.uuid}>
36
+ <span className={styles.childConcept}>{member.concept?.display}</span>
37
+ <span>{renderAnswer(member.value)}</span>
38
+ </React.Fragment>
39
+ ))}
40
+ </React.Fragment>
41
+ ) : (
42
+ <React.Fragment key={obs.uuid}>
43
+ <span>{obs.concept?.name?.name}</span>
44
+ <span>{renderAnswer(obs.value)}</span>
45
+ </React.Fragment>
46
+ ),
47
+ )
48
+ ) : (
49
+ <div className={styles.observation}>
50
+ <p>{t('noObservationsFound', 'No observations found')}</p>
51
+ </div>
52
+ )}
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export default EncounterObservations;
@@ -0,0 +1,24 @@
1
+ @use '@carbon/layout';
2
+
3
+ .observation {
4
+ display: grid;
5
+ grid-template-columns: 1fr 1fr;
6
+ grid-gap: layout.$spacing-03;
7
+ margin: layout.$spacing-05 layout.$spacing-05 layout.$spacing-05 0;
8
+ }
9
+
10
+ .observation > span {
11
+ align-self: center;
12
+ }
13
+
14
+ .parentConcept {
15
+ font-weight: bold;
16
+ }
17
+
18
+ .childConcept {
19
+ padding-left: 0.8rem;
20
+ }
21
+
22
+ .questionText {
23
+ font-weight: bold;
24
+ }
@@ -0,0 +1,46 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/colors';
3
+
4
+ .observation {
5
+ display: grid;
6
+ grid-template-columns: 1fr 1fr;
7
+ grid-gap: layout.$spacing-03;
8
+ margin-block: layout.$spacing-05;
9
+ margin-inline: 0 layout.$spacing-05;
10
+ }
11
+
12
+ .observation > span {
13
+ align-self: center;
14
+ justify-self: start;
15
+ }
16
+
17
+ .parentConcept {
18
+ font-weight: bold;
19
+ }
20
+
21
+ .childConcept {
22
+ padding-inline-start: layout.$spacing-04;
23
+ }
24
+ .medicalHistoryContainer {
25
+ background-color: colors.$gray-30;
26
+ width: 98%;
27
+ margin: 0 auto;
28
+ max-width: 95vw;
29
+ padding-bottom: 0;
30
+ }
31
+
32
+ .filterContainer {
33
+ display: flex;
34
+ justify-content: flex-end;
35
+ align-items: center;
36
+ margin-bottom: 1rem;
37
+ gap: 0.5rem;
38
+ }
39
+
40
+ .search {
41
+ max-width: 300px;
42
+ }
43
+ .table {
44
+ border: 1px solid colors.$gray-30;
45
+ padding: layout.$spacing-05 layout.$spacing-05;
46
+ }
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import classNames from 'classnames';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Tab, TabList, TabPanel, TabPanels, Tabs, InlineLoading } from '@carbon/react';
5
+ import { ExtensionSlot, useConfig, useLayoutType } from '@openmrs/esm-framework';
6
+
7
+ import styles from './views-details.scss';
8
+ import { getPatientUuidFromStore } from '@openmrs/esm-patient-common-lib';
9
+ import { usePerson } from '../deceased-patient-header/deceasedInfo/deceased-info.resource';
10
+ import BillingHistoryView from './panels/billing-history.component';
11
+ import AutopsyView from './panels/autopsy.component';
12
+ import AttachmentView from './panels/attachement.component';
13
+ import { useActiveMorgueVisit } from './view-details.resource';
14
+ import BannerInfo from '../extension/deceasedInfoBanner.component';
15
+
16
+ interface MortuarySummaryProps {
17
+ bedNumber?: string;
18
+ }
19
+
20
+ const MortuarySummary: React.FC<MortuarySummaryProps> = ({ bedNumber }) => {
21
+ const { t } = useTranslation();
22
+ const patientUuid = getPatientUuidFromStore();
23
+ const { isLoading } = usePerson(patientUuid);
24
+ const { activeVisit, isLoading: isActiveLoading } = useActiveMorgueVisit(patientUuid);
25
+
26
+ if (isLoading || isActiveLoading) {
27
+ return (
28
+ <InlineLoading status="active" iconDescription="Loading" description={t('loadData', 'Loading summary data...')} />
29
+ );
30
+ }
31
+
32
+ return (
33
+ <div className={styles.summaryContainer}>
34
+ <p className={styles.morgueLabel}></p>
35
+ <BannerInfo patientUuid={patientUuid} visit={activeVisit} bedNumber={bedNumber} />
36
+ <Tabs>
37
+ <TabList aria-label="morgue summary tabs" className={styles.tablist}>
38
+ <Tab className={styles.tab} id="billing-tab">
39
+ {t('billingHistory', 'Billing history')}
40
+ </Tab>
41
+ <Tab className={classNames(styles.tab, styles.bodyLong01)} id="autopsy-tab">
42
+ {t('autopsyReport', 'Autopsy report')}
43
+ </Tab>
44
+
45
+ <Tab className={styles.tab} id="medications-tab">
46
+ {t('attachements', 'Attachements')}
47
+ </Tab>
48
+ </TabList>
49
+ <TabPanels>
50
+ <TabPanel>
51
+ <BillingHistoryView />
52
+ </TabPanel>
53
+ <TabPanel>
54
+ <AutopsyView />
55
+ </TabPanel>
56
+ <TabPanel>
57
+ <AttachmentView />
58
+ </TabPanel>
59
+ </TabPanels>
60
+ </Tabs>
61
+ </div>
62
+ );
63
+ };
64
+
65
+ export default MortuarySummary;
@@ -0,0 +1,65 @@
1
+ import { openmrsFetch, useConfig, restBaseUrl, FetchResponse } from '@openmrs/esm-framework';
2
+ import { OpenmrsEncounter, Visit } from '../types';
3
+ import useSWR from 'swr';
4
+ import { ConfigObject } from '../config-schema';
5
+
6
+ export function useAutospyEncounter(patientUuid: string, encounterType: string) {
7
+ const encounterRepresentation =
8
+ 'custom:(uuid,encounterDatetime,encounterType,location:(uuid,name),' +
9
+ 'patient:(uuid,display),encounterProviders:(uuid,provider:(uuid,name)),' +
10
+ 'obs:(uuid,obsDatetime,voided,groupMembers,concept:(uuid,name:(uuid,name)),value:(uuid,name:(uuid,name),' +
11
+ 'names:(uuid,conceptNameType,name))),form:(uuid,name),' +
12
+ 'visit:(visitType:(uuid,display)))';
13
+
14
+ const url = `/ws/rest/v1/encounter?encounterType=${encounterType}&patient=${patientUuid}&v=${encounterRepresentation}`;
15
+
16
+ const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: OpenmrsEncounter[] } }, Error>(
17
+ url,
18
+ openmrsFetch,
19
+ );
20
+
21
+ return {
22
+ encounters: data?.data ? data?.data?.results : [],
23
+ isLoading,
24
+ isValidating,
25
+ error,
26
+ mutate,
27
+ };
28
+ }
29
+
30
+ export const useActiveMorgueVisit = (uuid: string) => {
31
+ const { morgueVisitTypeUuid } = useConfig<ConfigObject>();
32
+ const url = `${restBaseUrl}/visit?v=full&includeInactive=false&totalCount=true&visitType=${morgueVisitTypeUuid}&q=${uuid}`;
33
+
34
+ const { data, error, isLoading } = useSWR<FetchResponse<{ results: Visit[] }>>(url, openmrsFetch);
35
+
36
+ return { activeVisit: data?.data?.results[0], error, isLoading };
37
+ };
38
+
39
+ export const usePatientDischargedStatus = (uuid: string) => {
40
+ const customRepresentation = 'custom:(visitType:(uuid),startDatetime,stopDatetime,encounters:(encounterType:(uuid)))';
41
+ const url = `${restBaseUrl}/visit?v=${customRepresentation}&patient=${uuid}&limit=1`;
42
+
43
+ const { data, error, isLoading } = useSWR<
44
+ FetchResponse<{
45
+ results: Array<{
46
+ visitType: { uuid: string };
47
+ startDatetime: string;
48
+ stopDatetime: string;
49
+ encounters: Array<{
50
+ encounterType: {
51
+ uuid: string;
52
+ };
53
+ }>;
54
+ }>;
55
+ }>
56
+ >(url, openmrsFetch);
57
+
58
+ const status = data?.data?.results[0];
59
+
60
+ return {
61
+ status,
62
+ error,
63
+ isLoading,
64
+ };
65
+ };