@openmrs/esm-appointments-app 4.3.1-pre.1421 → 4.3.1-pre.1443
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 +6 -6
- package/dist/437.js +1 -1
- package/dist/437.js.map +1 -1
- package/dist/574.js +1 -1
- package/dist/75.js +1 -0
- package/dist/75.js.map +1 -0
- package/dist/990.js +1 -1
- package/dist/990.js.map +1 -1
- package/dist/openmrs-esm-appointments-app.js.buildmanifest.json +29 -29
- package/package.json +2 -2
- package/src/appointments/common-components/{appointments-base-table.component.tsx → appointments-table.component.tsx} +13 -17
- package/src/appointments/common-components/appointments-table.scss +18 -0
- package/src/appointments/common-components/appointments-table.test.tsx +158 -0
- package/src/appointments/details/appointment-details.scss +1 -0
- package/src/appointments/details/appointment-details.test.tsx +57 -17
- package/src/appointments/scheduled/scheduled-appointments.component.tsx +3 -3
- package/src/appointments/unscheduled/unscheduled-appointments.component.tsx +24 -17
- package/src/appointments/unscheduled/unscheduled-appointments.test.tsx +146 -0
- package/src/appointments/utils.tsx +11 -14
- package/src/appointments-calendar/monthly/monthly-view-workload.component.tsx +2 -2
- package/src/appointments-calendar/monthly/monthly-view-workload.scss +8 -6
- package/src/home-appointments/appointments-list.scss +2 -0
- package/translations/en.json +0 -1
- package/dist/210.js +0 -1
- package/dist/210.js.map +0 -1
- package/src/appointments/common-components/appointment-base-table.test.tsx +0 -77
|
@@ -2,54 +2,54 @@
|
|
|
2
2
|
"chunks": [
|
|
3
3
|
{
|
|
4
4
|
"rendered": true,
|
|
5
|
-
"initial":
|
|
6
|
-
"entry":
|
|
5
|
+
"initial": false,
|
|
6
|
+
"entry": false,
|
|
7
7
|
"recorded": false,
|
|
8
|
-
"size":
|
|
8
|
+
"size": 1302878,
|
|
9
9
|
"sizes": {
|
|
10
|
-
"
|
|
11
|
-
"javascript": 171154,
|
|
12
|
-
"share-init": 168,
|
|
13
|
-
"runtime": 19616
|
|
10
|
+
"javascript": 1302878
|
|
14
11
|
},
|
|
15
|
-
"names": [
|
|
16
|
-
"main"
|
|
17
|
-
],
|
|
12
|
+
"names": [],
|
|
18
13
|
"idHints": [],
|
|
19
14
|
"runtime": [
|
|
15
|
+
"@openmrs/esm-appointments-app",
|
|
20
16
|
"main"
|
|
21
17
|
],
|
|
22
18
|
"files": [
|
|
23
|
-
"
|
|
19
|
+
"75.js"
|
|
24
20
|
],
|
|
25
21
|
"auxiliaryFiles": [
|
|
26
|
-
"
|
|
22
|
+
"75.js.map"
|
|
27
23
|
],
|
|
28
|
-
"hash": "
|
|
24
|
+
"hash": "98b7523ed9fbb92a",
|
|
29
25
|
"childrenByOrder": {}
|
|
30
26
|
},
|
|
31
27
|
{
|
|
32
28
|
"rendered": true,
|
|
33
|
-
"initial":
|
|
34
|
-
"entry":
|
|
29
|
+
"initial": true,
|
|
30
|
+
"entry": true,
|
|
35
31
|
"recorded": false,
|
|
36
|
-
"size":
|
|
32
|
+
"size": 191064,
|
|
37
33
|
"sizes": {
|
|
38
|
-
"
|
|
34
|
+
"consume-shared": 126,
|
|
35
|
+
"javascript": 171154,
|
|
36
|
+
"share-init": 168,
|
|
37
|
+
"runtime": 19616
|
|
39
38
|
},
|
|
40
|
-
"names": [
|
|
39
|
+
"names": [
|
|
40
|
+
"main"
|
|
41
|
+
],
|
|
41
42
|
"idHints": [],
|
|
42
43
|
"runtime": [
|
|
43
|
-
"@openmrs/esm-appointments-app",
|
|
44
44
|
"main"
|
|
45
45
|
],
|
|
46
46
|
"files": [
|
|
47
|
-
"
|
|
47
|
+
"main.js"
|
|
48
48
|
],
|
|
49
49
|
"auxiliaryFiles": [
|
|
50
|
-
"
|
|
50
|
+
"main.js.map"
|
|
51
51
|
],
|
|
52
|
-
"hash": "
|
|
52
|
+
"hash": "e97c29db522046cb",
|
|
53
53
|
"childrenByOrder": {}
|
|
54
54
|
},
|
|
55
55
|
{
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
"auxiliaryFiles": [
|
|
141
141
|
"437.js.map"
|
|
142
142
|
],
|
|
143
|
-
"hash": "
|
|
143
|
+
"hash": "ae2d6b4900867a99",
|
|
144
144
|
"childrenByOrder": {}
|
|
145
145
|
},
|
|
146
146
|
{
|
|
@@ -226,9 +226,9 @@
|
|
|
226
226
|
"initial": false,
|
|
227
227
|
"entry": false,
|
|
228
228
|
"recorded": false,
|
|
229
|
-
"size":
|
|
229
|
+
"size": 4599,
|
|
230
230
|
"sizes": {
|
|
231
|
-
"javascript":
|
|
231
|
+
"javascript": 4599
|
|
232
232
|
},
|
|
233
233
|
"names": [],
|
|
234
234
|
"idHints": [],
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
"574.js"
|
|
241
241
|
],
|
|
242
242
|
"auxiliaryFiles": [],
|
|
243
|
-
"hash": "
|
|
243
|
+
"hash": "befb8842eb6eb2f7",
|
|
244
244
|
"childrenByOrder": {}
|
|
245
245
|
},
|
|
246
246
|
{
|
|
@@ -498,9 +498,9 @@
|
|
|
498
498
|
"initial": false,
|
|
499
499
|
"entry": false,
|
|
500
500
|
"recorded": false,
|
|
501
|
-
"size":
|
|
501
|
+
"size": 110491,
|
|
502
502
|
"sizes": {
|
|
503
|
-
"javascript":
|
|
503
|
+
"javascript": 110491
|
|
504
504
|
},
|
|
505
505
|
"names": [],
|
|
506
506
|
"idHints": [],
|
|
@@ -514,7 +514,7 @@
|
|
|
514
514
|
"auxiliaryFiles": [
|
|
515
515
|
"990.js.map"
|
|
516
516
|
],
|
|
517
|
-
"hash": "
|
|
517
|
+
"hash": "ad89890929bec485",
|
|
518
518
|
"childrenByOrder": {}
|
|
519
519
|
}
|
|
520
520
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-appointments-app",
|
|
3
|
-
"version": "4.3.1-pre.
|
|
3
|
+
"version": "4.3.1-pre.1443",
|
|
4
4
|
"description": "Appointments front-end module for the OpenMRS SPA",
|
|
5
5
|
"browser": "dist/openmrs-esm-appointments-app.js",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"webpack": "^5.74.0"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "3501bdde5d0462c3dfd3731c03f3e994d199b695"
|
|
52
52
|
}
|
|
@@ -30,9 +30,9 @@ import PatientSearch from '../../patient-search/patient-search.component';
|
|
|
30
30
|
import { MappedAppointment } from '../../types';
|
|
31
31
|
import { getPageSizes, useSearchResults } from '../utils';
|
|
32
32
|
import AppointmentActions from './appointments-actions.component';
|
|
33
|
-
import styles from './appointments-
|
|
33
|
+
import styles from './appointments-table.scss';
|
|
34
34
|
|
|
35
|
-
interface
|
|
35
|
+
interface AppointmentsTableProps {
|
|
36
36
|
appointments: Array<MappedAppointment>;
|
|
37
37
|
isLoading: boolean;
|
|
38
38
|
tableHeading: string;
|
|
@@ -41,7 +41,7 @@ interface AppointmentsBaseTableProps {
|
|
|
41
41
|
scheduleType?: string;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const AppointmentsTable: React.FC<AppointmentsTableProps> = ({
|
|
45
45
|
appointments,
|
|
46
46
|
isLoading,
|
|
47
47
|
tableHeading,
|
|
@@ -63,14 +63,6 @@ const AppointmentsBaseTable: React.FC<AppointmentsBaseTableProps> = ({
|
|
|
63
63
|
header: t('identifier', 'Identifier'),
|
|
64
64
|
key: 'identifier',
|
|
65
65
|
},
|
|
66
|
-
{
|
|
67
|
-
header: t('nextAppointmentDate', 'Next appointment date'),
|
|
68
|
-
key: 'nextAppointmentDate',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
header: t('dateTime', 'Date & Time'),
|
|
72
|
-
key: 'dateTime',
|
|
73
|
-
},
|
|
74
66
|
{
|
|
75
67
|
header: t('serviceType', 'Service Type'),
|
|
76
68
|
key: 'serviceType',
|
|
@@ -115,7 +107,7 @@ const AppointmentsBaseTable: React.FC<AppointmentsBaseTableProps> = ({
|
|
|
115
107
|
|
|
116
108
|
return (
|
|
117
109
|
<>
|
|
118
|
-
<DataTable rows={rowData} headers={headerData} isSortable>
|
|
110
|
+
<DataTable rows={rowData} headers={headerData} isSortable useZebraStyles>
|
|
119
111
|
{({ rows, headers, getHeaderProps, getRowProps, getTableProps, getToolbarProps, getTableContainerProps }) => (
|
|
120
112
|
<TableContainer
|
|
121
113
|
title={`${startCase(tableHeading)} ${t('appointments', 'appointment')}`}
|
|
@@ -158,10 +150,14 @@ const AppointmentsBaseTable: React.FC<AppointmentsBaseTableProps> = ({
|
|
|
158
150
|
<TableCell key={cell.id}>{cell.value?.content ?? cell.value}</TableCell>
|
|
159
151
|
))}
|
|
160
152
|
</TableExpandRow>
|
|
161
|
-
{row.isExpanded
|
|
162
|
-
<
|
|
163
|
-
<
|
|
164
|
-
|
|
153
|
+
{row.isExpanded ? (
|
|
154
|
+
<TableRow className={styles.expandedActiveVisitRow}>
|
|
155
|
+
<th colSpan={headers.length + 2}>
|
|
156
|
+
<AppointmentDetails appointment={appointments[row.id]} />
|
|
157
|
+
</th>
|
|
158
|
+
</TableRow>
|
|
159
|
+
) : (
|
|
160
|
+
<TableExpandedRow className={styles.hiddenRow} colSpan={headers.length + 2} />
|
|
165
161
|
)}
|
|
166
162
|
</React.Fragment>
|
|
167
163
|
))}
|
|
@@ -188,4 +184,4 @@ const AppointmentsBaseTable: React.FC<AppointmentsBaseTableProps> = ({
|
|
|
188
184
|
);
|
|
189
185
|
};
|
|
190
186
|
|
|
191
|
-
export default
|
|
187
|
+
export default AppointmentsTable;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
@use '@carbon/styles/scss/spacing';
|
|
2
|
+
@use '@carbon/styles/scss/type';
|
|
3
|
+
|
|
4
|
+
.expandedActiveVisitRow > td > div {
|
|
5
|
+
max-height: max-content !important;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.expandedActiveVisitRow td {
|
|
9
|
+
padding: 0 2rem;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.expandedActiveVisitRow th[colspan] td[colspan] > div:first-child {
|
|
13
|
+
padding: 0 1rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.hiddenRow {
|
|
17
|
+
display: none;
|
|
18
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import AppointmentsTable from './appointments-table.component';
|
|
4
|
+
import { MappedAppointment } from '../../types';
|
|
5
|
+
import { usePagination } from '@openmrs/esm-framework';
|
|
6
|
+
import { downloadAppointmentsAsExcel } from '../../helpers/excel';
|
|
7
|
+
import { launchOverlay } from '../../hooks/useOverlay';
|
|
8
|
+
import PatientSearch from '../../patient-search/patient-search.component';
|
|
9
|
+
|
|
10
|
+
// Define mock appointments data for testing purposes
|
|
11
|
+
const appointments: Array<MappedAppointment> = [
|
|
12
|
+
{
|
|
13
|
+
patientUuid: '1234',
|
|
14
|
+
name: 'John Smith',
|
|
15
|
+
identifier: '12345',
|
|
16
|
+
dateTime: '2023-04-13T12:00:00.000Z',
|
|
17
|
+
serviceType: 'Service',
|
|
18
|
+
provider: 'Dr. Jane Doe',
|
|
19
|
+
id: '1234',
|
|
20
|
+
age: '50',
|
|
21
|
+
gender: 'F',
|
|
22
|
+
providers: [],
|
|
23
|
+
appointmentKind: 'Scheduled',
|
|
24
|
+
appointmentNumber: '1234',
|
|
25
|
+
location: 'location',
|
|
26
|
+
phoneNumber: '1234567890',
|
|
27
|
+
status: 'status',
|
|
28
|
+
comments: 'some comments',
|
|
29
|
+
dob: '2020-04-13T12:00:00.000Z',
|
|
30
|
+
serviceUuid: '1234',
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const mockUsePagination = usePagination as jest.Mock;
|
|
35
|
+
const mockGoToPage = jest.fn();
|
|
36
|
+
const mockDownloadAppointmentsAsExcel = downloadAppointmentsAsExcel as jest.Mock;
|
|
37
|
+
const mockLaunchOverlay = launchOverlay as jest.Mock;
|
|
38
|
+
|
|
39
|
+
jest.mock('../../helpers/excel');
|
|
40
|
+
jest.mock('../../hooks/useOverlay');
|
|
41
|
+
|
|
42
|
+
jest.mock('@openmrs/esm-framework', () => {
|
|
43
|
+
const originalModule = jest.requireActual('@openmrs/esm-framework');
|
|
44
|
+
return {
|
|
45
|
+
...originalModule,
|
|
46
|
+
openmrsFetch: jest.fn(),
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('AppointmentsBaseTable', () => {
|
|
51
|
+
const props = {
|
|
52
|
+
appointments: [],
|
|
53
|
+
isLoading: false,
|
|
54
|
+
tableHeading: 'Scheduled',
|
|
55
|
+
visits: [],
|
|
56
|
+
scheduleType: 'Scheduled',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
it('should render empty state if appointments are not provided', () => {
|
|
60
|
+
render(<AppointmentsTable {...props} />);
|
|
61
|
+
expect(screen.getByText(/Scheduled appointments/)).toBeInTheDocument();
|
|
62
|
+
const emptyScreenText = screen.getByText(/There are no scheduled appointments to display/);
|
|
63
|
+
expect(emptyScreenText).toBeInTheDocument();
|
|
64
|
+
|
|
65
|
+
const launchAppointmentsForm = screen.getByRole('button', { name: /Create appointment/ });
|
|
66
|
+
fireEvent.click(launchAppointmentsForm);
|
|
67
|
+
expect(mockLaunchOverlay).toHaveBeenCalledWith('Search', <PatientSearch />);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should render loading state when loading data', () => {
|
|
71
|
+
render(<AppointmentsTable {...props} isLoading={true} />);
|
|
72
|
+
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should render table with headers and rows if appointments are provided', async () => {
|
|
76
|
+
mockUsePagination.mockReturnValue({
|
|
77
|
+
results: appointments.slice(0, 2),
|
|
78
|
+
goTo: mockGoToPage,
|
|
79
|
+
currentPage: 1,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
render(<AppointmentsTable {...props} appointments={appointments} />);
|
|
83
|
+
waitFor(() => {
|
|
84
|
+
expect(screen.getByText('Patient name')).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByText('Identifier')).toBeInTheDocument();
|
|
86
|
+
expect(screen.getByText('Service Type')).toBeInTheDocument();
|
|
87
|
+
expect(screen.getByText('Actions')).toBeInTheDocument();
|
|
88
|
+
expect(screen.getByText('John Smith')).toBeInTheDocument();
|
|
89
|
+
expect(screen.getByText('12345')).toBeInTheDocument();
|
|
90
|
+
expect(screen.getByText('Service')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should update search string when search input is changed', () => {
|
|
95
|
+
render(<AppointmentsTable {...props} appointments={appointments} />);
|
|
96
|
+
const searchInput = screen.getByRole('searchbox');
|
|
97
|
+
fireEvent.change(searchInput, { target: { value: 'John' } });
|
|
98
|
+
expect(searchInput).toHaveValue('John');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should contain the title 'Scheduled appointments' and Total count", () => {
|
|
102
|
+
render(<AppointmentsTable {...props} appointments={appointments} />);
|
|
103
|
+
expect(screen.getByText(/Scheduled appointment/)).toBeInTheDocument();
|
|
104
|
+
expect(screen.getByText(/Total 1/)).toBeInTheDocument();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should execute the download function when download button is clicked', () => {
|
|
108
|
+
render(<AppointmentsTable {...props} appointments={appointments} />);
|
|
109
|
+
const downloadButton = screen.getByRole('button', { name: /Download/ });
|
|
110
|
+
fireEvent.click(downloadButton);
|
|
111
|
+
expect(downloadButton).toBeInTheDocument();
|
|
112
|
+
expect(mockDownloadAppointmentsAsExcel).toHaveBeenCalledWith(appointments, expect.anything());
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should have pagination when there are more than 25 appointments', () => {
|
|
116
|
+
const mockAppointments = Array.from({ length: 100 }, (_, i) => ({
|
|
117
|
+
patientUuid: `${i}`,
|
|
118
|
+
name: `Patient ${i}`,
|
|
119
|
+
identifier: `${i}${i}${i}${i}${i}`,
|
|
120
|
+
dateTime: new Date().toISOString(),
|
|
121
|
+
serviceType: 'Service',
|
|
122
|
+
provider: `Dr. Provider ${i}`,
|
|
123
|
+
id: `${i}`,
|
|
124
|
+
age: `${i + 20}`,
|
|
125
|
+
gender: i % 2 === 0 ? 'M' : 'F',
|
|
126
|
+
providers: [],
|
|
127
|
+
appointmentKind: 'Scheduled',
|
|
128
|
+
appointmentNumber: `${i}${i}${i}${i}${i}`,
|
|
129
|
+
location: 'Location',
|
|
130
|
+
phoneNumber: '1234567890',
|
|
131
|
+
status: 'Status',
|
|
132
|
+
comments: 'Some comments',
|
|
133
|
+
dob: new Date(`${i + 1950}-01-01T00:00:00.000Z`).toISOString(),
|
|
134
|
+
serviceUuid: `${i}${i}${i}${i}${i}`,
|
|
135
|
+
}));
|
|
136
|
+
mockUsePagination.mockReturnValue({
|
|
137
|
+
results: appointments.slice(0, 2),
|
|
138
|
+
goTo: mockGoToPage,
|
|
139
|
+
currentPage: 1,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
render(<AppointmentsTable {...props} appointments={mockAppointments} />);
|
|
143
|
+
expect(screen.getByText(/1–25 of 100 items/)).toBeInTheDocument();
|
|
144
|
+
const nextPageButton = screen.getByRole('button', { name: /Next page/ });
|
|
145
|
+
const previousPageButton = screen.getByRole('button', { name: /Previous page/ });
|
|
146
|
+
|
|
147
|
+
// Clicking the next page button should call the goTo function with the next page number
|
|
148
|
+
fireEvent.click(nextPageButton);
|
|
149
|
+
expect(mockGoToPage).toHaveBeenCalledWith(2);
|
|
150
|
+
expect(screen.getByText(/26–50 of 100 items/)).toBeInTheDocument();
|
|
151
|
+
|
|
152
|
+
// Clicking the previous page button should call the goTo function with the previous page number
|
|
153
|
+
fireEvent.click(previousPageButton);
|
|
154
|
+
expect(mockGoToPage).toHaveBeenCalledWith(1);
|
|
155
|
+
|
|
156
|
+
expect(screen.getByText(/1–25 of 100 items/)).toBeInTheDocument();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -1,22 +1,62 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { mockMappedAppointmentsData } from '../../../../../__mocks__/appointments.mock';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
4
3
|
import AppointmentDetails from './appointment-details.component';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const appointment = {
|
|
6
|
+
patientUuid: '12345',
|
|
7
|
+
name: 'John Doe',
|
|
8
|
+
identifier: '1234567890',
|
|
9
|
+
dateTime: new Date().toISOString(),
|
|
10
|
+
serviceType: 'Service',
|
|
11
|
+
provider: 'Dr. Provider',
|
|
12
|
+
id: '1',
|
|
13
|
+
age: '30',
|
|
14
|
+
gender: 'M',
|
|
15
|
+
providers: [],
|
|
16
|
+
appointmentKind: 'Scheduled',
|
|
17
|
+
appointmentNumber: '12345',
|
|
18
|
+
location: 'Location',
|
|
19
|
+
phoneNumber: '1234567890',
|
|
20
|
+
status: 'Status',
|
|
21
|
+
comments: 'Some comments',
|
|
22
|
+
dob: new Date('1992-01-01T00:00:00.000Z').toISOString(),
|
|
23
|
+
serviceUuid: '12345',
|
|
24
|
+
};
|
|
9
25
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
})
|
|
26
|
+
jest.mock('../../hooks/usePatientAppointmentHistory', () => ({
|
|
27
|
+
usePatientAppointmentHistory: () => ({
|
|
28
|
+
appointmentsCount: {
|
|
29
|
+
completedAppointments: 1,
|
|
30
|
+
missedAppointments: 2,
|
|
31
|
+
cancelledAppointments: 3,
|
|
32
|
+
upcomingAppointments: 4,
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
}));
|
|
19
36
|
|
|
20
|
-
|
|
21
|
-
render(<AppointmentDetails appointment={
|
|
22
|
-
|
|
37
|
+
test('renders appointment details correctly', () => {
|
|
38
|
+
const { getByText } = render(<AppointmentDetails appointment={appointment} />);
|
|
39
|
+
expect(getByText('Service')).toBeInTheDocument();
|
|
40
|
+
expect(getByText('1992-01-01T00:00:00.000Z')).toBeInTheDocument();
|
|
41
|
+
expect(getByText('Patient name :')).toBeInTheDocument();
|
|
42
|
+
expect(getByText('John Doe')).toBeInTheDocument();
|
|
43
|
+
expect(getByText('Age :')).toBeInTheDocument();
|
|
44
|
+
expect(getByText('30')).toBeInTheDocument();
|
|
45
|
+
expect(getByText('Gender :')).toBeInTheDocument();
|
|
46
|
+
expect(getByText('Male')).toBeInTheDocument();
|
|
47
|
+
expect(getByText('Dob :')).toBeInTheDocument();
|
|
48
|
+
expect(getByText('1992-01-01T00:00:00.000Z')).toBeInTheDocument();
|
|
49
|
+
expect(getByText('Phone number :')).toBeInTheDocument();
|
|
50
|
+
expect(getByText('1234567890')).toBeInTheDocument();
|
|
51
|
+
expect(getByText('Appointment Notes')).toBeInTheDocument();
|
|
52
|
+
expect(getByText('Some comments')).toBeInTheDocument();
|
|
53
|
+
expect(getByText('Appointment History')).toBeInTheDocument();
|
|
54
|
+
expect(getByText('Completed')).toBeInTheDocument();
|
|
55
|
+
expect(getByText('1')).toBeInTheDocument();
|
|
56
|
+
expect(getByText('Missed')).toBeInTheDocument();
|
|
57
|
+
expect(getByText('2')).toBeInTheDocument();
|
|
58
|
+
expect(getByText('Cancelled')).toBeInTheDocument();
|
|
59
|
+
expect(getByText('3')).toBeInTheDocument();
|
|
60
|
+
expect(getByText('Upcoming')).toBeInTheDocument();
|
|
61
|
+
expect(getByText('4')).toBeInTheDocument();
|
|
62
|
+
});
|
|
@@ -7,7 +7,7 @@ import dayjs from 'dayjs';
|
|
|
7
7
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
|
8
8
|
dayjs.extend(isSameOrBefore);
|
|
9
9
|
import styles from './scheduled-appointments.scss';
|
|
10
|
-
import
|
|
10
|
+
import AppointmentsTable from '../common-components/appointments-table.component';
|
|
11
11
|
|
|
12
12
|
interface ScheduledAppointmentsProps {
|
|
13
13
|
visits: Array<any>;
|
|
@@ -75,7 +75,7 @@ const ScheduledAppointments: React.FC<ScheduledAppointmentsProps> = ({ visits, a
|
|
|
75
75
|
Pending: {
|
|
76
76
|
appointments: filteredRow,
|
|
77
77
|
isLoading,
|
|
78
|
-
tableHeading: isDateInPast ? t('
|
|
78
|
+
tableHeading: isDateInPast ? t('missed', 'Missed') : t('notArrived', 'Not arrived'),
|
|
79
79
|
visits,
|
|
80
80
|
scheduleType,
|
|
81
81
|
},
|
|
@@ -106,7 +106,7 @@ const ScheduledAppointments: React.FC<ScheduledAppointmentsProps> = ({ visits, a
|
|
|
106
106
|
</ContentSwitcher>
|
|
107
107
|
)}
|
|
108
108
|
<div className={styles.container}>
|
|
109
|
-
<
|
|
109
|
+
<AppointmentsTable {...currentConfig} />
|
|
110
110
|
</div>
|
|
111
111
|
</>
|
|
112
112
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import isEmpty from 'lodash-es/isEmpty';
|
|
4
4
|
import {
|
|
@@ -22,10 +22,15 @@ import { ConfigurableLink, usePagination } from '@openmrs/esm-framework';
|
|
|
22
22
|
import { useUnscheduledAppointments } from '../../hooks/useUnscheduledAppointments';
|
|
23
23
|
import { downloadUnscheduledAppointments } from '../../helpers/excel';
|
|
24
24
|
import { EmptyState } from '../../empty-state/empty-state.component';
|
|
25
|
+
import { getPageSizes, useSearchResults } from '../utils';
|
|
25
26
|
|
|
26
27
|
const UnscheduledAppointments: React.FC = () => {
|
|
27
28
|
const { t } = useTranslation();
|
|
29
|
+
const [pageSize, setPageSize] = useState(25);
|
|
30
|
+
|
|
31
|
+
const [searchString, setSearchString] = useState('');
|
|
28
32
|
const { data: unscheduledAppointments, isLoading, error } = useUnscheduledAppointments();
|
|
33
|
+
const searchResults = useSearchResults(unscheduledAppointments, searchString);
|
|
29
34
|
const headerData = [
|
|
30
35
|
{
|
|
31
36
|
header: 'Patient Name',
|
|
@@ -45,7 +50,7 @@ const UnscheduledAppointments: React.FC = () => {
|
|
|
45
50
|
},
|
|
46
51
|
];
|
|
47
52
|
|
|
48
|
-
const { results, currentPage, goTo } = usePagination(
|
|
53
|
+
const { results, currentPage, goTo } = usePagination(searchResults, pageSize);
|
|
49
54
|
|
|
50
55
|
const rowData = results?.map((visit) => ({
|
|
51
56
|
id: `${visit.uuid}`,
|
|
@@ -59,18 +64,11 @@ const UnscheduledAppointments: React.FC = () => {
|
|
|
59
64
|
phoneNumber: visit.phoneNumber === '' ? '--' : visit.phoneNumber,
|
|
60
65
|
}));
|
|
61
66
|
|
|
62
|
-
const pageSizes = useMemo(() => {
|
|
63
|
-
const numberOfPages = Math.ceil(unscheduledAppointments.length / 10);
|
|
64
|
-
return [...Array(numberOfPages).keys()].map((x) => {
|
|
65
|
-
return (x + 1) * 10;
|
|
66
|
-
});
|
|
67
|
-
}, [unscheduledAppointments]);
|
|
68
|
-
|
|
69
67
|
if (isLoading) {
|
|
70
68
|
return <DataTableSkeleton />;
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
if (
|
|
71
|
+
if (!unscheduledAppointments?.length) {
|
|
74
72
|
return (
|
|
75
73
|
<EmptyState
|
|
76
74
|
displayText={t('unscheduledAppointments_lower', 'unscheduled appointments')}
|
|
@@ -83,14 +81,20 @@ const UnscheduledAppointments: React.FC = () => {
|
|
|
83
81
|
return (
|
|
84
82
|
<div>
|
|
85
83
|
<DataTable rows={rowData} headers={headerData} isSortable>
|
|
86
|
-
{({ rows, headers, getHeaderProps, getTableProps
|
|
87
|
-
<TableContainer
|
|
84
|
+
{({ rows, headers, getHeaderProps, getTableProps }) => (
|
|
85
|
+
<TableContainer
|
|
86
|
+
title={`${t('unscheduledAppointments', 'Unscheduled appointments')} ${unscheduledAppointments.length}`}
|
|
87
|
+
description={`${t(`Total ${unscheduledAppointments.length ?? 0}`)}`}>
|
|
88
88
|
<TableToolbar>
|
|
89
89
|
<TableToolbarContent>
|
|
90
|
-
<TableToolbarSearch
|
|
90
|
+
<TableToolbarSearch
|
|
91
|
+
style={{ backgroundColor: '#f4f4f4' }}
|
|
92
|
+
tabIndex={0}
|
|
93
|
+
onChange={(event) => setSearchString(event.target.value)}
|
|
94
|
+
/>
|
|
91
95
|
<Button
|
|
92
96
|
size="lg"
|
|
93
|
-
kind="
|
|
97
|
+
kind="tertiary"
|
|
94
98
|
renderIcon={Download}
|
|
95
99
|
onClick={() => downloadUnscheduledAppointments(unscheduledAppointments)}>
|
|
96
100
|
{t('download', 'Download')}
|
|
@@ -124,9 +128,12 @@ const UnscheduledAppointments: React.FC = () => {
|
|
|
124
128
|
forwardText="Next page"
|
|
125
129
|
page={currentPage}
|
|
126
130
|
pageNumberText="Page Number"
|
|
127
|
-
pageSize={
|
|
128
|
-
onChange={({ page }) =>
|
|
129
|
-
|
|
131
|
+
pageSize={pageSize}
|
|
132
|
+
onChange={({ page, pageSize }) => {
|
|
133
|
+
goTo(page);
|
|
134
|
+
setPageSize(pageSize);
|
|
135
|
+
}}
|
|
136
|
+
pageSizes={getPageSizes(unscheduledAppointments, pageSize) ?? []}
|
|
130
137
|
totalItems={unscheduledAppointments.length ?? 0}
|
|
131
138
|
/>
|
|
132
139
|
</div>
|