@kenyaemr/esm-active-visits-app 8.1.1-pre.129 → 8.1.2-pre.152
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.
- package/.turbo/turbo-build.log +17 -17
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/136.js +2 -0
- package/dist/136.js.map +1 -0
- package/dist/236.js +1 -0
- package/dist/240.js +1 -0
- package/dist/261.js +1 -0
- package/dist/271.js +1 -1
- package/dist/272.js +1 -0
- package/dist/319.js +1 -1
- package/dist/336.js +1 -0
- package/dist/378.js +1 -0
- package/dist/460.js +1 -1
- package/dist/539.js +1 -0
- package/dist/566.js +1 -0
- package/dist/6.js +1 -1
- package/dist/6.js.map +1 -1
- package/dist/652.js +1 -0
- package/dist/673.js +1 -0
- package/dist/705.js +1 -0
- package/dist/711.js +1 -0
- package/dist/725.js +1 -1
- package/dist/727.js +1 -0
- package/dist/737.js +1 -0
- package/dist/744.js +1 -0
- package/dist/899.js +1 -0
- package/dist/967.js +1 -1
- package/dist/kenyaemr-esm-active-visits-app.js +1 -1
- package/dist/kenyaemr-esm-active-visits-app.js.buildmanifest.json +389 -37
- package/dist/kenyaemr-esm-active-visits-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package-lock.json +2028 -1668
- package/package.json +5 -5
- package/src/active-visits-widget/active-visits.component.tsx +156 -187
- package/src/active-visits-widget/active-visits.resource.tsx +202 -28
- package/src/active-visits-widget/active-visits.scss +18 -0
- package/src/active-visits-widget/active-visits.test.tsx +120 -83
- package/src/config-schema.ts +11 -1
- package/src/types/index.ts +156 -1
- package/src/visits-summary/visit-detail.component.tsx +3 -2
- package/src/visits-summary/visit-detail.test.tsx +27 -14
- package/src/visits-summary/visit.resource.ts +1 -135
- package/src/visits-summary/visits-components/encounter-list.component.tsx +9 -9
- package/src/visits-summary/visits-components/encounter-observations.component.tsx +1 -1
- package/src/visits-summary/visits-components/encounter-observations.test.tsx +1 -1
- package/src/visits-summary/visits-components/medications-summary.component.tsx +1 -1
- package/src/visits-summary/visits-components/notes-summary.component.tsx +1 -1
- package/src/visits-summary/visits-components/tests-summary.component.tsx +1 -1
- package/src/visits-summary/visits-components/visit-summary.component.tsx +4 -4
- package/translations/ar.json +6 -6
- package/translations/de.json +37 -0
- package/translations/es.json +11 -11
- package/translations/hi.json +37 -0
- package/translations/hi_IN.json +37 -0
- package/translations/id.json +37 -0
- package/translations/it.json +37 -0
- package/translations/ne.json +37 -0
- package/translations/pt.json +37 -0
- package/translations/pt_BR.json +37 -0
- package/translations/qu.json +37 -0
- package/translations/si.json +37 -0
- package/translations/sw.json +37 -0
- package/translations/sw_KE.json +37 -0
- package/translations/tr.json +37 -0
- package/translations/tr_TR.json +37 -0
- package/translations/uk.json +37 -0
- package/translations/vi.json +37 -0
- package/translations/zh.json +1 -1
- package/dist/586.js +0 -2
- package/dist/586.js.map +0 -1
- /package/dist/{586.js.LICENSE.txt → 136.js.LICENSE.txt} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenyaemr/esm-active-visits-app",
|
|
3
|
-
"version": "8.1.
|
|
3
|
+
"version": "8.1.2-pre.152",
|
|
4
4
|
"description": "Active visits widget microfrontend for the OpenMRS SPA",
|
|
5
5
|
"browser": "dist/kenyaemr-esm-active-visits-app.js",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
|
|
19
19
|
"coverage": "yarn test --coverage",
|
|
20
20
|
"typescript": "tsc",
|
|
21
|
-
"extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.extension.tsx' 'src/**/*modal.tsx' 'src/**/*.workspace.tsx' 'src/index.ts' --config ../../tools/i18next-parser.config.js"
|
|
21
|
+
"extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.resource.tsx' 'src/**/*.extension.tsx' 'src/**/*modal.tsx' 'src/**/*.workspace.tsx' 'src/index.ts' --config ../../tools/i18next-parser.config.js"
|
|
22
22
|
},
|
|
23
23
|
"browserslist": [
|
|
24
24
|
"extends browserslist-config-openmrs"
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"url": "https://github.com/openmrs/openmrs-esm-patient-management/issues"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@carbon/react": "
|
|
40
|
+
"@carbon/react": "^1.71.0",
|
|
41
41
|
"lodash-es": "^4.17.15"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@openmrs/esm-framework": "
|
|
44
|
+
"@openmrs/esm-framework": "6.x",
|
|
45
45
|
"dayjs": "1.x",
|
|
46
46
|
"react": "^18.1.0",
|
|
47
47
|
"react-dom": "^18.1.0",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"webpack": "^5.74.0"
|
|
53
53
|
},
|
|
54
|
-
"stableVersion": "8.
|
|
54
|
+
"stableVersion": "8.0.2"
|
|
55
55
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo, useState
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
DataTable,
|
|
4
4
|
DataTableSkeleton,
|
|
@@ -20,88 +20,53 @@ import {
|
|
|
20
20
|
} from '@carbon/react';
|
|
21
21
|
import { useTranslation } from 'react-i18next';
|
|
22
22
|
import {
|
|
23
|
-
|
|
23
|
+
ConfigurableLink,
|
|
24
|
+
ErrorState,
|
|
25
|
+
ExtensionSlot,
|
|
24
26
|
isDesktop,
|
|
25
27
|
useConfig,
|
|
28
|
+
useLayoutType,
|
|
26
29
|
usePagination,
|
|
27
|
-
ExtensionSlot,
|
|
28
|
-
ErrorState,
|
|
29
|
-
ConfigurableLink,
|
|
30
30
|
} from '@openmrs/esm-framework';
|
|
31
31
|
import { EmptyDataIllustration } from './empty-data-illustration.component';
|
|
32
|
-
import { useActiveVisits } from './active-visits.resource';
|
|
32
|
+
import { useActiveVisits, useActiveVisitsSorting, useObsConcepts, useTableHeaders } from './active-visits.resource';
|
|
33
33
|
import styles from './active-visits.scss';
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
let headersIndex = 0;
|
|
37
|
-
|
|
38
|
-
const headers = [
|
|
39
|
-
{
|
|
40
|
-
id: headersIndex++,
|
|
41
|
-
header: t('visitStartTime', 'Visit Time'),
|
|
42
|
-
key: 'visitStartTime',
|
|
43
|
-
},
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
config?.activeVisits?.identifiers?.map((identifier) => {
|
|
47
|
-
headers.push({
|
|
48
|
-
id: headersIndex++,
|
|
49
|
-
header: t(identifier?.header?.key, identifier?.header?.default),
|
|
50
|
-
key: identifier?.header?.key,
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
if (!config?.activeVisit?.identifiers) {
|
|
55
|
-
headers.push({
|
|
56
|
-
id: headersIndex++,
|
|
57
|
-
header: t('idNumber', 'ID Number'),
|
|
58
|
-
key: 'idNumber',
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
config?.activeVisits?.attributes?.map((attribute) => {
|
|
63
|
-
headers.push({
|
|
64
|
-
id: headersIndex++,
|
|
65
|
-
header: t(attribute?.header?.key, attribute?.header?.default),
|
|
66
|
-
key: attribute?.header?.key,
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
headers.push(
|
|
71
|
-
{
|
|
72
|
-
id: headersIndex++,
|
|
73
|
-
header: t('name', 'Name'),
|
|
74
|
-
key: 'name',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
id: headersIndex++,
|
|
78
|
-
header: t('gender', 'Gender'),
|
|
79
|
-
key: 'gender',
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
id: headersIndex++,
|
|
83
|
-
header: t('age', 'Age'),
|
|
84
|
-
key: 'age',
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
id: headersIndex++,
|
|
88
|
-
header: t('visitType', 'Visit Type'),
|
|
89
|
-
key: 'visitType',
|
|
90
|
-
},
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
return headers;
|
|
94
|
-
}
|
|
34
|
+
import { type ActiveVisitsConfigSchema } from '../config-schema';
|
|
35
|
+
import { type ActiveVisit } from '../types';
|
|
95
36
|
|
|
96
37
|
const ActiveVisitsTable = () => {
|
|
97
38
|
const { t } = useTranslation();
|
|
98
|
-
const config = useConfig();
|
|
39
|
+
const config = useConfig<ActiveVisitsConfigSchema>();
|
|
99
40
|
const layout = useLayoutType();
|
|
100
41
|
const pageSizes = config?.activeVisits?.pageSizes ?? [10, 20, 30, 40, 50];
|
|
101
42
|
const [pageSize, setPageSize] = useState(config?.activeVisits?.pageSize ?? 10);
|
|
43
|
+
const { obsConcepts, isLoadingObsConcepts } = useObsConcepts(config.activeVisits.obs);
|
|
102
44
|
const { activeVisits, isLoading, isValidating, error } = useActiveVisits();
|
|
103
45
|
const [searchString, setSearchString] = useState('');
|
|
104
|
-
const headerData =
|
|
46
|
+
const headerData = useTableHeaders(obsConcepts);
|
|
47
|
+
|
|
48
|
+
const transformVisitForDisplay = useCallback(
|
|
49
|
+
(visit: ActiveVisit) => {
|
|
50
|
+
const displayData = { ...visit };
|
|
51
|
+
|
|
52
|
+
// Add observation values to the display data
|
|
53
|
+
obsConcepts?.forEach((concept) => {
|
|
54
|
+
const obsValues = visit?.observations?.[concept.uuid] ?? [];
|
|
55
|
+
const latestObs = obsValues[0];
|
|
56
|
+
|
|
57
|
+
if (latestObs) {
|
|
58
|
+
// Handle both string and object values
|
|
59
|
+
displayData[`obs-${concept.uuid}`] =
|
|
60
|
+
typeof latestObs.value === 'object' ? latestObs.value.display : latestObs.value;
|
|
61
|
+
} else {
|
|
62
|
+
displayData[`obs-${concept.uuid}`] = '--';
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return displayData;
|
|
67
|
+
},
|
|
68
|
+
[obsConcepts],
|
|
69
|
+
);
|
|
105
70
|
|
|
106
71
|
const searchResults = useMemo(() => {
|
|
107
72
|
if (activeVisits !== undefined && activeVisits.length > 0) {
|
|
@@ -118,10 +83,11 @@ const ActiveVisitsTable = () => {
|
|
|
118
83
|
}
|
|
119
84
|
}
|
|
120
85
|
|
|
121
|
-
return activeVisits;
|
|
122
|
-
}, [searchString, activeVisits]);
|
|
86
|
+
return activeVisits.map(transformVisitForDisplay);
|
|
87
|
+
}, [searchString, activeVisits, transformVisitForDisplay]);
|
|
123
88
|
|
|
124
|
-
const {
|
|
89
|
+
const { sortedRows, sortRow } = useActiveVisitsSorting(searchResults);
|
|
90
|
+
const { paginated, goTo, results, currentPage } = usePagination(sortedRows, pageSize as number);
|
|
125
91
|
|
|
126
92
|
const handleSearch = useCallback(
|
|
127
93
|
(e) => {
|
|
@@ -131,7 +97,7 @@ const ActiveVisitsTable = () => {
|
|
|
131
97
|
[goTo, setSearchString],
|
|
132
98
|
);
|
|
133
99
|
|
|
134
|
-
if (isLoading) {
|
|
100
|
+
if (isLoading || isLoadingObsConcepts) {
|
|
135
101
|
return (
|
|
136
102
|
<div className={styles.activeVisitsContainer}>
|
|
137
103
|
<div className={styles.activeVisitsDetailHeaderContainer}>
|
|
@@ -187,125 +153,128 @@ const ActiveVisitsTable = () => {
|
|
|
187
153
|
</Layer>
|
|
188
154
|
</div>
|
|
189
155
|
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
</
|
|
197
|
-
<div className={styles.backgroundDataFetchingIndicator}>
|
|
198
|
-
<span>{isValidating ? <InlineLoading /> : null}</span>
|
|
199
|
-
</div>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div className={styles.activeVisitsContainer}>
|
|
160
|
+
<div className={styles.activeVisitsDetailHeaderContainer}>
|
|
161
|
+
<div className={!isDesktop(layout) ? styles.tabletHeading : styles.desktopHeading}>
|
|
162
|
+
<h4>{t('activeVisits', 'Active Visits')}</h4>
|
|
200
163
|
</div>
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
{
|
|
225
|
-
|
|
164
|
+
<div className={styles.backgroundDataFetchingIndicator}>
|
|
165
|
+
<span>{isValidating ? <InlineLoading /> : null}</span>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
<Search
|
|
169
|
+
labelText=""
|
|
170
|
+
placeholder={t('filterTable', 'Filter table')}
|
|
171
|
+
onChange={handleSearch}
|
|
172
|
+
size={isDesktop(layout) ? 'sm' : 'lg'}
|
|
173
|
+
/>
|
|
174
|
+
<DataTable
|
|
175
|
+
isSortable
|
|
176
|
+
useStaticWidth
|
|
177
|
+
rows={results}
|
|
178
|
+
headers={headerData}
|
|
179
|
+
sortRow={sortRow}
|
|
180
|
+
size={isDesktop(layout) ? 'sm' : 'lg'}
|
|
181
|
+
useZebraStyles={activeVisits?.length > 1}>
|
|
182
|
+
{({ rows, headers, getHeaderProps, getTableProps, getRowProps, getExpandHeaderProps }) => (
|
|
183
|
+
<TableContainer className={styles.tableContainer}>
|
|
184
|
+
<Table className={styles.activeVisitsTable} {...getTableProps()}>
|
|
185
|
+
<TableHead>
|
|
186
|
+
<TableRow>
|
|
187
|
+
<TableExpandHeader enableToggle {...getExpandHeaderProps()} />
|
|
188
|
+
{headers.map((header) => (
|
|
189
|
+
<TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
|
|
190
|
+
))}
|
|
191
|
+
</TableRow>
|
|
192
|
+
</TableHead>
|
|
193
|
+
<TableBody>
|
|
194
|
+
{rows.map((row, index) => {
|
|
195
|
+
const currentVisit = activeVisits.find((visit) => visit.id === row.id);
|
|
226
196
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
197
|
+
if (!currentVisit) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
230
200
|
|
|
231
|
-
|
|
201
|
+
const patientChartUrl = '${openmrsSpaBase}/patient/${patientUuid}/chart';
|
|
232
202
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
)}
|
|
275
|
-
</DataTable>
|
|
276
|
-
{searchResults?.length === 0 && (
|
|
277
|
-
<div className={styles.filterEmptyState}>
|
|
278
|
-
<Layer level={0}>
|
|
279
|
-
<Tile className={styles.filterEmptyStateTile}>
|
|
280
|
-
<p className={styles.filterEmptyStateContent}>{t('noVisitsToDisplay', 'No visits to display')}</p>
|
|
281
|
-
<p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
282
|
-
</Tile>
|
|
283
|
-
</Layer>
|
|
284
|
-
</div>
|
|
203
|
+
return (
|
|
204
|
+
<React.Fragment key={`active-visit-row-${index}`}>
|
|
205
|
+
<TableExpandRow
|
|
206
|
+
{...getRowProps({ row })}
|
|
207
|
+
data-testid={`activeVisitRow${currentVisit.patientUuid || 'unknown'}`}>
|
|
208
|
+
{row.cells.map((cell) => (
|
|
209
|
+
<TableCell key={`active-visit-row-${index}-cell-${cell.id}`} data-testid={cell.id}>
|
|
210
|
+
{cell.info.header === 'name' && currentVisit.patientUuid ? (
|
|
211
|
+
<ConfigurableLink
|
|
212
|
+
to={patientChartUrl}
|
|
213
|
+
templateParams={{ patientUuid: currentVisit.patientUuid }}>
|
|
214
|
+
{cell.value}
|
|
215
|
+
</ConfigurableLink>
|
|
216
|
+
) : (
|
|
217
|
+
cell.value
|
|
218
|
+
)}
|
|
219
|
+
</TableCell>
|
|
220
|
+
))}
|
|
221
|
+
</TableExpandRow>
|
|
222
|
+
{row.isExpanded ? (
|
|
223
|
+
<TableRow className={styles.expandedActiveVisitRow}>
|
|
224
|
+
<th colSpan={headers.length + 2}>
|
|
225
|
+
<ExtensionSlot
|
|
226
|
+
className={styles.visitSummaryContainer}
|
|
227
|
+
name="visit-summary-slot"
|
|
228
|
+
state={{
|
|
229
|
+
patientUuid: currentVisit.patientUuid,
|
|
230
|
+
visitUuid: currentVisit.visitUuid,
|
|
231
|
+
}}
|
|
232
|
+
/>
|
|
233
|
+
</th>
|
|
234
|
+
</TableRow>
|
|
235
|
+
) : (
|
|
236
|
+
<TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 2} />
|
|
237
|
+
)}
|
|
238
|
+
</React.Fragment>
|
|
239
|
+
);
|
|
240
|
+
})}
|
|
241
|
+
</TableBody>
|
|
242
|
+
</Table>
|
|
243
|
+
</TableContainer>
|
|
285
244
|
)}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
245
|
+
</DataTable>
|
|
246
|
+
{searchResults?.length === 0 && (
|
|
247
|
+
<div className={styles.filterEmptyState}>
|
|
248
|
+
<Layer level={0}>
|
|
249
|
+
<Tile className={styles.filterEmptyStateTile}>
|
|
250
|
+
<p className={styles.filterEmptyStateContent}>{t('noVisitsToDisplay', 'No visits to display')}</p>
|
|
251
|
+
<p className={styles.filterEmptyStateHelper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
252
|
+
</Tile>
|
|
253
|
+
</Layer>
|
|
254
|
+
</div>
|
|
255
|
+
)}
|
|
256
|
+
{paginated && (
|
|
257
|
+
<Pagination
|
|
258
|
+
forwardText="Next page"
|
|
259
|
+
backwardText="Previous page"
|
|
260
|
+
page={currentPage}
|
|
261
|
+
pageSize={pageSize}
|
|
262
|
+
pageSizes={pageSizes}
|
|
263
|
+
totalItems={searchResults?.length}
|
|
264
|
+
className={styles.pagination}
|
|
265
|
+
size={isDesktop(layout) ? 'sm' : 'lg'}
|
|
266
|
+
onChange={({ pageSize: newPageSize, page: newPage }) => {
|
|
267
|
+
if (newPageSize !== pageSize) {
|
|
268
|
+
setPageSize(newPageSize);
|
|
269
|
+
}
|
|
270
|
+
if (newPage !== currentPage) {
|
|
271
|
+
goTo(newPage);
|
|
272
|
+
}
|
|
273
|
+
}}
|
|
274
|
+
/>
|
|
275
|
+
)}
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
309
278
|
};
|
|
310
279
|
|
|
311
280
|
export default ActiveVisitsTable;
|