@kenyaemr/esm-express-workflow-app 5.4.3 → 5.4.4-pre.12

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.
@@ -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, error } = useQueues();
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 { data: totalVisits, isLoading: isLoadingTotalVisits } = useTotalVisits();
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
- if (isLoading || isLoadingTotalVisits || isLoadingInvestigations || isLoadingQueueMetrics) {
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
- <QueueTab
275
- queues={consultationQueues}
276
- navigatePath="mch"
277
- cards={cards}
278
- usePatientChart
279
- filters={filters}
280
- onFiltersChanged={setFilters}
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
- attendedtoEntries,
35
+ attendedToEntries,
35
36
  waitingEntries,
37
+ isValidating: isValidatingMetrics,
36
38
  } = useTriageQueuesMetrics(activeQueue);
37
39
 
38
- if (isLoading || isLoadingMetrics) {
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
- <QueueTab
48
- queues={triageQueues}
49
- navigatePath="mch"
50
- cards={[
51
- {
52
- title: t('patientsAwaiting', 'Patient awaiting'),
53
- value: waitingEntries?.length?.toString(),
54
- onClick: () => {
55
- setFilters((prevFilters) => [
56
- ...prevFilters.filter((f) => f.key !== 'status'),
57
- { key: 'status', value: waitingStatus, label: t('waiting', 'Waiting') },
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
- attendedtoEntries,
70
+ attendedToEntries,
71
+ isValidating: isValidatingMetrics,
64
72
  } = useTriageQueuesMetrics(activeQueue);
65
73
 
66
- if (isLoadingMetrics) {
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 <ErrorState error={metricsError} headerTitle={t('errorLoadingQueues', 'Error loading queues')} />;
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: attendedtoEntries.length.toString(),
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
- <QueueTab
98
- queues={triageQueues}
99
- navigatePath="triage"
100
- usePatientChart
101
- cards={cards}
102
- onTabChanged={setCurrQueue}
103
- filters={filters}
104
- onFiltersChanged={setFilters}
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 { ExpressWorkflowConfig } from '../../config-schema';
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 { queueServiceConceptUuids, queueStatusConceptUuids } = useConfig<ExpressWorkflowConfig>();
9
- const {
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: finishedEntries,
29
- isLoading: isLoadingFinished,
30
- error: finishedError,
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: isLoadingFinished || isLoadingInService || isLoadingWaiting,
44
- waitingEntries: useMemo(
45
- () => waitingEntries.filter((entry) => entry?.queue?.uuid === queue?.uuid),
46
- [waitingEntries, queue],
47
- ),
48
- inServiceEntries: useMemo(
49
- () => inServiceEntries.filter((entry) => entry?.queue?.uuid === queue?.uuid),
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
  };
@@ -176,6 +176,11 @@ export const configSchema = {
176
176
  _description: 'The UUID of the outpatient visit type.',
177
177
  _default: '3371a4d4-f66f-4454-a86d-92c7b3da990c',
178
178
  },
179
+ inPatientVisitTypeUuid: {
180
+ _type: Type.String,
181
+ _description: 'The UUID of the in-patient visit type.',
182
+ _default: 'a73e2ac6-263b-47fc-99fc-e0f2c09fc914',
183
+ },
179
184
  };
180
185
 
181
186
  export type ExpressWorkflowConfig = {
@@ -231,4 +236,5 @@ export type ExpressWorkflowConfig = {
231
236
  labourWard: string;
232
237
  };
233
238
  outpatientVisitTypeUuid: string;
239
+ inPatientVisitTypeUuid: string;
234
240
  };
@@ -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 useSWR from 'swr';
5
- import { ExpressWorkflowConfig } from '../config-schema';
6
- import { Queue, QueueEntry, QueueEntryFilters } from '../types/index';
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 { data, isLoading, error } = useSWR<{ data: { results: Array<Queue> } }>(
10
- '/ws/rest/v1/queue?v=full',
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,display,queue,status,patient:(uuid,display,person,identifiers:(uuid,display,identifier,identifierType)),visit:(uuid,display,startDatetime,visitType:(uuid,display),encounters:(uuid,display,diagnoses,encounterDatetime,encounterType,obs,encounterProviders,voided),attributes:(uuid,display,value,attributeType)),priority,priorityComment,sortWeight,startedAt,endedAt,locationWaitingFor,queueComingFrom,providerWaitingFor,previousQueueEntry)';
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 buildQueryParams = (filters?: QueueEntryFilters & { status: string[] }): string => {
38
+ const queryString = useMemo(() => {
27
39
  const params = new URLSearchParams();
28
40
  params.append('v', repString);
29
41
 
30
- if (filters) {
31
- Object.entries(filters).forEach(([key, value]) => {
32
- if (value !== undefined && value !== null) {
33
- if (Array.isArray(value)) {
34
- value.forEach((v) => params.append(key, v));
35
- } else {
36
- params.append(key, String(value));
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 (!filters || filters.isEnded === undefined || filters.isEnded === null) {
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 queryString = buildQueryParams({ ...filters, status: filters?.statuses ?? [], statuses: [] });
50
- const url = `/ws/rest/v1/queue-entry${queryString ? `?${queryString}` : ''}`;
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?.data?.results ?? [])?.filter((entry) => {
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?.data?.results, outpatientVisitTypeUuid]);
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) {
@@ -3,17 +3,15 @@ import { useTranslation } from 'react-i18next';
3
3
  import {
4
4
  AssignedExtension,
5
5
  FetchResponse,
6
- type Location,
7
6
  openmrsFetch,
8
7
  restBaseUrl,
9
8
  useAssignedExtensions,
10
9
  useConfig,
11
10
  useEmrConfiguration,
12
- usePatient,
13
11
  useVisit,
14
12
  } from '@openmrs/esm-framework';
15
13
  import { ExpressWorkflowConfig } from '../../config-schema';
16
- import useSWR from 'swr';
14
+ import useSWRImmutable from 'swr/immutable';
17
15
 
18
16
  export const usePatientChartTabs = (navigationPath: string, patientUuid: string, patient?: fhir.Patient) => {
19
17
  const { t } = useTranslation();
@@ -99,11 +97,10 @@ export const usePatientChartTabs = (navigationPath: string, patientUuid: string,
99
97
  * }
100
98
  */
101
99
  export const useCurrentPatientAdmissionEncounter = (patientUuid: string) => {
100
+ // TODO: use visit context store instead of useVisit. This is happening on every mount of the patient chart dashboard. Which is not efficient.
102
101
  const { currentVisit, error: visitError, isLoading: isLoadingVisit, mutate: mutateVisit } = useVisit(patientUuid);
103
102
  const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
104
- const { inPatientVisitTypeUuid } = useConfig<{ inPatientVisitTypeUuid: string }>({
105
- externalModuleName: '@kenyaemr/esm-ward-app',
106
- });
103
+ const { inPatientVisitTypeUuid } = useConfig<ExpressWorkflowConfig>();
107
104
  // Admission or Tranfer encounter depending on wether patient was transfered or admitted directly
108
105
  const latestAdmisionEncounter = useMemo(() => {
109
106
  return currentVisit?.encounters?.find(
@@ -138,8 +135,7 @@ export const useCurrentPatientAdmissionEncounter = (patientUuid: string) => {
138
135
  * Show partography component when patient is a female patient, admitted to labour ward
139
136
  * @param patientUuid string
140
137
  */
141
- export const useShowPatography = (patientUuid: string) => {
142
- const { patient, isLoading: isLoadingPatient, error: patientError } = usePatient(patientUuid);
138
+ export const useShowPatography = (patient: fhir.Patient) => {
143
139
  const isFemale = patient?.gender?.toLowerCase() === 'female';
144
140
 
145
141
  const {
@@ -147,7 +143,7 @@ export const useShowPatography = (patientUuid: string) => {
147
143
  error: admissionError,
148
144
  isLoading: isloadingAdmission,
149
145
  isPatientAdmitted,
150
- } = useCurrentPatientAdmissionEncounter(patientUuid);
146
+ } = useCurrentPatientAdmissionEncounter(patient.id);
151
147
  const {
152
148
  error: tagsError,
153
149
  isLoading: isloadingTags,
@@ -160,8 +156,8 @@ export const useShowPatography = (patientUuid: string) => {
160
156
  );
161
157
 
162
158
  return {
163
- isLoading: isLoadingPatient || isLoadingPatient || isloadingAdmission || isloadingTags,
164
- error: patientError ?? admissionError ?? tagsError,
159
+ isLoading: isloadingAdmission || isloadingTags,
160
+ error: admissionError ?? tagsError,
165
161
  showPartography: isFemale && isPatientAdmitted && admissionLocationIsLabourWard,
166
162
  };
167
163
  };
@@ -171,7 +167,7 @@ type Tag = { uuid: string; display: string; name: string; description: string };
171
167
  const useAdmissionLocationTags = (locationUuid?: string) => {
172
168
  const rep = 'custom:(tags:(uuid,display,name,description))';
173
169
  const url = `${restBaseUrl}/location/${locationUuid}?v=${rep}`;
174
- const { data, error, isLoading, mutate } = useSWR<FetchResponse<{ tags: Array<Tag> }>>(
170
+ const { data, error, isLoading, mutate } = useSWRImmutable<FetchResponse<{ tags: Array<Tag> }>>(
175
171
  locationUuid ? url : null,
176
172
  openmrsFetch,
177
173
  );