@openmrs/esm-patient-lists-app 11.3.1-pre.9452 → 11.3.1-pre.9455
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 +7 -7
- package/dist/3125.js +1 -1
- package/dist/3125.js.map +1 -1
- package/dist/4341.js +1 -0
- package/dist/4341.js.map +1 -0
- package/dist/5984.js +1 -1
- package/dist/5984.js.map +1 -1
- package/dist/9078.js +1 -1
- package/dist/9078.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-lists-app.js +1 -1
- package/dist/openmrs-esm-patient-lists-app.js.buildmanifest.json +39 -39
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/action-button/patient-lists-action-button.extension.tsx +6 -8
- package/src/index.ts +0 -2
- package/src/routes.json +12 -16
- package/src/workspaces/patient-list-details.workspace.test.tsx +26 -19
- package/src/workspaces/patient-list-details.workspace.tsx +38 -32
- package/src/workspaces/patient-lists.workspace.test.tsx +22 -3
- package/src/workspaces/patient-lists.workspace.tsx +90 -81
- package/dist/8803.js +0 -1
- package/dist/8803.js.map +0 -1
|
@@ -1,50 +1,56 @@
|
|
|
1
1
|
import React, { type ComponentProps, useCallback } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Button } from '@carbon/react';
|
|
4
|
-
import { ArrowLeftIcon, formatDate,
|
|
5
|
-
import { type
|
|
4
|
+
import { ArrowLeftIcon, formatDate, parseDate, Workspace2 } from '@openmrs/esm-framework';
|
|
5
|
+
import { type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
|
|
6
6
|
import { type MappedList, usePatientListMembers } from '../patient-lists.resource';
|
|
7
7
|
import PatientListDetailsTable from './patient-list-details-table.component';
|
|
8
8
|
import styles from './patient-list-details.scss';
|
|
9
9
|
|
|
10
|
-
interface PatientListDetailsWorkspaceProps
|
|
10
|
+
export interface PatientListDetailsWorkspaceProps {
|
|
11
11
|
list: MappedList;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const PatientListDetailsWorkspace: React.FC<PatientWorkspace2DefinitionProps<PatientListDetailsWorkspaceProps, {}>> = ({
|
|
15
|
+
workspaceProps: { list },
|
|
16
|
+
closeWorkspace,
|
|
17
|
+
}) => {
|
|
15
18
|
const { t } = useTranslation();
|
|
16
19
|
const { listMembers, isLoading } = usePatientListMembers(list.id);
|
|
17
20
|
|
|
18
|
-
const closeListDetailsWorkspace = useCallback(() =>
|
|
19
|
-
launchWorkspace('patient-lists');
|
|
20
|
-
}, []);
|
|
21
|
+
const closeListDetailsWorkspace = useCallback(() => closeWorkspace(), [closeWorkspace]);
|
|
21
22
|
|
|
22
23
|
return (
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
<Workspace2
|
|
25
|
+
title={list.name || t('patientListDetailWorkspaceTitle', 'Patient List Details')}
|
|
26
|
+
hasUnsavedChanges={false}
|
|
27
|
+
>
|
|
28
|
+
<main className={styles.container}>
|
|
29
|
+
<section className={styles.header}>
|
|
30
|
+
<h4 className={styles.description}>{list.description ?? '--'}</h4>
|
|
31
|
+
<p className={styles.details}>
|
|
32
|
+
{list.size} {t('patients', 'patients')} ·{' '}
|
|
33
|
+
<span className={styles.label}>{t('createdOn', 'Created on')}:</span>{' '}
|
|
34
|
+
{list.startDate ? formatDate(parseDate(list.startDate)) : null}
|
|
35
|
+
</p>
|
|
36
|
+
</section>
|
|
37
|
+
<div className={styles.backButton}>
|
|
38
|
+
<Button
|
|
39
|
+
kind="ghost"
|
|
40
|
+
renderIcon={(props: ComponentProps<typeof ArrowLeftIcon>) => <ArrowLeftIcon size={24} {...props} />}
|
|
41
|
+
iconDescription="Back to patient lists"
|
|
42
|
+
size="sm"
|
|
43
|
+
onClick={closeListDetailsWorkspace}
|
|
44
|
+
>
|
|
45
|
+
<span>{t('backToPatientLists', 'Back to patient lists')}</span>
|
|
46
|
+
</Button>
|
|
47
|
+
</div>
|
|
48
|
+
<section className={styles.tableContainer}>
|
|
49
|
+
<PatientListDetailsTable isLoading={isLoading} listMembers={listMembers} />
|
|
50
|
+
</section>
|
|
51
|
+
</main>
|
|
52
|
+
</Workspace2>
|
|
47
53
|
);
|
|
48
|
-
}
|
|
54
|
+
};
|
|
49
55
|
|
|
50
56
|
export default PatientListDetailsWorkspace;
|
|
@@ -23,8 +23,7 @@ it('renders an empty state if patient list data is unavailable', async () => {
|
|
|
23
23
|
mutate: jest.fn(),
|
|
24
24
|
isValidating: false,
|
|
25
25
|
});
|
|
26
|
-
|
|
27
|
-
render(<PatientListsWorkspace />);
|
|
26
|
+
renderPatientListWorkspace();
|
|
28
27
|
|
|
29
28
|
expect(screen.getByTitle(/empty data illustration/i)).toBeInTheDocument();
|
|
30
29
|
expect(screen.getByText(/no patient lists to display/i)).toBeInTheDocument();
|
|
@@ -52,7 +51,7 @@ it('renders a tabular overview of the available patient lists', async () => {
|
|
|
52
51
|
isValidating: false,
|
|
53
52
|
});
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
renderPatientListWorkspace();
|
|
56
55
|
|
|
57
56
|
await screen.findByRole('table');
|
|
58
57
|
|
|
@@ -75,3 +74,23 @@ it('renders a tabular overview of the available patient lists', async () => {
|
|
|
75
74
|
await user.type(searchbox, 'COTD');
|
|
76
75
|
expect(screen.getByRole('row', { name: /COTD Study My List 2/i })).toBeInTheDocument();
|
|
77
76
|
});
|
|
77
|
+
|
|
78
|
+
function renderPatientListWorkspace() {
|
|
79
|
+
render(
|
|
80
|
+
<PatientListsWorkspace
|
|
81
|
+
workspaceName={''}
|
|
82
|
+
launchChildWorkspace={jest.fn()}
|
|
83
|
+
closeWorkspace={jest.fn()}
|
|
84
|
+
workspaceProps={{}}
|
|
85
|
+
windowProps={{}}
|
|
86
|
+
groupProps={{
|
|
87
|
+
mutateVisitContext: jest.fn(),
|
|
88
|
+
patient: null,
|
|
89
|
+
patientUuid: null,
|
|
90
|
+
visitContext: null,
|
|
91
|
+
}}
|
|
92
|
+
windowName={''}
|
|
93
|
+
isRootWorkspace={false}
|
|
94
|
+
/>,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -18,21 +18,24 @@ import {
|
|
|
18
18
|
TableToolbarSearch,
|
|
19
19
|
Tile,
|
|
20
20
|
} from '@carbon/react';
|
|
21
|
-
import { EmptyDataIllustration } from '@openmrs/esm-patient-common-lib';
|
|
22
|
-
import { launchWorkspace, useLayoutType } from '@openmrs/esm-framework';
|
|
23
|
-
import { usePatientLists
|
|
21
|
+
import { EmptyDataIllustration, type PatientWorkspace2DefinitionProps } from '@openmrs/esm-patient-common-lib';
|
|
22
|
+
import { launchWorkspace, useLayoutType, Workspace2 } from '@openmrs/esm-framework';
|
|
23
|
+
import { usePatientLists } from '../patient-lists.resource';
|
|
24
24
|
import styles from './patient-lists.scss';
|
|
25
25
|
|
|
26
|
-
function PatientListsWorkspace() {
|
|
26
|
+
function PatientListsWorkspace({ launchChildWorkspace }: PatientWorkspace2DefinitionProps<{}, {}>) {
|
|
27
27
|
const { t } = useTranslation();
|
|
28
28
|
const layout = useLayoutType();
|
|
29
29
|
const responsiveSize = layout === 'tablet' ? 'lg' : 'sm';
|
|
30
30
|
const [searchTerm, setSearchTerm] = useState('');
|
|
31
31
|
const { patientLists, isLoading } = usePatientLists();
|
|
32
32
|
|
|
33
|
-
const launchListDetailsWorkspace = useCallback(
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
const launchListDetailsWorkspace = useCallback(
|
|
34
|
+
(list) => {
|
|
35
|
+
launchChildWorkspace('patient-list-details', { list });
|
|
36
|
+
},
|
|
37
|
+
[launchChildWorkspace],
|
|
38
|
+
);
|
|
36
39
|
|
|
37
40
|
const tableHeaders = [
|
|
38
41
|
{
|
|
@@ -66,90 +69,96 @@ function PatientListsWorkspace() {
|
|
|
66
69
|
|
|
67
70
|
if (isLoading)
|
|
68
71
|
return (
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
+
<Workspace2 title={t('patientListsWorkspaceTitle', 'Patient Lists')} hasUnsavedChanges={false}>
|
|
73
|
+
<div className={styles.skeletonContainer}>
|
|
74
|
+
<DataTableSkeleton className={styles.dataTableSkeleton} rowCount={5} columnCount={5} zebra />
|
|
75
|
+
</div>
|
|
76
|
+
</Workspace2>
|
|
72
77
|
);
|
|
73
78
|
|
|
74
79
|
if (patientLists?.length > 0) {
|
|
75
80
|
return (
|
|
76
|
-
<
|
|
77
|
-
<
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
{header.header}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
81
|
+
<Workspace2 title={t('patientListsWorkspaceTitle', 'Patient Lists')} hasUnsavedChanges={false}>
|
|
82
|
+
<section className={styles.container}>
|
|
83
|
+
<DataTable headers={tableHeaders} rows={filteredLists || []} size={responsiveSize} useZebraStyles>
|
|
84
|
+
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
|
|
85
|
+
<>
|
|
86
|
+
<TableContainer className={styles.tableContainer}>
|
|
87
|
+
<div className={styles.toolbarWrapper}>
|
|
88
|
+
<TableToolbar className={styles.tableToolbar}>
|
|
89
|
+
<TableToolbarContent>
|
|
90
|
+
<TableToolbarSearch
|
|
91
|
+
className={styles.search}
|
|
92
|
+
expanded
|
|
93
|
+
onChange={handleSearchTermChange}
|
|
94
|
+
placeholder={t('searchThisList', 'Search this list')}
|
|
95
|
+
size={responsiveSize}
|
|
96
|
+
/>
|
|
97
|
+
</TableToolbarContent>
|
|
98
|
+
</TableToolbar>
|
|
99
|
+
</div>
|
|
100
|
+
{rows.length > 0 ? (
|
|
101
|
+
<Table aria-label="patient lists" className={styles.table} {...getTableProps()}>
|
|
102
|
+
<TableHead>
|
|
103
|
+
<TableRow>
|
|
104
|
+
{headers.map((header) => (
|
|
105
|
+
<TableHeader key={header.key} {...getHeaderProps({ header })}>
|
|
106
|
+
{header.header}
|
|
107
|
+
</TableHeader>
|
|
108
|
+
))}
|
|
109
|
+
</TableRow>
|
|
110
|
+
</TableHead>
|
|
111
|
+
<TableBody>
|
|
112
|
+
{rows.map((row) => {
|
|
113
|
+
const currentList = patientLists?.find((list) => list?.id === row.id);
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
115
|
+
return (
|
|
116
|
+
<TableRow {...getRowProps({ row })} key={row.id}>
|
|
117
|
+
<TableCell>
|
|
118
|
+
<Link className={styles.link} onClick={() => launchListDetailsWorkspace(currentList)}>
|
|
119
|
+
{currentList?.name ?? ''}
|
|
120
|
+
</Link>
|
|
121
|
+
</TableCell>
|
|
122
|
+
<TableCell>{currentList?.type ?? ''}</TableCell>
|
|
123
|
+
<TableCell>{currentList?.size ?? ''}</TableCell>
|
|
124
|
+
</TableRow>
|
|
125
|
+
);
|
|
126
|
+
})}
|
|
127
|
+
</TableBody>
|
|
128
|
+
</Table>
|
|
129
|
+
) : null}
|
|
130
|
+
</TableContainer>
|
|
131
|
+
{filteredLists?.length === 0 ? (
|
|
132
|
+
<div className={styles.tileContainer}>
|
|
133
|
+
<Tile className={styles.tile}>
|
|
134
|
+
<div className={styles.tileContent}>
|
|
135
|
+
<p className={styles.content}>{t('noMatchingListsFound', 'No matching lists to display')}</p>
|
|
136
|
+
<p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
137
|
+
</div>
|
|
138
|
+
</Tile>
|
|
139
|
+
</div>
|
|
123
140
|
) : null}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<p className={styles.content}>{t('noMatchingListsFound', 'No matching lists to display')}</p>
|
|
130
|
-
<p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
|
|
131
|
-
</div>
|
|
132
|
-
</Tile>
|
|
133
|
-
</div>
|
|
134
|
-
) : null}
|
|
135
|
-
</>
|
|
136
|
-
)}
|
|
137
|
-
</DataTable>
|
|
138
|
-
</section>
|
|
141
|
+
</>
|
|
142
|
+
)}
|
|
143
|
+
</DataTable>
|
|
144
|
+
</section>
|
|
145
|
+
</Workspace2>
|
|
139
146
|
);
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
return (
|
|
143
|
-
<
|
|
144
|
-
<
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
<Workspace2 title={t('patientListsWorkspaceTitle', 'Patient Lists')} hasUnsavedChanges={false}>
|
|
151
|
+
<div className={styles.emptyStateContainer}>
|
|
152
|
+
<Layer>
|
|
153
|
+
<Tile className={styles.emptyStateTile}>
|
|
154
|
+
<div className={styles.tileContent}>
|
|
155
|
+
<EmptyDataIllustration />
|
|
156
|
+
<p className={styles.emptyStateContent}>{t('noPatientListsToDisplay', 'No patient lists to display')}</p>
|
|
157
|
+
</div>
|
|
158
|
+
</Tile>
|
|
159
|
+
</Layer>
|
|
160
|
+
</div>
|
|
161
|
+
</Workspace2>
|
|
153
162
|
);
|
|
154
163
|
}
|
|
155
164
|
|