@kenyaemr/esm-express-workflow-app 5.4.3 → 5.4.4-pre.10
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 +4 -4
- package/dist/971.js +1 -0
- package/dist/971.js.map +1 -0
- package/dist/kenyaemr-esm-express-workflow-app.js +3 -3
- package/dist/kenyaemr-esm-express-workflow-app.js.buildmanifest.json +29 -29
- package/dist/kenyaemr-esm-express-workflow-app.js.map +1 -1
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +3 -2
- package/src/components/consultation/consultation.component.tsx +41 -177
- package/src/components/consultation/consultation.resource.ts +17 -7
- package/src/components/consultation/consultation.utils.tsx +222 -0
- package/src/components/mch/mch.consultation.component.tsx +27 -15
- package/src/components/mch/mch.triage.component.tsx +42 -33
- package/src/components/triage/triage.component.tsx +35 -18
- package/src/components/triage/triage.resource.ts +56 -50
- package/src/hooks/useServiceQueues.tsx +93 -25
- package/src/shared/queue/queue-entry/queue-entry-table.component.tsx +73 -81
- package/src/shared/queue/queue-summary-cards.component.tsx +32 -0
- package/src/shared/queue/queue-tab.component.tsx +35 -40
- package/src/shared/queue/queue-tab.scss +1 -0
- package/src/types/index.ts +16 -2
- package/dist/24.js +0 -1
- package/dist/24.js.map +0 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
1
2
|
import { IconButton, InlineLoading } from '@carbon/react';
|
|
2
3
|
import { Renew } from '@carbon/react/icons';
|
|
3
4
|
import { useConfig } from '@openmrs/esm-framework';
|
|
4
5
|
import { EmptyState } from '@openmrs/esm-patient-common-lib';
|
|
5
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
7
8
|
import { ExpressWorkflowConfig } from '../../config-schema';
|
|
8
9
|
import { useQueues } from '../../hooks/useServiceQueues';
|
|
9
10
|
import QueueTab from '../../shared/queue/queue-tab.component';
|
|
11
|
+
import QueueSummaryCards, { QueueSummaryCard } from '../../shared/queue/queue-summary-cards.component';
|
|
10
12
|
import { Queue, QueueFilter } from '../../types';
|
|
11
13
|
import {
|
|
12
14
|
useConsultationQueueMetrics,
|
|
@@ -17,7 +19,7 @@ import styles from '../consultation/consultation.scss';
|
|
|
17
19
|
|
|
18
20
|
const MCHConsultation: React.FC = () => {
|
|
19
21
|
const { t } = useTranslation();
|
|
20
|
-
const { queues, isLoading
|
|
22
|
+
const { queues, isLoading: isLoadingQueues } = useQueues();
|
|
21
23
|
const [currQueue, setCurrQueue] = useState<Queue>();
|
|
22
24
|
const [filters, setFilters] = useState<Array<QueueFilter>>([]);
|
|
23
25
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
@@ -26,7 +28,12 @@ const MCHConsultation: React.FC = () => {
|
|
|
26
28
|
priorities: { emergencyPriorityConceptUuid, urgentPriorityConceptUuid, notUrgentPriorityConceptUuid },
|
|
27
29
|
queueServiceConceptUuids,
|
|
28
30
|
} = useConfig<ExpressWorkflowConfig>();
|
|
29
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
data: totalVisits,
|
|
33
|
+
isLoading: isLoadingTotalVisits,
|
|
34
|
+
isValidating: isValidatingTotalVisits,
|
|
35
|
+
} = useTotalVisits();
|
|
36
|
+
|
|
30
37
|
const consultationQueues = queues.filter(
|
|
31
38
|
(queue) =>
|
|
32
39
|
queue.service.uuid === queueServiceConceptUuids.consultationService &&
|
|
@@ -38,21 +45,21 @@ const MCHConsultation: React.FC = () => {
|
|
|
38
45
|
const {
|
|
39
46
|
waitingEntries,
|
|
40
47
|
isLoading: isLoadingQueueMetrics,
|
|
41
|
-
error: waitingError,
|
|
42
48
|
emergencyEntries,
|
|
43
49
|
urgentEntries,
|
|
44
50
|
notUrgentEntries,
|
|
51
|
+
isValidating: isValidatingQueueMetrics,
|
|
45
52
|
} = useConsultationQueueMetrics(activeQueue);
|
|
46
53
|
const {
|
|
47
54
|
awaitingCount,
|
|
48
55
|
completedCount,
|
|
49
|
-
totalCount,
|
|
50
56
|
lab,
|
|
51
57
|
radiology,
|
|
52
58
|
procedures,
|
|
53
59
|
isLoading: isLoadingInvestigations,
|
|
54
60
|
refresh: refreshInvestigations,
|
|
55
61
|
investigationCategorizedEntries,
|
|
62
|
+
isValidating: isValidatingInvestigations,
|
|
56
63
|
} = useInvestigationStats(activeQueue);
|
|
57
64
|
// Single refresh handler for all investigations
|
|
58
65
|
const handleRefresh = useCallback(async () => {
|
|
@@ -66,7 +73,7 @@ const MCHConsultation: React.FC = () => {
|
|
|
66
73
|
}
|
|
67
74
|
}, [refreshInvestigations]);
|
|
68
75
|
|
|
69
|
-
const cards = useMemo(
|
|
76
|
+
const cards: Array<QueueSummaryCard> = useMemo(
|
|
70
77
|
() => [
|
|
71
78
|
{
|
|
72
79
|
title: t('awaitingConsultation', 'Awaiting consultation'),
|
|
@@ -258,7 +265,10 @@ const MCHConsultation: React.FC = () => {
|
|
|
258
265
|
],
|
|
259
266
|
);
|
|
260
267
|
|
|
261
|
-
|
|
268
|
+
const isLoading = isLoadingQueues || isLoadingTotalVisits || isLoadingInvestigations || isLoadingQueueMetrics;
|
|
269
|
+
const isValidating = isValidatingTotalVisits || isValidatingInvestigations || isValidatingQueueMetrics;
|
|
270
|
+
|
|
271
|
+
if (isLoading && !isValidating) {
|
|
262
272
|
return <InlineLoading description={t('loadingQueues', 'Loading queues...')} />;
|
|
263
273
|
}
|
|
264
274
|
|
|
@@ -271,14 +281,16 @@ const MCHConsultation: React.FC = () => {
|
|
|
271
281
|
);
|
|
272
282
|
}
|
|
273
283
|
return (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
284
|
+
<>
|
|
285
|
+
<QueueSummaryCards cards={cards} />
|
|
286
|
+
<QueueTab
|
|
287
|
+
queues={consultationQueues}
|
|
288
|
+
navigatePath="mch"
|
|
289
|
+
usePatientChart
|
|
290
|
+
filters={filters}
|
|
291
|
+
onFiltersChanged={setFilters}
|
|
292
|
+
/>
|
|
293
|
+
</>
|
|
282
294
|
);
|
|
283
295
|
};
|
|
284
296
|
|
|
@@ -5,13 +5,14 @@ import { ErrorState, useConfig } from '@openmrs/esm-framework';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { useQueues } from '../../hooks/useServiceQueues';
|
|
7
7
|
import QueueTab from '../../shared/queue/queue-tab.component';
|
|
8
|
+
import QueueSummaryCards, { QueueSummaryCard } from '../../shared/queue/queue-summary-cards.component';
|
|
8
9
|
import { Queue, QueueFilter } from '../../types';
|
|
9
10
|
import { useTriageQueuesMetrics } from '../triage/triage.resource';
|
|
10
11
|
import { ExpressWorkflowConfig } from '../../config-schema';
|
|
11
12
|
|
|
12
13
|
const MCHTriage: React.FC = () => {
|
|
13
14
|
const { t } = useTranslation();
|
|
14
|
-
const { queues, isLoading, error } = useQueues();
|
|
15
|
+
const { queues, isLoading: isLoadingQueues, error } = useQueues();
|
|
15
16
|
const [currQueue, setCurrQueue] = useState<Queue>();
|
|
16
17
|
const [filters, setFilters] = useState<Array<QueueFilter>>([]);
|
|
17
18
|
const {
|
|
@@ -31,11 +32,15 @@ const MCHTriage: React.FC = () => {
|
|
|
31
32
|
const {
|
|
32
33
|
error: metricsError,
|
|
33
34
|
isLoading: isLoadingMetrics,
|
|
34
|
-
|
|
35
|
+
attendedToEntries,
|
|
35
36
|
waitingEntries,
|
|
37
|
+
isValidating: isValidatingMetrics,
|
|
36
38
|
} = useTriageQueuesMetrics(activeQueue);
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
const isLoading = isLoadingQueues || isLoadingMetrics;
|
|
41
|
+
const isValidating = isValidatingMetrics;
|
|
42
|
+
|
|
43
|
+
if (isLoading && !isValidating) {
|
|
39
44
|
return <InlineLoading description={t('loadingQueues', 'Loading queues...')} />;
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -43,37 +48,41 @@ const MCHTriage: React.FC = () => {
|
|
|
43
48
|
return <ErrorState error={error ?? metricsError} headerTitle={t('errorLoadingQueues', 'Error loading queues')} />;
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
const cards: Array<QueueSummaryCard> = [
|
|
52
|
+
{
|
|
53
|
+
title: t('patientsAwaiting', 'Patient awaiting'),
|
|
54
|
+
value: waitingEntries?.length?.toString(),
|
|
55
|
+
onClick: () => {
|
|
56
|
+
setFilters((prevFilters) => [
|
|
57
|
+
...prevFilters.filter((f) => f.key !== 'status'),
|
|
58
|
+
{ key: 'status', value: waitingStatus, label: t('waiting', 'Waiting') },
|
|
59
|
+
]);
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: t('patientAttended', 'Patient attended to'),
|
|
64
|
+
value: attendedToEntries?.length?.toString(),
|
|
65
|
+
onClick: () => {
|
|
66
|
+
setFilters((prevFilters) => [
|
|
67
|
+
...prevFilters.filter((f) => f.key !== 'status'),
|
|
68
|
+
{ key: 'status', value: `${finishedStatus},${inServiceStatus}`, label: t('attendedTo', 'Attended to') },
|
|
69
|
+
]);
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
46
74
|
return (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
]);
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
title: t('patientAttended', 'Patient attended to'),
|
|
63
|
-
value: attendedtoEntries?.length?.toString(),
|
|
64
|
-
onClick: () => {
|
|
65
|
-
setFilters((prevFilters) => [
|
|
66
|
-
...prevFilters.filter((f) => f.key !== 'status'),
|
|
67
|
-
{ key: 'status', value: `${finishedStatus},${inServiceStatus}`, label: t('attendedTo', 'Attended to') },
|
|
68
|
-
]);
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
]}
|
|
72
|
-
onTabChanged={setCurrQueue}
|
|
73
|
-
usePatientChart
|
|
74
|
-
filters={filters}
|
|
75
|
-
onFiltersChanged={setFilters}
|
|
76
|
-
/>
|
|
75
|
+
<>
|
|
76
|
+
<QueueSummaryCards cards={cards} />
|
|
77
|
+
<QueueTab
|
|
78
|
+
queues={triageQueues}
|
|
79
|
+
navigatePath="mch"
|
|
80
|
+
onTabChanged={setCurrQueue}
|
|
81
|
+
usePatientChart
|
|
82
|
+
filters={filters}
|
|
83
|
+
onFiltersChanged={setFilters}
|
|
84
|
+
/>
|
|
85
|
+
</>
|
|
77
86
|
);
|
|
78
87
|
};
|
|
79
88
|
|
|
@@ -13,6 +13,7 @@ import { InlineLoading } from '@carbon/react';
|
|
|
13
13
|
|
|
14
14
|
import { useQueues } from '../../hooks/useServiceQueues';
|
|
15
15
|
import QueueTab from '../../shared/queue/queue-tab.component';
|
|
16
|
+
import QueueSummaryCards, { QueueSummaryCard } from '../../shared/queue/queue-summary-cards.component';
|
|
16
17
|
import styles from './triage.scss';
|
|
17
18
|
import { Queue, QueueFilter } from '../../types';
|
|
18
19
|
import { useTriageQueuesMetrics } from './triage.resource';
|
|
@@ -38,13 +39,19 @@ export default Triage;
|
|
|
38
39
|
|
|
39
40
|
const TriageQueueTab: React.FC = () => {
|
|
40
41
|
const { t } = useTranslation();
|
|
41
|
-
const [currQueue, setCurrQueue] = useState<Queue>();
|
|
42
|
-
const { queues, isLoading, error } = useQueues();
|
|
43
|
-
const [filters, setFilters] = useState<Array<QueueFilter>>([]);
|
|
44
42
|
const {
|
|
45
43
|
queueStatusConceptUuids: { finishedStatus, waitingStatus, inServiceStatus },
|
|
46
44
|
queueServiceConceptUuids,
|
|
47
45
|
} = useConfig<ExpressWorkflowConfig>();
|
|
46
|
+
const [currQueue, setCurrQueue] = useState<Queue>();
|
|
47
|
+
const [filters, setFilters] = useState<Array<QueueFilter>>([]);
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
queues,
|
|
51
|
+
isLoading: isLoadingQueues,
|
|
52
|
+
error: errorLoadingQueues,
|
|
53
|
+
isValidating: isValidatingQueues,
|
|
54
|
+
} = useQueues();
|
|
48
55
|
|
|
49
56
|
const triageQueues = queues
|
|
50
57
|
.filter(
|
|
@@ -60,17 +67,25 @@ const TriageQueueTab: React.FC = () => {
|
|
|
60
67
|
error: metricsError,
|
|
61
68
|
isLoading: isLoadingMetrics,
|
|
62
69
|
waitingEntries,
|
|
63
|
-
|
|
70
|
+
attendedToEntries,
|
|
71
|
+
isValidating: isValidatingMetrics,
|
|
64
72
|
} = useTriageQueuesMetrics(activeQueue);
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
const isLoading = (isLoadingQueues || isLoadingMetrics) && !isValidatingQueues && !isValidatingMetrics;
|
|
75
|
+
|
|
76
|
+
if (isLoading) {
|
|
67
77
|
return <InlineLoading description={t('loadingQueues', 'Loading queues...')} />;
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
if (metricsError) {
|
|
71
|
-
return
|
|
80
|
+
if (metricsError || errorLoadingQueues) {
|
|
81
|
+
return (
|
|
82
|
+
<ErrorState
|
|
83
|
+
error={metricsError ?? errorLoadingQueues}
|
|
84
|
+
headerTitle={t('errorLoadingQueues', 'Error loading queues')}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
72
87
|
}
|
|
73
|
-
const cards = [
|
|
88
|
+
const cards: Array<QueueSummaryCard> = [
|
|
74
89
|
{
|
|
75
90
|
title: t('clientsPatientsWaiting', 'Clients/Patients waiting'),
|
|
76
91
|
value: waitingEntries.length.toString(),
|
|
@@ -83,7 +98,7 @@ const TriageQueueTab: React.FC = () => {
|
|
|
83
98
|
},
|
|
84
99
|
{
|
|
85
100
|
title: t('clientsPatientsAttendedTo', 'Clients/Patients attended to'),
|
|
86
|
-
value:
|
|
101
|
+
value: attendedToEntries.length.toString(),
|
|
87
102
|
onClick: () => {
|
|
88
103
|
setFilters((prevFilters) => [
|
|
89
104
|
...prevFilters.filter((f) => f.key !== 'status'),
|
|
@@ -94,14 +109,16 @@ const TriageQueueTab: React.FC = () => {
|
|
|
94
109
|
];
|
|
95
110
|
|
|
96
111
|
return (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
<>
|
|
113
|
+
<QueueSummaryCards cards={cards} />
|
|
114
|
+
<QueueTab
|
|
115
|
+
queues={triageQueues}
|
|
116
|
+
navigatePath="triage"
|
|
117
|
+
usePatientChart
|
|
118
|
+
onTabChanged={setCurrQueue}
|
|
119
|
+
filters={filters}
|
|
120
|
+
onFiltersChanged={setFilters}
|
|
121
|
+
/>
|
|
122
|
+
</>
|
|
106
123
|
);
|
|
107
124
|
};
|
|
@@ -1,62 +1,68 @@
|
|
|
1
|
-
import { useConfig } from '@openmrs/esm-framework';
|
|
2
1
|
import { useMemo } from 'react';
|
|
3
|
-
import
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
import { useConfig } from '@openmrs/esm-framework';
|
|
4
|
+
|
|
5
|
+
import { type ExpressWorkflowConfig } from '../../config-schema';
|
|
4
6
|
import { useQueueEntries } from '../../hooks/useServiceQueues';
|
|
5
|
-
import { Queue } from '../../types';
|
|
7
|
+
import { type Queue } from '../../types';
|
|
6
8
|
|
|
7
9
|
export const useTriageQueuesMetrics = (queue?: Queue) => {
|
|
8
|
-
const {
|
|
9
|
-
|
|
10
|
-
queueEntries: waitingEntries,
|
|
11
|
-
isLoading: isLoadingWaiting,
|
|
12
|
-
error: waitingError,
|
|
13
|
-
} = useQueueEntries({
|
|
14
|
-
service: [queueServiceConceptUuids.triageService],
|
|
15
|
-
statuses: [queueStatusConceptUuids.waitingStatus],
|
|
16
|
-
location: queue?.location?.uuid ? [queue.location.uuid] : undefined,
|
|
17
|
-
});
|
|
18
|
-
const {
|
|
19
|
-
queueEntries: inServiceEntries,
|
|
20
|
-
isLoading: isLoadingInService,
|
|
21
|
-
error: inServiceError,
|
|
22
|
-
} = useQueueEntries({
|
|
23
|
-
service: [queueServiceConceptUuids.triageService],
|
|
24
|
-
statuses: [queueStatusConceptUuids.inServiceStatus],
|
|
25
|
-
location: queue?.location?.uuid ? [queue.location.uuid] : undefined,
|
|
26
|
-
});
|
|
10
|
+
const { queueStatusConceptUuids } = useConfig<ExpressWorkflowConfig>();
|
|
11
|
+
|
|
27
12
|
const {
|
|
28
|
-
queueEntries:
|
|
29
|
-
isLoading
|
|
30
|
-
error
|
|
13
|
+
queueEntries: allTriageEntries,
|
|
14
|
+
isLoading,
|
|
15
|
+
error,
|
|
16
|
+
isValidating: isValidatingQueueEntries,
|
|
31
17
|
} = useQueueEntries({
|
|
32
|
-
service: [queueServiceConceptUuids.triageService],
|
|
33
|
-
statuses: [queueStatusConceptUuids.finishedStatus],
|
|
34
|
-
location: queue?.location?.uuid ? [queue.location.uuid] : undefined,
|
|
35
|
-
});
|
|
36
|
-
const { queueEntries: attendedtoEntries } = useQueueEntries({
|
|
37
|
-
service: [queueServiceConceptUuids.triageService],
|
|
38
|
-
statuses: [queueStatusConceptUuids.inServiceStatus, queueStatusConceptUuids.finishedStatus],
|
|
39
18
|
location: queue?.location?.uuid ? [queue.location.uuid] : undefined,
|
|
19
|
+
startedOnOrAfter: dayjs().subtract(24, 'hour').format('YYYY-MM-DD HH:mm:ss'),
|
|
40
20
|
});
|
|
41
21
|
|
|
22
|
+
const queueUuid = queue?.uuid;
|
|
23
|
+
|
|
24
|
+
const waitingEntries = useMemo(
|
|
25
|
+
() =>
|
|
26
|
+
allTriageEntries.filter(
|
|
27
|
+
(entry) => entry?.queue?.uuid === queueUuid && entry?.status?.uuid === queueStatusConceptUuids.waitingStatus,
|
|
28
|
+
),
|
|
29
|
+
[allTriageEntries, queueUuid, queueStatusConceptUuids.waitingStatus],
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const inServiceEntries = useMemo(
|
|
33
|
+
() =>
|
|
34
|
+
allTriageEntries.filter(
|
|
35
|
+
(entry) => entry?.queue?.uuid === queueUuid && entry?.status?.uuid === queueStatusConceptUuids.inServiceStatus,
|
|
36
|
+
),
|
|
37
|
+
[allTriageEntries, queueUuid, queueStatusConceptUuids.inServiceStatus],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const finishedEntries = useMemo(
|
|
41
|
+
() =>
|
|
42
|
+
allTriageEntries.filter(
|
|
43
|
+
(entry) => entry?.queue?.uuid === queueUuid && entry?.status?.uuid === queueStatusConceptUuids.finishedStatus,
|
|
44
|
+
),
|
|
45
|
+
[allTriageEntries, queueUuid, queueStatusConceptUuids.finishedStatus],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const attendedToEntries = useMemo(
|
|
49
|
+
() =>
|
|
50
|
+
allTriageEntries.filter(
|
|
51
|
+
(entry) =>
|
|
52
|
+
entry?.queue?.uuid === queueUuid &&
|
|
53
|
+
(entry?.status?.uuid === queueStatusConceptUuids.inServiceStatus ||
|
|
54
|
+
entry?.status?.uuid === queueStatusConceptUuids.finishedStatus),
|
|
55
|
+
),
|
|
56
|
+
[allTriageEntries, queueUuid, queueStatusConceptUuids.inServiceStatus, queueStatusConceptUuids.finishedStatus],
|
|
57
|
+
);
|
|
58
|
+
|
|
42
59
|
return {
|
|
43
|
-
isLoading
|
|
44
|
-
waitingEntries
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
[inServiceEntries, queue],
|
|
51
|
-
),
|
|
52
|
-
finishedEntries: useMemo(
|
|
53
|
-
() => finishedEntries.filter((entry) => entry?.queue?.uuid === queue?.uuid),
|
|
54
|
-
[finishedEntries, queue],
|
|
55
|
-
),
|
|
56
|
-
attendedtoEntries: useMemo(
|
|
57
|
-
() => attendedtoEntries.filter((entry) => entry?.queue?.uuid === queue?.uuid),
|
|
58
|
-
[attendedtoEntries, queue],
|
|
59
|
-
),
|
|
60
|
-
error: finishedError ?? inServiceError ?? waitingError,
|
|
60
|
+
isLoading,
|
|
61
|
+
waitingEntries,
|
|
62
|
+
inServiceEntries,
|
|
63
|
+
finishedEntries,
|
|
64
|
+
attendedToEntries,
|
|
65
|
+
error,
|
|
66
|
+
isValidating: isValidatingQueueEntries,
|
|
61
67
|
};
|
|
62
68
|
};
|
|
@@ -1,68 +1,136 @@
|
|
|
1
|
-
import { openmrsFetch, restBaseUrl, useConfig } from '@openmrs/esm-framework';
|
|
2
|
-
import dayjs from 'dayjs';
|
|
3
1
|
import { useMemo } from 'react';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
2
|
+
import useSWRImmutable from 'swr/immutable';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
import { openmrsFetch, restBaseUrl, useConfig, useOpenmrsPagination } from '@openmrs/esm-framework';
|
|
5
|
+
|
|
6
|
+
import { type ExpressWorkflowConfig } from '../config-schema';
|
|
7
|
+
import { QueueEntriesPagination, type Queue, type QueueEntry, type QueueEntryFilters } from '../types/index';
|
|
8
|
+
|
|
9
|
+
const QUEUE_LIST_REP =
|
|
10
|
+
'custom:(uuid,display,name,location:(uuid,display),service:(uuid,display),queueRooms:(uuid,display))';
|
|
11
|
+
|
|
12
|
+
const SWR_QUEUE_OPTIONS = {
|
|
13
|
+
dedupingInterval: 5000,
|
|
14
|
+
revalidateOnFocus: false,
|
|
15
|
+
} as const;
|
|
7
16
|
|
|
8
17
|
export const useQueues = () => {
|
|
9
|
-
const
|
|
10
|
-
|
|
18
|
+
const queueListKey = `/ws/rest/v1/queue?v=${encodeURIComponent(QUEUE_LIST_REP)}`;
|
|
19
|
+
const { data, isLoading, error, isValidating } = useSWRImmutable<{ data: { results: Array<Queue> } }>(
|
|
20
|
+
queueListKey,
|
|
11
21
|
openmrsFetch,
|
|
22
|
+
SWR_QUEUE_OPTIONS,
|
|
12
23
|
);
|
|
13
24
|
|
|
14
25
|
return {
|
|
15
26
|
queues: data?.data?.results || [],
|
|
16
27
|
isLoading,
|
|
17
28
|
error,
|
|
29
|
+
isValidating,
|
|
18
30
|
};
|
|
19
31
|
};
|
|
20
32
|
|
|
21
|
-
export const useQueueEntries = (filters?: QueueEntryFilters) => {
|
|
33
|
+
export const useQueueEntries = (filters?: QueueEntryFilters, defaultPageSize: number = 100) => {
|
|
22
34
|
const { outpatientVisitTypeUuid } = useConfig<ExpressWorkflowConfig>();
|
|
23
35
|
const repString =
|
|
24
|
-
'custom:(uuid,
|
|
36
|
+
'custom:(uuid,queue:(uuid,display,name,location:(uuid,display)),status:(uuid,display),patient:(uuid,person:(uuid,display),identifiers),visit:(uuid,visitType:(uuid),attributes:(uuid,value,attributeType:(uuid))),priority:(uuid,display),priorityComment,startedAt,previousQueueEntry:(uuid,queue:(uuid,display)))';
|
|
25
37
|
|
|
26
|
-
const
|
|
38
|
+
const queryString = useMemo(() => {
|
|
27
39
|
const params = new URLSearchParams();
|
|
28
40
|
params.append('v', repString);
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
const merged = {
|
|
43
|
+
...filters,
|
|
44
|
+
status: filters?.statuses ?? [],
|
|
45
|
+
statuses: undefined,
|
|
46
|
+
startedOnOrAfter: filters?.startedOnOrAfter ?? undefined,
|
|
47
|
+
startedOnOrBefore: filters?.startedOnOrBefore ?? undefined,
|
|
48
|
+
};
|
|
49
|
+
if (merged) {
|
|
50
|
+
Object.entries(merged).forEach(([key, value]) => {
|
|
51
|
+
if (key === 'statuses' || value === undefined || value === null) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
[...value].sort((a, b) => String(a).localeCompare(String(b))).forEach((v) => params.append(key, v));
|
|
56
|
+
} else {
|
|
57
|
+
params.append(key, String(value));
|
|
38
58
|
}
|
|
39
59
|
});
|
|
40
60
|
}
|
|
41
61
|
|
|
42
|
-
if (
|
|
62
|
+
if (filters?.isEnded == null) {
|
|
43
63
|
params.append('isEnded', 'false');
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
return params.toString();
|
|
47
|
-
}
|
|
67
|
+
}, [
|
|
68
|
+
filters?.location?.join(','),
|
|
69
|
+
filters?.service?.join(','),
|
|
70
|
+
[...(filters?.statuses ?? [])].sort((a, b) => String(a).localeCompare(String(b))).join(','),
|
|
71
|
+
filters?.priorities?.join(','),
|
|
72
|
+
filters?.patient,
|
|
73
|
+
filters?.visit,
|
|
74
|
+
filters?.hasVisit,
|
|
75
|
+
filters?.isEnded,
|
|
76
|
+
filters?.locationsWaitingFor?.join(','),
|
|
77
|
+
filters?.providersWaitingFor?.join(','),
|
|
78
|
+
filters?.queuesComingFrom?.join(','),
|
|
79
|
+
]);
|
|
48
80
|
|
|
49
|
-
const
|
|
50
|
-
|
|
81
|
+
const url = queryString ? `/ws/rest/v1/queue-entry?${queryString}` : '/ws/rest/v1/queue-entry';
|
|
82
|
+
|
|
83
|
+
const {
|
|
84
|
+
data,
|
|
85
|
+
isLoading,
|
|
86
|
+
isValidating,
|
|
87
|
+
error,
|
|
88
|
+
totalPages,
|
|
89
|
+
totalCount,
|
|
90
|
+
currentPage,
|
|
91
|
+
currentPageSize,
|
|
92
|
+
paginated,
|
|
93
|
+
showNextButton,
|
|
94
|
+
showPreviousButton,
|
|
95
|
+
goTo,
|
|
96
|
+
goToNext,
|
|
97
|
+
goToPrevious,
|
|
98
|
+
} = useOpenmrsPagination<QueueEntry>(url, defaultPageSize, {
|
|
99
|
+
swrConfig: {
|
|
100
|
+
dedupingInterval: 2000,
|
|
101
|
+
revalidateOnFocus: false,
|
|
102
|
+
keepPreviousData: true,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
51
105
|
|
|
52
|
-
const { data, isLoading, error } = useSWR<{ data: { results: Array<QueueEntry> } }>(url, openmrsFetch);
|
|
53
106
|
const queueEntries = useMemo(() => {
|
|
54
|
-
return (data
|
|
107
|
+
return (data ?? [])?.filter((entry) => {
|
|
55
108
|
return (
|
|
56
109
|
dayjs(entry.startedAt).isAfter(dayjs().subtract(24, 'hour')) &&
|
|
57
110
|
entry?.visit?.visitType?.uuid === outpatientVisitTypeUuid
|
|
58
111
|
);
|
|
59
112
|
});
|
|
60
|
-
}, [data
|
|
113
|
+
}, [data, outpatientVisitTypeUuid]);
|
|
114
|
+
|
|
115
|
+
const pagination: QueueEntriesPagination = {
|
|
116
|
+
totalPages,
|
|
117
|
+
totalCount,
|
|
118
|
+
currentPage,
|
|
119
|
+
currentPageSize,
|
|
120
|
+
paginated,
|
|
121
|
+
showNextButton,
|
|
122
|
+
showPreviousButton,
|
|
123
|
+
goTo,
|
|
124
|
+
goToNext,
|
|
125
|
+
goToPrevious,
|
|
126
|
+
};
|
|
61
127
|
|
|
62
128
|
return {
|
|
63
129
|
queueEntries,
|
|
64
130
|
isLoading,
|
|
131
|
+
isValidating,
|
|
65
132
|
error,
|
|
133
|
+
pagination,
|
|
66
134
|
};
|
|
67
135
|
};
|
|
68
136
|
export function serveQueueEntry(servicePointName: string, ticketNumber: string, status: string, locationUuid?: string) {
|