@kenyaemr/esm-service-queues-app 8.1.1-pre.114 → 8.1.1-pre.118

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 (58) hide show
  1. package/.turbo/turbo-build.log +19 -19
  2. package/dist/130.js +1 -1
  3. package/dist/130.js.map +1 -1
  4. package/dist/199.js +1 -1
  5. package/dist/199.js.map +1 -1
  6. package/dist/271.js +1 -1
  7. package/dist/319.js +1 -1
  8. package/dist/460.js +1 -1
  9. package/dist/574.js +1 -1
  10. package/dist/60.js +1 -0
  11. package/dist/60.js.map +1 -0
  12. package/dist/644.js +1 -1
  13. package/dist/665.js +2 -0
  14. package/dist/665.js.map +1 -0
  15. package/dist/670.js +1 -1
  16. package/dist/670.js.map +1 -1
  17. package/dist/748.js +1 -0
  18. package/dist/748.js.map +1 -0
  19. package/dist/757.js +1 -1
  20. package/dist/788.js +1 -1
  21. package/dist/807.js +1 -1
  22. package/dist/833.js +1 -1
  23. package/dist/kenyaemr-esm-service-queues-app.js +1 -1
  24. package/dist/kenyaemr-esm-service-queues-app.js.buildmanifest.json +99 -75
  25. package/dist/kenyaemr-esm-service-queues-app.js.map +1 -1
  26. package/dist/main.js +1 -1
  27. package/dist/main.js.map +1 -1
  28. package/dist/routes.json +1 -1
  29. package/package.json +1 -1
  30. package/src/active-visits/change-status-dialog.component.tsx +141 -138
  31. package/src/active-visits/change-status-dialog.test.tsx +5 -5
  32. package/src/config-schema.ts +4 -10
  33. package/src/index.ts +7 -0
  34. package/src/patient-queue-header/patient-queue-header.component.tsx +25 -38
  35. package/src/patient-queue-header/patient-queue-header.scss +3 -41
  36. package/src/queue-screen/queue-screen.component.tsx +67 -11
  37. package/src/queue-screen/queue-screen.test.tsx +1 -1
  38. package/src/queue-screen/useActiveTickets.tsx +1 -1
  39. package/src/queue-table/cells/columns.resource.ts +2 -2
  40. package/src/queue-table/queue-entry-actions/transition-queue-entry.modal.tsx +7 -2
  41. package/src/routes.json +4 -0
  42. package/src/transition-latest-queue-entry/transition-latest-queue-entry.component.tsx +33 -0
  43. package/src/transition-latest-queue-entry/transition-latest-queue-entry.resource.ts +30 -0
  44. package/translations/am.json +4 -3
  45. package/translations/ar.json +5 -4
  46. package/translations/en.json +7 -4
  47. package/translations/es.json +81 -80
  48. package/translations/fr.json +255 -254
  49. package/translations/he.json +5 -4
  50. package/translations/km.json +5 -4
  51. package/translations/zh.json +5 -4
  52. package/translations/zh_CN.json +5 -4
  53. package/dist/265.js +0 -1
  54. package/dist/265.js.map +0 -1
  55. package/dist/62.js +0 -2
  56. package/dist/62.js.map +0 -1
  57. package/src/patient-queue-header/patient-queue-illustration.component.tsx +0 -22
  58. /package/dist/{62.js.LICENSE.txt → 665.js.LICENSE.txt} +0 -0
@@ -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': {
@@ -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.',
package/src/routes.json CHANGED
@@ -77,6 +77,10 @@
77
77
  "name": "transition-queue-entry-modal",
78
78
  "component": "transitionQueueEntryModal"
79
79
  },
80
+ {
81
+ "name": "transition-patient-to-latest-queue-modal",
82
+ "component": "transitionPatientToLatestQueue"
83
+ },
80
84
  {
81
85
  "name": "edit-queue-entry-modal",
82
86
  "component": "editQueueEntryModal"
@@ -0,0 +1,33 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { useLatestQueueEntry } from './transition-latest-queue-entry.resource';
4
+ import TransitionQueueEntryModal from '../queue-table/queue-entry-actions/transition-queue-entry.modal';
5
+
6
+ interface TransitionLatestQueueEntryProps {
7
+ patientUuid: string;
8
+ closeModal: () => void;
9
+ modalTitle?: string;
10
+ }
11
+
12
+ const TransitionLatestQueueEntry: React.FC<TransitionLatestQueueEntryProps> = ({
13
+ closeModal,
14
+ patientUuid,
15
+ modalTitle,
16
+ }) => {
17
+ const { t } = useTranslation();
18
+ const { data: queueEntry, error, isLoading } = useLatestQueueEntry(patientUuid);
19
+
20
+ if (error || !queueEntry) {
21
+ return null;
22
+ }
23
+
24
+ return (
25
+ <TransitionQueueEntryModal
26
+ queueEntry={queueEntry}
27
+ closeModal={closeModal}
28
+ modalTitle={t('TransitionLatestQueueEntry', "Transition patient's latest queue")}
29
+ />
30
+ );
31
+ };
32
+
33
+ export default TransitionLatestQueueEntry;
@@ -0,0 +1,30 @@
1
+ import {
2
+ type FetchResponse,
3
+ openmrsFetch,
4
+ type OpenmrsResource,
5
+ type Patient,
6
+ type Visit,
7
+ } from '@openmrs/esm-framework';
8
+ import useSWR from 'swr';
9
+ import useSWRImmutable from 'swr/immutable';
10
+ import { type QueueEntry } from '../types';
11
+ import { FetcherResponse } from 'swr/_internal';
12
+
13
+ export function useLatestQueueEntry(patientUuid: string) {
14
+ const customRepresentation =
15
+ 'custom:(uuid,display,queue:(uuid,display,name,location:(uuid,display),service:(uuid,display),allowedPriorities:(uuid,display),allowedStatuses:(uuid,display)),status,patient:(uuid,display),visit:(uuid,display,startDatetime),priority,priorityComment,sortWeight,startedAt,endedAt,locationWaitingFor,queueComingFrom,providerWaitingFor,previousQueueEntry)';
16
+
17
+ const encodedRepresentation = encodeURIComponent(customRepresentation);
18
+ const url = `/ws/rest/v1/queue-entry?v=${encodedRepresentation}&patient=${patientUuid}&isEnded=false`;
19
+ const { data, error, isLoading, mutate } = useSWR<FetchResponse<{ results: QueueEntry[] }>>(url, openmrsFetch);
20
+
21
+ const queueEntry =
22
+ data?.data?.results?.reduce((latestEntry, currentEntry) => {
23
+ if (!latestEntry || new Date(currentEntry.startedAt) > new Date(latestEntry.startedAt)) {
24
+ return currentEntry;
25
+ }
26
+ return latestEntry;
27
+ }, null) || null;
28
+
29
+ return { data: queueEntry, error, isLoading, mutate };
30
+ }
@@ -93,7 +93,6 @@
93
93
  "gender": "Gender",
94
94
  "heartRate": "Heart rate",
95
95
  "height": "Height",
96
- "home": "Home",
97
96
  "hourAndMinuteFormatted": "{{hours}} hour(s) and {{minutes}} minute(s)",
98
97
  "idNumber": "ID Number",
99
98
  "indication": "Indication",
@@ -162,6 +161,7 @@
162
161
  "patientAlreadyInQueue": "Patient is already in the queue",
163
162
  "patientAttendingService": "Patient attending service",
164
163
  "patientHasActiveVisit": "The patient already has an active visit",
164
+ "patientInfo": "{{name}}{{sexInfo}}{{ageInfo}}",
165
165
  "patientList": "Patient list",
166
166
  "patientName": "Patient name",
167
167
  "patientNotInQueue": "The patient is not in the queue",
@@ -204,7 +204,7 @@
204
204
  "queueEntryTransitionUndoSuccessful": "Queue entry transition undo success",
205
205
  "queueEntryUpdateFailed": "Error updating queue entry",
206
206
  "queueEntryUpdateSuccessfully": "Queue Entry Updated Successfully",
207
- "queueLocation": "Queue Location",
207
+ "queueLocation": "Queue location",
208
208
  "queueLocationRequired": "Queue location is required",
209
209
  "queueName": "Queue name",
210
210
  "queuePriority": "Queue priority",
@@ -255,7 +255,7 @@
255
255
  "serve": "Serve",
256
256
  "servePatient": "Serve patient",
257
257
  "service": "Service",
258
- "serviceIsRequired": "Status is required",
258
+ "serviceIsRequired": "Service is required",
259
259
  "serviceQueue": "Service queue",
260
260
  "serviceQueues": "Service queues",
261
261
  "sex": "Sex",
@@ -281,6 +281,7 @@
281
281
  "today": "Today",
282
282
  "totalPatients": "Total Patients",
283
283
  "transition": "Transition",
284
+ "TransitionLatestQueueEntry": "Transition patient to latest queue",
284
285
  "transitionPatient": "Transition patient",
285
286
  "transitionPatientStatusOrQueue": "Select a new status or queue for patient to transition to.",
286
287
  "triageForm": "Triage form",
@@ -93,7 +93,6 @@
93
93
  "gender": "الجنس",
94
94
  "heartRate": "معدل ضربات القلب",
95
95
  "height": "الطول",
96
- "home": "المنزل",
97
96
  "hourAndMinuteFormatted": "{{hours}} hour(s) and {{minutes}} minute(s)",
98
97
  "idNumber": "رقم الهوية",
99
98
  "indication": "الدلالة",
@@ -162,6 +161,7 @@
162
161
  "patientAlreadyInQueue": "Patient is already in the queue",
163
162
  "patientAttendingService": "المريض الذي يحضر الخدمة",
164
163
  "patientHasActiveVisit": "لدى المريض زيارة نشطة بالفعل",
164
+ "patientInfo": "{{name}}{{sexInfo}}{{ageInfo}}",
165
165
  "patientList": "قائمة المرضى",
166
166
  "patientName": "اسم المريض",
167
167
  "patientNotInQueue": "المريض ليس في الطابور",
@@ -204,7 +204,7 @@
204
204
  "queueEntryTransitionUndoSuccessful": "Queue entry transition undo success",
205
205
  "queueEntryUpdateFailed": "خطأ في تحديث دخول الطابور",
206
206
  "queueEntryUpdateSuccessfully": "تم تحديث دخول الطابور بنجاح",
207
- "queueLocation": "موقع الطابور",
207
+ "queueLocation": "Queue location",
208
208
  "queueLocationRequired": "Queue location is required",
209
209
  "queueName": "اسم الطابور",
210
210
  "queuePriority": "أولوية الطابور",
@@ -213,7 +213,7 @@
213
213
  "queueRoomAddFailed": "خطأ في إضافة غرفة الانتظار",
214
214
  "queueRoomName": "اسم غرفة الانتظار",
215
215
  "queueRoomUpdatedSuccessfully": "تم تحديث غرفة الانتظار بنجاح",
216
- "queuesClearedSuccessfully": "تم مسح الطوابير بنجاح",
216
+ "queuesClearedSuccessfully": "Queues cleared successfully",
217
217
  "queueScreen": "شاشة الطابور",
218
218
  "queueService": "خدمة الطابور",
219
219
  "queueStatus": "حالة الطابور",
@@ -255,7 +255,7 @@
255
255
  "serve": "خدمة",
256
256
  "servePatient": "خدمة المريض",
257
257
  "service": "خدمة",
258
- "serviceIsRequired": "Status is required",
258
+ "serviceIsRequired": "Service is required",
259
259
  "serviceQueue": "طابور الخدمة",
260
260
  "serviceQueues": "طوابير الخدمة",
261
261
  "sex": "الجنس",
@@ -281,6 +281,7 @@
281
281
  "today": "اليوم",
282
282
  "totalPatients": "Total Patients",
283
283
  "transition": "Transition",
284
+ "TransitionLatestQueueEntry": "Transition patient to latest queue",
284
285
  "transitionPatient": "Transition patient",
285
286
  "transitionPatientStatusOrQueue": "Select a new status or queue for patient to transition to.",
286
287
  "triageForm": "نموذج الترياج",
@@ -93,7 +93,6 @@
93
93
  "gender": "Gender",
94
94
  "heartRate": "Heart rate",
95
95
  "height": "Height",
96
- "home": "Home",
97
96
  "hourAndMinuteFormatted": "{{hours}} hour(s) and {{minutes}} minute(s)",
98
97
  "idNumber": "ID Number",
99
98
  "indication": "Indication",
@@ -162,6 +161,7 @@
162
161
  "patientAlreadyInQueue": "Patient is already in the queue",
163
162
  "patientAttendingService": "Patient attending service",
164
163
  "patientHasActiveVisit": "The patient already has an active visit",
164
+ "patientInfo": "{{name}}{{sexInfo}}{{ageInfo}}",
165
165
  "patientList": "Patient list",
166
166
  "patientName": "Patient name",
167
167
  "patientNotInQueue": "The patient is not in the queue",
@@ -204,7 +204,7 @@
204
204
  "queueEntryTransitionUndoSuccessful": "Queue entry transition undo success",
205
205
  "queueEntryUpdateFailed": "Error updating queue entry status",
206
206
  "queueEntryUpdateSuccessfully": "Queue Entry Updated Successfully",
207
- "queueLocation": "Queue Location",
207
+ "queueLocation": "Queue location",
208
208
  "queueLocationRequired": "Queue location is required",
209
209
  "queueName": "Queue name",
210
210
  "queuePriority": "Queue priority",
@@ -213,8 +213,9 @@
213
213
  "queueRoomAddFailed": "Error adding queue room",
214
214
  "queueRoomName": "Queue room name",
215
215
  "queueRoomUpdatedSuccessfully": "Queue room updated successfully",
216
- "queuesClearedSuccessfully": "Queues Cleared Successfully",
216
+ "queuesClearedSuccessfully": "Queues cleared successfully",
217
217
  "queueScreen": "Queue screen",
218
+ "queueScreenError": "Queue screen error",
218
219
  "queueService": "Queue service",
219
220
  "queueStatus": "Queue status",
220
221
  "recentScheduledVisits_one": "{{count}} visit scheduled for +/- 7 days",
@@ -255,7 +256,7 @@
255
256
  "serve": "Serve",
256
257
  "servePatient": "Serve patient",
257
258
  "service": "Service",
258
- "serviceIsRequired": "Status is required",
259
+ "serviceIsRequired": "Service is required",
259
260
  "serviceQueue": "Service queue",
260
261
  "serviceQueues": "Service queues",
261
262
  "sex": "Sex",
@@ -272,6 +273,7 @@
272
273
  "submitting": "Submitting...",
273
274
  "success": "Success",
274
275
  "temperature": "Temperature",
276
+ "ticketAnnouncement": "Ticket number: {{prefix}}, - {{suffix}}, please proceed to room {{room}}",
275
277
  "ticketNumber": "Ticket Number",
276
278
  "time": "Time",
277
279
  "timeCannotBeInFuture": "Time cannot be in the future",
@@ -281,6 +283,7 @@
281
283
  "today": "Today",
282
284
  "totalPatients": "Total Patients",
283
285
  "transition": "Transition",
286
+ "TransitionLatestQueueEntry": "Transition patient to latest queue",
284
287
  "transitionPatient": "Transition patient",
285
288
  "transitionPatientStatusOrQueue": "Select a new status or queue for patient to transition to.",
286
289
  "triageForm": "Triage form",