@kenyaemr/esm-service-queues-app 8.0.1-pre.99 → 8.0.3-pre.131
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 +21 -22
- package/dist/130.js +1 -1
- package/dist/130.js.LICENSE.txt +2 -0
- package/dist/130.js.map +1 -1
- package/dist/199.js +1 -1
- package/dist/199.js.map +1 -1
- package/dist/2.js +1 -0
- package/dist/2.js.map +1 -0
- package/dist/271.js +1 -1
- package/dist/319.js +1 -1
- package/dist/325.js +1 -0
- package/dist/325.js.map +1 -0
- package/dist/372.js +2 -0
- package/dist/372.js.map +1 -0
- package/dist/392.js +1 -1
- package/dist/460.js +1 -1
- package/dist/574.js +1 -1
- package/dist/60.js +1 -0
- package/dist/60.js.map +1 -0
- package/dist/644.js +1 -1
- package/dist/660.js +2 -0
- package/dist/{484.js.LICENSE.txt → 660.js.LICENSE.txt} +10 -0
- package/dist/660.js.map +1 -0
- package/dist/670.js +1 -1
- package/dist/670.js.map +1 -1
- package/dist/727.js +1 -1
- package/dist/748.js +1 -0
- package/dist/748.js.map +1 -0
- package/dist/757.js +1 -1
- package/dist/760.js +1 -1
- package/dist/760.js.map +1 -1
- package/dist/788.js +1 -1
- package/dist/807.js +1 -1
- package/dist/818.js +1 -1
- package/dist/833.js +1 -1
- package/dist/911.js +1 -1
- package/dist/kenyaemr-esm-service-queues-app.js +1 -1
- package/dist/kenyaemr-esm-service-queues-app.js.buildmanifest.json +171 -174
- package/dist/kenyaemr-esm-service-queues-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 +5892 -0
- package/package.json +2 -2
- package/src/active-visits/change-status-dialog.component.tsx +141 -138
- package/src/active-visits/change-status-dialog.test.tsx +5 -5
- package/src/config-schema.ts +4 -10
- package/src/index.ts +7 -0
- package/src/patient-queue-header/patient-queue-header.component.tsx +25 -38
- package/src/patient-queue-header/patient-queue-header.scss +3 -41
- package/src/patient-queue-metrics/clinic-metrics.component.tsx +4 -4
- package/src/patient-queue-metrics/metrics-header.component.tsx +16 -15
- package/src/patient-search/patient-scheduled-visits.component.tsx +43 -52
- package/src/patient-search/visit-form/visit-form.component.tsx +43 -50
- package/src/queue-screen/queue-screen.component.tsx +67 -11
- package/src/queue-screen/queue-screen.test.tsx +1 -1
- package/src/queue-screen/useActiveTickets.tsx +1 -1
- package/src/queue-table/cells/columns.resource.ts +2 -2
- package/src/queue-table/default-queue-table.component.tsx +17 -15
- package/src/queue-table/queue-entry-actions/transition-queue-entry.modal.tsx +7 -2
- package/src/queue-table/queue-table-by-status-menu.component.tsx +1 -1
- package/src/queue-table/queue-table-metrics.component.tsx +6 -1
- package/src/queue-table/queue-table.component.tsx +11 -8
- package/src/queue-table/queue-table.scss +5 -0
- package/src/remove-queue-entry-dialog/remove-queue-entry.component.tsx +16 -15
- package/src/remove-queue-entry-dialog/remove-queue-entry.resource.ts +20 -26
- package/src/routes.json +34 -27
- package/src/transition-latest-queue-entry/transition-latest-queue-entry.component.tsx +33 -0
- package/src/transition-latest-queue-entry/transition-latest-queue-entry.resource.ts +30 -0
- package/translations/am.json +4 -3
- package/translations/ar.json +5 -4
- package/translations/en.json +9 -6
- package/translations/es.json +81 -80
- package/translations/fr.json +255 -254
- package/translations/he.json +5 -4
- package/translations/km.json +5 -4
- package/translations/zh.json +5 -4
- package/translations/zh_CN.json +5 -4
- package/dist/152.js +0 -1
- package/dist/152.js.map +0 -1
- package/dist/255.js +0 -2
- package/dist/255.js.map +0 -1
- package/dist/265.js +0 -1
- package/dist/265.js.map +0 -1
- package/dist/303.js +0 -1
- package/dist/303.js.map +0 -1
- package/dist/484.js +0 -2
- package/dist/484.js.map +0 -1
- package/dist/729.js +0 -1
- package/dist/729.js.map +0 -1
- package/src/patient-queue-header/patient-queue-illustration.component.tsx +0 -22
- /package/dist/{255.js.LICENSE.txt → 372.js.LICENSE.txt} +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
1
3
|
import { ComboButton, MenuItem } from '@carbon/react';
|
|
2
4
|
import {
|
|
3
5
|
UserHasAccess,
|
|
@@ -8,22 +10,29 @@ import {
|
|
|
8
10
|
useLayoutType,
|
|
9
11
|
useSession,
|
|
10
12
|
} from '@openmrs/esm-framework';
|
|
11
|
-
import React, { useState } from 'react';
|
|
12
|
-
import { useTranslation } from 'react-i18next';
|
|
13
13
|
import { spaBasePath } from '../constants';
|
|
14
14
|
import styles from './metrics-header.scss';
|
|
15
15
|
|
|
16
16
|
const MetricsHeader = () => {
|
|
17
17
|
const { t } = useTranslation();
|
|
18
|
+
const currentUserSession = useSession();
|
|
19
|
+
const layout = useLayoutType();
|
|
20
|
+
|
|
18
21
|
const metricsTitle = t('clinicMetrics', 'Clinic metrics');
|
|
19
22
|
const queueScreenText = t('queueScreen', 'Queue screen');
|
|
20
|
-
const currentUserSession = useSession();
|
|
21
23
|
const providerUuid = currentUserSession?.currentProvider?.uuid;
|
|
22
|
-
const layout = useLayoutType();
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
+
const launchAddProviderToRoomModal = useCallback(() => {
|
|
26
|
+
const dispose = showModal('add-provider-to-room-modal', {
|
|
27
|
+
closeModal: () => dispose(),
|
|
28
|
+
providerUuid,
|
|
29
|
+
});
|
|
30
|
+
}, [providerUuid]);
|
|
31
|
+
|
|
32
|
+
const navigateToQueueScreen = useCallback(() => {
|
|
25
33
|
navigate({ to: `${spaBasePath}/service-queues/screen` });
|
|
26
|
-
};
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
27
36
|
return (
|
|
28
37
|
<div className={styles.metricsContainer}>
|
|
29
38
|
<span className={styles.metricsTitle}>{metricsTitle}</span>
|
|
@@ -44,15 +53,7 @@ const MetricsHeader = () => {
|
|
|
44
53
|
onClick={() => launchWorkspace('service-queues-room-form')}
|
|
45
54
|
/>
|
|
46
55
|
</UserHasAccess>
|
|
47
|
-
<MenuItem
|
|
48
|
-
label={t('addProviderQueueRoom', 'Add provider queue room')}
|
|
49
|
-
onClick={() => {
|
|
50
|
-
const dispose = showModal('add-provider-to-room-modal', {
|
|
51
|
-
closeModal: () => dispose(),
|
|
52
|
-
providerUuid,
|
|
53
|
-
});
|
|
54
|
-
}}
|
|
55
|
-
/>
|
|
56
|
+
<MenuItem label={t('addProviderQueueRoom', 'Add provider queue room')} onClick={launchAddProviderToRoomModal} />
|
|
56
57
|
</ComboButton>
|
|
57
58
|
</div>
|
|
58
59
|
);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import head from 'lodash-es/head';
|
|
4
|
-
import { first } from 'rxjs/operators';
|
|
5
4
|
import { useTranslation } from 'react-i18next';
|
|
6
5
|
import {
|
|
7
6
|
Button,
|
|
@@ -113,58 +112,50 @@ const ScheduledVisitsForVisitType: React.FC<{
|
|
|
113
112
|
const abortController = new AbortController();
|
|
114
113
|
|
|
115
114
|
saveVisit(payload, abortController)
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
kind: 'error',
|
|
150
|
-
isLowContrast: false,
|
|
151
|
-
subtitle: error?.message,
|
|
152
|
-
});
|
|
153
|
-
setIsSubmitting(false);
|
|
154
|
-
},
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
(error) => {
|
|
159
|
-
showSnackbar({
|
|
160
|
-
title: t('startVisitError', 'Error starting visit'),
|
|
161
|
-
kind: 'error',
|
|
162
|
-
isLowContrast: false,
|
|
163
|
-
subtitle: error?.message,
|
|
115
|
+
.then((response) => {
|
|
116
|
+
postQueueEntry(
|
|
117
|
+
response.data.uuid,
|
|
118
|
+
patientId,
|
|
119
|
+
priority,
|
|
120
|
+
defaultStatus,
|
|
121
|
+
service,
|
|
122
|
+
appointment,
|
|
123
|
+
selectedQueueLocation,
|
|
124
|
+
visitQueueNumberAttributeUuid,
|
|
125
|
+
)
|
|
126
|
+
.then(() => {
|
|
127
|
+
showSnackbar({
|
|
128
|
+
kind: 'success',
|
|
129
|
+
title: t('startAVisit', 'Start a visit'),
|
|
130
|
+
subtitle: t(
|
|
131
|
+
'startVisitQueueSuccessfully',
|
|
132
|
+
'Patient has been added to active visits list and queue.',
|
|
133
|
+
`${hours} : ${minutes}`,
|
|
134
|
+
),
|
|
135
|
+
});
|
|
136
|
+
closeWorkspace();
|
|
137
|
+
setIsSubmitting(false);
|
|
138
|
+
mutateQueueEntries();
|
|
139
|
+
})
|
|
140
|
+
.catch((error) => {
|
|
141
|
+
showSnackbar({
|
|
142
|
+
title: t('queueEntryError', 'Error adding patient to the queue'),
|
|
143
|
+
kind: 'error',
|
|
144
|
+
isLowContrast: false,
|
|
145
|
+
subtitle: error?.message,
|
|
146
|
+
});
|
|
147
|
+
setIsSubmitting(false);
|
|
164
148
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
149
|
+
})
|
|
150
|
+
.catch((error) => {
|
|
151
|
+
showSnackbar({
|
|
152
|
+
title: t('startVisitError', 'Error starting visit'),
|
|
153
|
+
kind: 'error',
|
|
154
|
+
isLowContrast: false,
|
|
155
|
+
subtitle: error?.message,
|
|
156
|
+
});
|
|
157
|
+
setIsSubmitting(false);
|
|
158
|
+
});
|
|
168
159
|
}
|
|
169
160
|
},
|
|
170
161
|
[
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
|
-
import { first } from 'rxjs/operators';
|
|
4
3
|
import {
|
|
5
4
|
Button,
|
|
6
5
|
ButtonSet,
|
|
@@ -67,7 +66,7 @@ const VisitForm: React.FC<VisitFormProps> = ({ patientUuid, closeWorkspace }) =>
|
|
|
67
66
|
const [visitTime, setVisitTime] = useState(dayjs(new Date()).format('hh:mm'));
|
|
68
67
|
const state = useMemo(() => ({ patientUuid }), [patientUuid]);
|
|
69
68
|
const [ignoreChanges, setIgnoreChanges] = useState(true);
|
|
70
|
-
const { activePatientEnrollment
|
|
69
|
+
const { activePatientEnrollment } = useActivePatientEnrollment(patientUuid);
|
|
71
70
|
const [enrollment, setEnrollment] = useState<PatientProgram>(activePatientEnrollment[0]);
|
|
72
71
|
const { mutateQueueEntries } = useMutateQueueEntries();
|
|
73
72
|
const visitQueueNumberAttributeUuid = config.visitQueueNumberAttributeUuid;
|
|
@@ -116,56 +115,50 @@ const VisitForm: React.FC<VisitFormProps> = ({ patientUuid, closeWorkspace }) =>
|
|
|
116
115
|
|
|
117
116
|
const abortController = new AbortController();
|
|
118
117
|
|
|
119
|
-
saveVisit(payload, abortController)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
subtitle: error?.message,
|
|
156
|
-
});
|
|
157
|
-
},
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
(error) => {
|
|
118
|
+
saveVisit(payload, abortController).then((response) => {
|
|
119
|
+
// add new queue entry if visit created successfully
|
|
120
|
+
postQueueEntry(
|
|
121
|
+
response.data.uuid,
|
|
122
|
+
service,
|
|
123
|
+
patientUuid,
|
|
124
|
+
priority,
|
|
125
|
+
status,
|
|
126
|
+
sortWeight,
|
|
127
|
+
queueLocation,
|
|
128
|
+
visitQueueNumberAttributeUuid,
|
|
129
|
+
)
|
|
130
|
+
.then(() => {
|
|
131
|
+
showSnackbar({
|
|
132
|
+
kind: 'success',
|
|
133
|
+
isLowContrast: true,
|
|
134
|
+
title: t('startAVisit', 'Start a visit'),
|
|
135
|
+
subtitle: t(
|
|
136
|
+
'startVisitQueueSuccessfully',
|
|
137
|
+
'Patient has been added to active visits list and queue.',
|
|
138
|
+
`${hours} : ${minutes}`,
|
|
139
|
+
),
|
|
140
|
+
});
|
|
141
|
+
closeWorkspace();
|
|
142
|
+
setIsSubmitting(false);
|
|
143
|
+
mutateQueueEntries();
|
|
144
|
+
})
|
|
145
|
+
.catch((error) => {
|
|
146
|
+
showSnackbar({
|
|
147
|
+
title: t('queueEntryError', 'Error adding patient to the queue'),
|
|
148
|
+
kind: 'error',
|
|
149
|
+
subtitle: error?.message,
|
|
150
|
+
});
|
|
151
|
+
setIsSubmitting(false);
|
|
152
|
+
})
|
|
153
|
+
.catch((error) => {
|
|
162
154
|
showSnackbar({
|
|
163
155
|
title: t('startVisitError', 'Error starting visit'),
|
|
164
156
|
kind: 'error',
|
|
165
157
|
subtitle: error?.message,
|
|
166
158
|
});
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
setIsSubmitting(false);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
169
162
|
},
|
|
170
163
|
[
|
|
171
164
|
closeWorkspace,
|
|
@@ -181,9 +174,9 @@ const VisitForm: React.FC<VisitFormProps> = ({ patientUuid, closeWorkspace }) =>
|
|
|
181
174
|
],
|
|
182
175
|
);
|
|
183
176
|
|
|
184
|
-
const handleOnChange = () => {
|
|
177
|
+
const handleOnChange = useCallback(() => {
|
|
185
178
|
setIgnoreChanges((prevState) => !prevState);
|
|
186
|
-
};
|
|
179
|
+
}, []);
|
|
187
180
|
|
|
188
181
|
return (
|
|
189
182
|
<Form className={styles.form} onChange={handleOnChange} onSubmit={handleSubmit}>
|
|
@@ -1,31 +1,87 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
2
2
|
import { DataTableSkeleton } from '@carbon/react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { useActiveTickets } from './useActiveTickets';
|
|
5
5
|
import PatientQueueHeader from '../patient-queue-header/patient-queue-header.component';
|
|
6
6
|
import styles from './queue-screen.scss';
|
|
7
|
+
import { useSelectedQueueLocationUuid } from '../helpers/helpers';
|
|
8
|
+
import { ErrorState } from '@openmrs/esm-framework';
|
|
7
9
|
|
|
8
10
|
interface QueueScreenProps {}
|
|
9
11
|
|
|
10
12
|
const QueueScreen: React.FC<QueueScreenProps> = () => {
|
|
11
|
-
const { t } = useTranslation();
|
|
12
|
-
const { activeTickets, isLoading, error } = useActiveTickets();
|
|
13
|
+
const { t, i18n } = useTranslation();
|
|
14
|
+
const { activeTickets, isLoading, error, mutate } = useActiveTickets();
|
|
15
|
+
const [isSpeaking, setIsSpeaking] = useState(false);
|
|
16
|
+
const selectedLocation = useSelectedQueueLocationUuid();
|
|
17
|
+
|
|
18
|
+
const rowData = useMemo(
|
|
19
|
+
() =>
|
|
20
|
+
activeTickets.map((ticket, index) => ({
|
|
21
|
+
id: `${index}`,
|
|
22
|
+
room: ticket.room,
|
|
23
|
+
ticketNumber: ticket.ticketNumber,
|
|
24
|
+
status: ticket.status,
|
|
25
|
+
})),
|
|
26
|
+
[activeTickets],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const readTicket = useCallback(
|
|
30
|
+
(queue) => {
|
|
31
|
+
if ('speechSynthesis' in window) {
|
|
32
|
+
window.speechSynthesis.cancel();
|
|
33
|
+
|
|
34
|
+
const message = new SpeechSynthesisUtterance();
|
|
35
|
+
const [prefix, suffix] = queue.ticketNumber.split('-');
|
|
36
|
+
const utterance = t(
|
|
37
|
+
'ticketAnnouncement',
|
|
38
|
+
'Ticket number: {{prefix}}, - {{suffix}}, please proceed to room {{room}}',
|
|
39
|
+
{
|
|
40
|
+
prefix: prefix.split(''),
|
|
41
|
+
suffix: suffix.split(''),
|
|
42
|
+
room: queue.room,
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
message.rate = 1;
|
|
46
|
+
message.pitch = 1;
|
|
47
|
+
message.text = utterance;
|
|
48
|
+
message.lang = i18n.language;
|
|
49
|
+
|
|
50
|
+
return new Promise<void>((resolve) => {
|
|
51
|
+
message.onend = () => resolve();
|
|
52
|
+
window.speechSynthesis.speak(message);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return Promise.resolve();
|
|
56
|
+
},
|
|
57
|
+
[i18n.language, t],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const ticketsToCallOut = activeTickets.filter((item) => item.status.toLowerCase() === 'calling');
|
|
62
|
+
|
|
63
|
+
if (ticketsToCallOut.length > 0 && !isSpeaking) {
|
|
64
|
+
setIsSpeaking(true);
|
|
65
|
+
const readTickets = async () => {
|
|
66
|
+
for (const ticket of ticketsToCallOut) {
|
|
67
|
+
await readTicket(ticket);
|
|
68
|
+
}
|
|
69
|
+
setIsSpeaking(false);
|
|
70
|
+
mutate?.();
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
readTickets();
|
|
74
|
+
}
|
|
75
|
+
}, [activeTickets, isSpeaking, readTicket, mutate]);
|
|
13
76
|
|
|
14
77
|
if (isLoading) {
|
|
15
78
|
return <DataTableSkeleton row={5} className={styles.queueScreen} role="progressbar" />;
|
|
16
79
|
}
|
|
17
80
|
|
|
18
81
|
if (error) {
|
|
19
|
-
return <
|
|
82
|
+
return <ErrorState error={error} headerTitle={t('queueScreenError', 'Queue screen error')} />;
|
|
20
83
|
}
|
|
21
84
|
|
|
22
|
-
const rowData = activeTickets.map((ticket, index) => ({
|
|
23
|
-
id: `${index}}`,
|
|
24
|
-
room: ticket.room,
|
|
25
|
-
ticketNumber: ticket.ticketNumber,
|
|
26
|
-
status: ticket.status,
|
|
27
|
-
}));
|
|
28
|
-
|
|
29
85
|
return (
|
|
30
86
|
<div>
|
|
31
87
|
<PatientQueueHeader title={t('queueScreen', 'Queue screen')} showLocationDropdown />
|
|
@@ -26,7 +26,7 @@ describe('QueueScreen component', () => {
|
|
|
26
26
|
mutate: jest.fn(),
|
|
27
27
|
});
|
|
28
28
|
render(<QueueScreen />);
|
|
29
|
-
expect(screen.getByText(
|
|
29
|
+
expect(screen.getByText(/Error State/i)).toBeInTheDocument();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
test('renders table with active tickets when data is loaded', () => {
|
|
@@ -5,7 +5,7 @@ export const useActiveTickets = () => {
|
|
|
5
5
|
const { data, isLoading, error, mutate } = useSWR<{ data: Record<string, { status: string; ticketNumber: string }> }>(
|
|
6
6
|
`${restBaseUrl}/queueutil/active-tickets`,
|
|
7
7
|
openmrsFetch,
|
|
8
|
-
{ refreshInterval:
|
|
8
|
+
{ refreshInterval: 30000 },
|
|
9
9
|
);
|
|
10
10
|
const activeTickets =
|
|
11
11
|
Array.from(Object.entries(data?.data ?? {}).map(([key, value]) => ({ room: key, ...value }))) ?? [];
|
|
@@ -83,9 +83,9 @@ export function useColumns(queue: string, status: string): QueueTableColumn[] {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
function getColumnFromDefinition(t: TFunction, columnDef: ColumnDefinition): QueueTableColumn {
|
|
86
|
-
const { id, header,
|
|
86
|
+
const { id, header, columnType } = columnDef;
|
|
87
87
|
|
|
88
|
-
const translatedHeader = header ?
|
|
88
|
+
const translatedHeader = header ? t(header) : null;
|
|
89
89
|
|
|
90
90
|
switch (columnType ?? id) {
|
|
91
91
|
case 'patient-name': {
|
|
@@ -133,17 +133,18 @@ function DefaultQueueTable() {
|
|
|
133
133
|
queueUuid={null}
|
|
134
134
|
statusUuid={null}
|
|
135
135
|
ExpandedRow={QueueTableExpandedRow}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
136
|
+
tableFilters={
|
|
137
|
+
<>
|
|
138
|
+
<QueueDropdownFilter /> <StatusDropdownFilter />
|
|
139
|
+
<TableToolbarSearch
|
|
140
|
+
className={styles.search}
|
|
141
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
142
|
+
placeholder={t('searchThisList', 'Search this list')}
|
|
143
|
+
size={isDesktop(layout) ? 'sm' : 'lg'}
|
|
144
|
+
/>
|
|
145
|
+
<ClearQueueEntries queueEntries={filteredQueueEntries} />
|
|
146
|
+
</>
|
|
147
|
+
}
|
|
147
148
|
/>
|
|
148
149
|
</div>
|
|
149
150
|
) : (
|
|
@@ -160,17 +161,18 @@ function QueueDropdownFilter() {
|
|
|
160
161
|
const layout = useLayoutType();
|
|
161
162
|
const { services } = useQueueServices();
|
|
162
163
|
const selectedService = useSelectedService();
|
|
163
|
-
|
|
164
|
+
|
|
165
|
+
const handleServiceChange = useCallback(({ selectedItem }) => {
|
|
164
166
|
updateSelectedService(selectedItem.uuid, selectedItem?.display);
|
|
165
167
|
mutateQueueEntries.mutateQueueEntries();
|
|
166
|
-
};
|
|
168
|
+
}, []);
|
|
167
169
|
|
|
168
170
|
return (
|
|
169
171
|
<>
|
|
170
172
|
<div className={styles.filterContainer}>
|
|
171
173
|
<Dropdown
|
|
172
174
|
id="serviceFilter"
|
|
173
|
-
titleText={t('filterByService', 'Filter by service
|
|
175
|
+
titleText={t('filterByService', 'Filter by service:')}
|
|
174
176
|
label={selectedService?.serviceDisplay ?? t('all', 'All')}
|
|
175
177
|
type="inline"
|
|
176
178
|
items={[{ display: `${t('all', 'All')}` }, ...(services ?? [])]}
|
|
@@ -197,7 +199,7 @@ function StatusDropdownFilter() {
|
|
|
197
199
|
<div className={styles.filterContainer}>
|
|
198
200
|
<Dropdown
|
|
199
201
|
id="statusFilter"
|
|
200
|
-
titleText={t('filterByStatus', 'Filter by status
|
|
202
|
+
titleText={t('filterByStatus', 'Filter by status:')}
|
|
201
203
|
label={queueStatus?.statusDisplay ?? t('all', 'All')}
|
|
202
204
|
type="inline"
|
|
203
205
|
items={[{ display: `${t('all', 'All')}` }, ...(statuses ?? [])]}
|
|
@@ -8,16 +8,21 @@ import { convertTime12to24 } from '../../helpers/time-helpers';
|
|
|
8
8
|
interface TransitionQueueEntryModalProps {
|
|
9
9
|
queueEntry: QueueEntry;
|
|
10
10
|
closeModal: () => void;
|
|
11
|
+
modalTitle?: string;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const TransitionQueueEntryModal: React.FC<TransitionQueueEntryModalProps> = ({
|
|
14
|
+
const TransitionQueueEntryModal: React.FC<TransitionQueueEntryModalProps> = ({
|
|
15
|
+
queueEntry,
|
|
16
|
+
closeModal,
|
|
17
|
+
modalTitle,
|
|
18
|
+
}) => {
|
|
14
19
|
const { t } = useTranslation();
|
|
15
20
|
return (
|
|
16
21
|
<QueueEntryActionModal
|
|
17
22
|
queueEntry={queueEntry}
|
|
18
23
|
closeModal={closeModal}
|
|
19
24
|
modalParams={{
|
|
20
|
-
modalTitle: t('transitionPatient', 'Transition patient'),
|
|
25
|
+
modalTitle: modalTitle || t('transitionPatient', 'Transition patient'),
|
|
21
26
|
modalInstruction: t(
|
|
22
27
|
'transitionPatientStatusOrQueue',
|
|
23
28
|
'Select a new status or queue for patient to transition to.',
|
|
@@ -18,7 +18,7 @@ export default function QueueTableByStatusMenu() {
|
|
|
18
18
|
<SideNavMenu title={t('serviceQueues', 'Service queues')} className={styles.queueTableByStatusNavMenu}>
|
|
19
19
|
<BrowserRouter>
|
|
20
20
|
{queues.map((queue) => (
|
|
21
|
-
<QueueTableByStatusLink queue={queue} />
|
|
21
|
+
<QueueTableByStatusLink key={queue.uuid} queue={queue} />
|
|
22
22
|
))}
|
|
23
23
|
</BrowserRouter>
|
|
24
24
|
</SideNavMenu>
|
|
@@ -20,7 +20,12 @@ function QueueTableMetrics({ selectedQueue }: QueueTableMetricsProps) {
|
|
|
20
20
|
<QueueTableMetricsCard value={count} headerLabel={t('totalPatients', 'Total Patients')} />
|
|
21
21
|
{allowedStatuses?.map((status) => {
|
|
22
22
|
return (
|
|
23
|
-
<QueueTableMetricsCard
|
|
23
|
+
<QueueTableMetricsCard
|
|
24
|
+
headerLabel={status.display}
|
|
25
|
+
key={status.uuid}
|
|
26
|
+
queueUuid={selectedQueue.uuid}
|
|
27
|
+
status={status.uuid}
|
|
28
|
+
/>
|
|
24
29
|
);
|
|
25
30
|
})}
|
|
26
31
|
</div>
|
|
@@ -46,7 +46,7 @@ interface QueueTableProps {
|
|
|
46
46
|
ExpandedRow?: FC<{ queueEntry: QueueEntry }>;
|
|
47
47
|
|
|
48
48
|
// if provided, adds addition table toolbar elements
|
|
49
|
-
|
|
49
|
+
tableFilters?: React.ReactNode;
|
|
50
50
|
|
|
51
51
|
isLoading?: boolean;
|
|
52
52
|
}
|
|
@@ -58,7 +58,7 @@ function QueueTable({
|
|
|
58
58
|
statusUuid,
|
|
59
59
|
queueTableColumnsOverride,
|
|
60
60
|
ExpandedRow,
|
|
61
|
-
|
|
61
|
+
tableFilters,
|
|
62
62
|
isLoading,
|
|
63
63
|
}: QueueTableProps) {
|
|
64
64
|
const { t } = useTranslation();
|
|
@@ -80,7 +80,7 @@ function QueueTable({
|
|
|
80
80
|
paginatedQueueEntries?.map((queueEntry) => {
|
|
81
81
|
const row: Record<string, JSX.Element | string> = { id: queueEntry.uuid };
|
|
82
82
|
columns.forEach(({ key, CellComponent }) => {
|
|
83
|
-
row[key] = <CellComponent queueEntry={queueEntry} />;
|
|
83
|
+
row[key] = <CellComponent key={key} queueEntry={queueEntry} />;
|
|
84
84
|
});
|
|
85
85
|
return row;
|
|
86
86
|
}) ?? [];
|
|
@@ -111,9 +111,9 @@ function QueueTable({
|
|
|
111
111
|
</span>
|
|
112
112
|
) : null}
|
|
113
113
|
|
|
114
|
-
{
|
|
114
|
+
{tableFilters && (
|
|
115
115
|
<TableToolbar {...getToolbarProps()}>
|
|
116
|
-
<TableToolbarContent className={styles.toolbarContent}>{
|
|
116
|
+
<TableToolbarContent className={styles.toolbarContent}>{tableFilters}</TableToolbarContent>
|
|
117
117
|
</TableToolbar>
|
|
118
118
|
)}
|
|
119
119
|
</div>
|
|
@@ -121,8 +121,8 @@ function QueueTable({
|
|
|
121
121
|
<TableHead>
|
|
122
122
|
<TableRow>
|
|
123
123
|
{ExpandedRow && <TableExpandHeader enableToggle {...getExpandHeaderProps()} />}
|
|
124
|
-
{headers.map((header
|
|
125
|
-
<TableHeader key={
|
|
124
|
+
{headers.map((header) => (
|
|
125
|
+
<TableHeader key={header.key} {...getHeaderProps({ header })}>
|
|
126
126
|
{header.header}
|
|
127
127
|
</TableHeader>
|
|
128
128
|
))}
|
|
@@ -146,7 +146,10 @@ function QueueTable({
|
|
|
146
146
|
))}
|
|
147
147
|
</Row>
|
|
148
148
|
{ExpandedRow && row.isExpanded && (
|
|
149
|
-
<TableExpandedRow
|
|
149
|
+
<TableExpandedRow
|
|
150
|
+
key={i}
|
|
151
|
+
className={styles.expandedActiveVisitRow}
|
|
152
|
+
colSpan={headers.length + 1}>
|
|
150
153
|
<ExpandedRow queueEntry={paginatedQueueEntries[i]} />
|
|
151
154
|
</TableExpandedRow>
|
|
152
155
|
)}
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.filterContainer {
|
|
31
|
+
label {
|
|
32
|
+
margin-right: layout.$spacing-03 !important;
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
:global(.cds--dropdown__wrapper--inline) {
|
|
32
36
|
gap: 0;
|
|
33
37
|
}
|
|
@@ -97,6 +101,7 @@
|
|
|
97
101
|
.toolbarContent {
|
|
98
102
|
display: flex;
|
|
99
103
|
z-index: 1; // prevent dropdown from getting covered by table elements
|
|
104
|
+
column-gap: layout.$spacing-03;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
:global(.cds--table-toolbar) {
|