@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.
Files changed (92) hide show
  1. package/.turbo/turbo-build.log +21 -22
  2. package/dist/130.js +1 -1
  3. package/dist/130.js.LICENSE.txt +2 -0
  4. package/dist/130.js.map +1 -1
  5. package/dist/199.js +1 -1
  6. package/dist/199.js.map +1 -1
  7. package/dist/2.js +1 -0
  8. package/dist/2.js.map +1 -0
  9. package/dist/271.js +1 -1
  10. package/dist/319.js +1 -1
  11. package/dist/325.js +1 -0
  12. package/dist/325.js.map +1 -0
  13. package/dist/372.js +2 -0
  14. package/dist/372.js.map +1 -0
  15. package/dist/392.js +1 -1
  16. package/dist/460.js +1 -1
  17. package/dist/574.js +1 -1
  18. package/dist/60.js +1 -0
  19. package/dist/60.js.map +1 -0
  20. package/dist/644.js +1 -1
  21. package/dist/660.js +2 -0
  22. package/dist/{484.js.LICENSE.txt → 660.js.LICENSE.txt} +10 -0
  23. package/dist/660.js.map +1 -0
  24. package/dist/670.js +1 -1
  25. package/dist/670.js.map +1 -1
  26. package/dist/727.js +1 -1
  27. package/dist/748.js +1 -0
  28. package/dist/748.js.map +1 -0
  29. package/dist/757.js +1 -1
  30. package/dist/760.js +1 -1
  31. package/dist/760.js.map +1 -1
  32. package/dist/788.js +1 -1
  33. package/dist/807.js +1 -1
  34. package/dist/818.js +1 -1
  35. package/dist/833.js +1 -1
  36. package/dist/911.js +1 -1
  37. package/dist/kenyaemr-esm-service-queues-app.js +1 -1
  38. package/dist/kenyaemr-esm-service-queues-app.js.buildmanifest.json +171 -174
  39. package/dist/kenyaemr-esm-service-queues-app.js.map +1 -1
  40. package/dist/main.js +1 -1
  41. package/dist/main.js.map +1 -1
  42. package/dist/routes.json +1 -1
  43. package/package-lock.json +5892 -0
  44. package/package.json +2 -2
  45. package/src/active-visits/change-status-dialog.component.tsx +141 -138
  46. package/src/active-visits/change-status-dialog.test.tsx +5 -5
  47. package/src/config-schema.ts +4 -10
  48. package/src/index.ts +7 -0
  49. package/src/patient-queue-header/patient-queue-header.component.tsx +25 -38
  50. package/src/patient-queue-header/patient-queue-header.scss +3 -41
  51. package/src/patient-queue-metrics/clinic-metrics.component.tsx +4 -4
  52. package/src/patient-queue-metrics/metrics-header.component.tsx +16 -15
  53. package/src/patient-search/patient-scheduled-visits.component.tsx +43 -52
  54. package/src/patient-search/visit-form/visit-form.component.tsx +43 -50
  55. package/src/queue-screen/queue-screen.component.tsx +67 -11
  56. package/src/queue-screen/queue-screen.test.tsx +1 -1
  57. package/src/queue-screen/useActiveTickets.tsx +1 -1
  58. package/src/queue-table/cells/columns.resource.ts +2 -2
  59. package/src/queue-table/default-queue-table.component.tsx +17 -15
  60. package/src/queue-table/queue-entry-actions/transition-queue-entry.modal.tsx +7 -2
  61. package/src/queue-table/queue-table-by-status-menu.component.tsx +1 -1
  62. package/src/queue-table/queue-table-metrics.component.tsx +6 -1
  63. package/src/queue-table/queue-table.component.tsx +11 -8
  64. package/src/queue-table/queue-table.scss +5 -0
  65. package/src/remove-queue-entry-dialog/remove-queue-entry.component.tsx +16 -15
  66. package/src/remove-queue-entry-dialog/remove-queue-entry.resource.ts +20 -26
  67. package/src/routes.json +34 -27
  68. package/src/transition-latest-queue-entry/transition-latest-queue-entry.component.tsx +33 -0
  69. package/src/transition-latest-queue-entry/transition-latest-queue-entry.resource.ts +30 -0
  70. package/translations/am.json +4 -3
  71. package/translations/ar.json +5 -4
  72. package/translations/en.json +9 -6
  73. package/translations/es.json +81 -80
  74. package/translations/fr.json +255 -254
  75. package/translations/he.json +5 -4
  76. package/translations/km.json +5 -4
  77. package/translations/zh.json +5 -4
  78. package/translations/zh_CN.json +5 -4
  79. package/dist/152.js +0 -1
  80. package/dist/152.js.map +0 -1
  81. package/dist/255.js +0 -2
  82. package/dist/255.js.map +0 -1
  83. package/dist/265.js +0 -1
  84. package/dist/265.js.map +0 -1
  85. package/dist/303.js +0 -1
  86. package/dist/303.js.map +0 -1
  87. package/dist/484.js +0 -2
  88. package/dist/484.js.map +0 -1
  89. package/dist/729.js +0 -1
  90. package/dist/729.js.map +0 -1
  91. package/src/patient-queue-header/patient-queue-illustration.component.tsx +0 -22
  92. /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 navigateToQueueScreen = () => {
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
- .pipe(first())
117
- .subscribe(
118
- (response) => {
119
- if (response.status === 201) {
120
- postQueueEntry(
121
- response.data.uuid,
122
- patientId,
123
- priority,
124
- defaultStatus,
125
- service,
126
- appointment,
127
- selectedQueueLocation,
128
- visitQueueNumberAttributeUuid,
129
- ).then(
130
- ({ status }) => {
131
- if (status === 201) {
132
- showSnackbar({
133
- kind: 'success',
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
- },
146
- (error) => {
147
- showSnackbar({
148
- title: t('queueEntryError', 'Error adding patient to the queue'),
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
- setIsSubmitting(false);
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, { useEffect, useState, useMemo, useCallback, useRef } from '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, isLoading } = useActivePatientEnrollment(patientUuid);
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
- .pipe(first())
121
- .subscribe(
122
- (response) => {
123
- if (response.status === 201) {
124
- // add new queue entry if visit created successfully
125
- postQueueEntry(
126
- response.data.uuid,
127
- service,
128
- patientUuid,
129
- priority,
130
- status,
131
- sortWeight,
132
- queueLocation,
133
- visitQueueNumberAttributeUuid,
134
- ).then(
135
- ({ status }) => {
136
- if (status === 201) {
137
- showSnackbar({
138
- kind: 'success',
139
- isLowContrast: true,
140
- title: t('startAVisit', 'Start a visit'),
141
- subtitle: t(
142
- 'startVisitQueueSuccessfully',
143
- 'Patient has been added to active visits list and queue.',
144
- `${hours} : ${minutes}`,
145
- ),
146
- });
147
- closeWorkspace();
148
- mutateQueueEntries();
149
- }
150
- },
151
- (error) => {
152
- showSnackbar({
153
- title: t('queueEntryError', 'Error adding patient to the queue'),
154
- kind: 'error',
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 <div>Error</div>;
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('Error')).toBeInTheDocument();
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: 3000 },
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, headerI18nModule, columnType } = columnDef;
86
+ const { id, header, columnType } = columnDef;
87
87
 
88
- const translatedHeader = header ? translateFrom(headerI18nModule ?? '@openmrs/esm-service-queues-app', header) : null;
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
- tableFilter={[
137
- <QueueDropdownFilter />,
138
- <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
- ]}
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
- const handleServiceChange = ({ selectedItem }) => {
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> = ({ queueEntry, closeModal }) => {
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 queueUuid={selectedQueue.uuid} status={status.uuid} headerLabel={status.display} />
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
- tableFilter?: React.ReactNode[];
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
- tableFilter,
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
- {tableFilter && (
114
+ {tableFilters && (
115
115
  <TableToolbar {...getToolbarProps()}>
116
- <TableToolbarContent className={styles.toolbarContent}>{tableFilter}</TableToolbarContent>
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, i) => (
125
- <TableHeader key={i} {...getHeaderProps({ header })}>
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 className={styles.expandedActiveVisitRow} colSpan={headers.length + 1}>
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) {