Skip to content

Commit 5a14d9e

Browse files
authoredAug 3, 2021
Visit Summary added to Active visits widget (#28)
* Added Expandable rows and copied visit summary from the chart app * Extension slot added for visit summary * Completed Visit summary * Minor change and translations added * Review Changes * Review changes * Translations added * Added abortControllers
1 parent 378a4fa commit 5a14d9e

15 files changed

+1029
-82
lines changed
 

‎packages/esm-active-visits-app/src/active-visits-widget/active-visits.component.tsx

+102-71
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,18 @@ import DataTable, {
99
TableCell,
1010
TableToolbar,
1111
TableToolbarContent,
12+
TableExpandRow,
13+
TableExpandHeader,
1214
} from 'carbon-components-react/es/components/DataTable';
1315
import DataTableSkeleton from 'carbon-components-react/es/components/DataTableSkeleton';
1416
import Pagination from 'carbon-components-react/es/components/Pagination';
1517
import Search from 'carbon-components-react/es/components/Search';
18+
import { useLayoutType, useConfig, usePagination, ConfigurableLink, ExtensionSlot } from '@openmrs/esm-framework';
1619
import { useTranslation } from 'react-i18next';
17-
import { useLayoutType, useConfig, usePagination, ConfigurableLink } from '@openmrs/esm-framework';
1820
import { ActiveVisitRow, fetchActiveVisits } from './active-visits.resource';
1921
import styles from './active-visits.scss';
2022
import dayjs from 'dayjs';
2123

22-
const headerData = [
23-
{
24-
id: 0,
25-
header: 'Visit Time',
26-
key: 'visitStartTime',
27-
},
28-
{
29-
id: 1,
30-
header: 'ID Number',
31-
key: 'IDNumber',
32-
},
33-
{
34-
id: 2,
35-
header: 'Name',
36-
key: 'name',
37-
},
38-
{
39-
id: 3,
40-
header: 'Gender',
41-
key: 'gender',
42-
},
43-
{
44-
id: 4,
45-
header: 'Age',
46-
key: 'age',
47-
},
48-
{
49-
id: 5,
50-
header: 'Visit Type',
51-
key: 'visitType',
52-
},
53-
];
54-
5524
function formatDatetime(startDatetime) {
5625
const todayDate = dayjs();
5726
const today =
@@ -65,7 +34,7 @@ function formatDatetime(startDatetime) {
6534
}
6635
}
6736

68-
const ActiveVisitsTable = (props) => {
37+
const ActiveVisitsTable = () => {
6938
const { t } = useTranslation();
7039
const layout = useLayoutType();
7140
const desktopView = layout === 'desktop';
@@ -76,50 +45,95 @@ const ActiveVisitsTable = (props) => {
7645
const [activeVisits, setActiveVisits] = useState<ActiveVisitRow[]>([]);
7746
const [searchString, setSearchString] = useState('');
7847

79-
const searchResults = useMemo(() => {
80-
if (searchString && searchString.trim() !== '') {
81-
const search = searchString.toLowerCase();
82-
return activeVisits.filter((activeVisitRow) =>
83-
Object.keys(activeVisitRow).some((header) => {
84-
if (header === 'patientUuid') {
85-
return false;
86-
}
87-
return `${activeVisitRow[header]}`.toLowerCase().includes(search);
88-
}),
89-
);
90-
} else {
91-
return activeVisits;
92-
}
93-
}, [searchString, activeVisits]);
94-
const { goTo, currentPage, results } = usePagination(searchResults, currentPageSize);
48+
const headerData = useMemo(
49+
() => [
50+
{
51+
id: 0,
52+
header: t('visitStartTime', 'Visit Time'),
53+
key: 'visitStartTime',
54+
},
55+
{
56+
id: 1,
57+
header: t('IDNumber', 'ID Number'),
58+
key: 'IDNumber',
59+
},
60+
{
61+
id: 2,
62+
header: t('name', 'Name'),
63+
key: 'name',
64+
},
65+
{
66+
id: 3,
67+
header: t('gender', 'Gender'),
68+
key: 'gender',
69+
},
70+
{
71+
id: 4,
72+
header: t('age', 'Age'),
73+
key: 'age',
74+
},
75+
{
76+
id: 5,
77+
header: t('visitType', 'Visit Type'),
78+
key: 'visitType',
79+
},
80+
],
81+
[t],
82+
);
9583

9684
useEffect(() => {
97-
const activeVisits = fetchActiveVisits().subscribe((data) => {
85+
const abortController = new AbortController();
86+
fetchActiveVisits(abortController).then(({ data }) => {
9887
const rowData = data.results.map((visit, ind) => ({
9988
id: `${ind}`,
10089
visitStartTime: formatDatetime(visit.startDatetime),
10190
IDNumber: visit?.patient?.identifiers[0]?.identifier,
10291
name: visit?.patient?.person?.display,
10392
gender: visit?.patient?.person?.gender,
10493
age: visit?.patient?.person?.age,
105-
visitType: visit?.visitType.display,
94+
visitType: visit?.visitType?.display,
10695
patientUuid: visit?.patient?.uuid,
96+
visitUuid: visit?.uuid,
10797
}));
10898
setActiveVisits(rowData);
10999
setLoading(false);
110100
});
111-
return () => activeVisits.unsubscribe();
101+
102+
return () => abortController.abort();
112103
}, []);
113104

105+
const searchResults = useMemo(() => {
106+
if (searchString && searchString.trim() !== '') {
107+
const search = searchString.toLowerCase();
108+
return activeVisits.filter((activeVisitRow) =>
109+
Object.keys(activeVisitRow).some((header) => {
110+
if (header === 'patientUuid') {
111+
return false;
112+
}
113+
return `${activeVisitRow[header]}`.toLowerCase().includes(search);
114+
}),
115+
);
116+
} else {
117+
return activeVisits;
118+
}
119+
}, [searchString, activeVisits]);
120+
121+
const { goTo, results, currentPage } = usePagination(searchResults, currentPageSize);
114122
const handleSearch = useCallback((e) => setSearchString(e.target.value), []);
115123

124+
useEffect(() => {
125+
if (currentPage !== 1) {
126+
goTo(1);
127+
}
128+
}, [searchString]);
129+
116130
return !loading ? (
117131
<div className={styles.activeVisitsContainer}>
118132
<div className={styles.activeVisitsDetailHeaderContainer}>
119133
<h4 className={styles.productiveHeading02}>{t('activeVisits', 'Active Visits')}</h4>
120134
</div>
121135
<DataTable rows={results} headers={headerData} isSortable>
122-
{({ rows, headers, getHeaderProps, getTableProps, getBatchActionProps }) => (
136+
{({ rows, headers, getHeaderProps, getTableProps, getBatchActionProps, getRowProps }) => (
123137
<TableContainer title="" className={styles.tableContainer}>
124138
<TableToolbar>
125139
<TableToolbarContent>
@@ -131,35 +145,52 @@ const ActiveVisitsTable = (props) => {
131145
/>
132146
</TableToolbarContent>
133147
</TableToolbar>
134-
<Table {...getTableProps()} useZebraStyles>
148+
<Table className={styles.activeVisitsTable} {...getTableProps()} size={desktopView ? 'short' : 'normal'}>
135149
<TableHead>
136-
<TableRow style={{ height: desktopView ? '2rem' : '3rem' }}>
150+
<TableRow>
151+
<TableExpandHeader />
137152
{headers.map((header) => (
138153
<TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
139154
))}
140155
</TableRow>
141156
</TableHead>
142157
<TableBody>
143158
{rows.map((row, ind) => (
144-
<TableRow key={row.id} style={{ height: desktopView ? '2rem' : '3rem' }}>
145-
{row.cells.map((cell) => (
146-
<TableCell key={cell.id}>
147-
{cell.info.header === 'name' ? (
148-
<ConfigurableLink to={`\${openmrsSpaBase}/patient/${results[ind]?.patientUuid}/chart/`}>
149-
{cell.value}
150-
</ConfigurableLink>
151-
) : (
152-
cell.value
153-
)}
154-
</TableCell>
155-
))}
156-
</TableRow>
159+
<React.Fragment key={row.id}>
160+
<TableExpandRow {...getRowProps({ row })}>
161+
{row.cells.map((cell) => (
162+
<TableCell key={cell.id}>
163+
{cell.info.header === 'name' ? (
164+
<ConfigurableLink to={`\${openmrsSpaBase}/patient/${results[ind]?.patientUuid}/chart/`}>
165+
{cell.value}
166+
</ConfigurableLink>
167+
) : (
168+
cell.value
169+
)}
170+
</TableCell>
171+
))}
172+
</TableExpandRow>
173+
{row.isExpanded && (
174+
<TableRow className={styles.expandedActiveVisitRow} colSpan={headers.length + 1}>
175+
<th colSpan={headers.length + 2}>
176+
<ExtensionSlot
177+
className={styles.visitSummaryContainer}
178+
extensionSlotName="visit-summary-slot"
179+
state={{
180+
visitUuid: results[ind]?.visitUuid,
181+
patientUuid: results[ind]?.patientUuid,
182+
}}
183+
/>
184+
</th>
185+
</TableRow>
186+
)}
187+
</React.Fragment>
157188
))}
158189
</TableBody>
159190
</Table>
160191
{rows.length === 0 && (
161192
<p
162-
style={{ height: desktopView ? '2rem' : '3rem' }}
193+
style={{ height: desktopView ? '2rem' : '3rem', marginLeft: desktopView ? '2rem' : '3rem' }}
163194
className={`${styles.emptyRow} ${styles.bodyLong01}`}>
164195
{t('noVisitsFound', 'No visits found')}
165196
</p>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { FetchResponse, openmrsFetch, openmrsObservableFetch, OpenmrsResource } from '@openmrs/esm-framework';
2-
import { take, map } from 'rxjs/operators';
1+
import { openmrsFetch, Visit } from '@openmrs/esm-framework';
32

43
export interface ActiveVisitRow {
54
id: string;
@@ -10,18 +9,15 @@ export interface ActiveVisitRow {
109
age: string;
1110
visitType: string;
1211
patientUuid: string;
12+
visitUuid: string;
1313
}
1414

15-
export function fetchActiveVisits() {
15+
export function fetchActiveVisits(abortController: AbortController) {
1616
const v =
1717
'custom:(uuid,patient:(uuid,identifiers:(identifier,uuid),person:(age,display,gender,uuid)),' +
1818
'visitType:(uuid,name,display),location:(uuid,name,display),startDatetime,' +
1919
'stopDatetime)';
20-
return openmrsObservableFetch(`/ws/rest/v1/visit?includeInactive=false&v=${v}`, {
21-
headers: {
22-
contentType: 'application/json',
23-
},
24-
})
25-
.pipe(take(1))
26-
.pipe(map((response: FetchResponse<{ results: Array<any> }>) => response.data));
20+
return openmrsFetch<{ results: Visit[] }>(`/ws/rest/v1/visit?includeInactive=false&v=${v}`, {
21+
signal: abortController.signal,
22+
});
2723
}

‎packages/esm-active-visits-app/src/active-visits-widget/active-visits.scss

+27
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,30 @@
4141
display: flex;
4242
align-items: center;
4343
}
44+
45+
.activeVisitsTable tr[data-parent-row]:nth-child(odd) td {
46+
background-color: #ffffff;
47+
}
48+
49+
.activeVisitsTable tbody tr[data-parent-row]:nth-child(even) td {
50+
background-color: #f4f4f4;
51+
}
52+
53+
.visitSummaryContainer {
54+
width: 100%;
55+
max-width: 768px;
56+
margin: 1rem auto;
57+
}
58+
59+
.expandedActiveVisitRow > td > div {
60+
max-height: max-content !important;
61+
}
62+
63+
.expandedActiveVisitRow td {
64+
padding: 0 2rem;
65+
}
66+
67+
.expandedActiveVisitRow th[colspan] td[colspan] > div:first-child {
68+
padding: 0 1rem;
69+
}
70+

‎packages/esm-active-visits-app/src/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ function setupOpenMRS() {
2828
slot: 'homepage-widgets-slot',
2929
load: getAsyncLifecycle(() => import('./active-visits-widget/active-visits.component'), options),
3030
},
31+
{
32+
id: 'visit-summary-widget',
33+
slot: 'visit-summary-slot',
34+
load: getAsyncLifecycle(() => import('./visits-summary/visit-detail.component'), options),
35+
},
3136
],
3237
};
3338
}

‎packages/esm-active-visits-app/src/root.scss

+16
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,19 @@
1212
.bodyLong01 {
1313
@include carbon--type-style("body-long-01");
1414
}
15+
16+
.caption01 {
17+
@include carbon--type-style("caption-01");
18+
}
19+
20+
.bodyShort02 {
21+
@include carbon--type-style("body-short-02");
22+
}
23+
24+
.text02 {
25+
color: #525252;
26+
}
27+
28+
.text01 {
29+
color: #161616;
30+
}

0 commit comments

Comments
 (0)
Failed to load comments.