@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.
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":">=2.24.0"},"pages":[{"component":"root","route":"express-workflow"},{"component":"queuesAdminHome","route":"queues-admin"},{"component":"appointmentsDashboard","route":"appointments"}],"extensions":[{"component":"appointmentsDashboardLink","name":"appointments-dashboard-link","slot":"appointments-left-panel-slot","meta":{"name":"appointments","slot":"appointments-dashboard-slot","title":"Appointments"}},{"component":"admissionsLeftPanelLink","name":"admissions-dashboard-link","slot":"express-workflow-left-panel-slot","order":9,"meta":{"name":"admissions","slot":"admissions-dashboard-slot","title":"Admissions"}},{"component":"admissionsDashboard","name":"admissions-dashboard","slot":"admissions-dashboard-slot"},{"component":"consultationLeftPanelLink","name":"consultation-dashboard-link","slot":"homepage-dashboard-slot","order":3,"meta":{"name":"consultation","slot":"consultation-dashboard-slot","title":"Consultation"}},{"component":"consultationDashboard","name":"consultation-dashboard","slot":"consultation-dashboard-slot"},{"component":"facilityLeftPanelLink","name":"facility-dashboard-link","slot":"homepage-dashboard-slot","order":0,"meta":{"name":"dashboard","slot":"facility-dashboard-slot","title":"Dashboard"}},{"component":"facilityDashboard","name":"facility-dashboard","slot":"facility-dashboard-slot"},{"component":"mchLeftPanelLink","name":"mch-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"mch","slot":"mch-dashboard-slot","title":"MCH"}},{"component":"mchDashboard","name":"mch-dashboard","slot":"mch-dashboard-slot"},{"component":"registrationLeftPanelLink","name":"registration-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"registration","slot":"registration-dashboard-slot","title":"Registration"}},{"component":"registrationDashboard","name":"registration-dashboard","slot":"registration-dashboard-slot"},{"component":"triageLeftPanelLink","name":"triage-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"triage","slot":"triage-dashboard-slot","title":"Triage"}},{"component":"triageDashboard","name":"triage-dashboard","slot":"triage-dashboard-slot"},{"name":"custom-visit-form-queue-fields","component":"checkinFormExtraExtension","slot":"visit-form-bottom-slot","order":0},{"component":"reportsDashboardLink","name":"reports-homepage-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"reports","slot":"reports-homepage-dashboard-slot","title":"Reports"}},{"component":"reportsDashboard","name":"reports-dashboard","slot":"reports-homepage-dashboard-slot"},{"name":"pharmacy-homepage-dashboard-link","component":"pharmacyLink","slot":"homepage-dashboard-slot","meta":{"name":"pharmacy","title":"Pharmacy","path":"pharmacy","slot":"pharmacy-homepage-dashboard-slot"}},{"name":"pharmacy-homepage-dashboard","component":"pharmacy","slot":"pharmacy-homepage-dashboard-slot","online":true,"offline":false},{"name":"ewf-pharmacy-orders-dashboard","component":"pharmacyOrders","slot":"ewf-orders-slot","online":true,"offline":false},{"name":"patient-summary-dashboard","component":"patientSummaryDashboard","slot":"patient-chart-summary-dashboard-slot"},{"name":"ewf-clinical-encounter-link","component":"clinicalEncounterLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-clinical-encounter","title":"Clinical Encounter","slot":"ewf-patient-chart-clinical-encounter-slot","path":"clinical-encounter","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-clinical-encounter-dashboard","component":"clinicalEncounter","slot":"ewf-patient-chart-clinical-encounter-slot","online":true,"offline":false},{"name":"ewf-laboratory-dashboard-link","component":"laboratoryLeftPanelLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-laboratory","title":"Lab Orders","path":"laboratory","slot":"ewf-patient-chart-laboratory-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-laboratory-dashboard","component":"laboratoryDashboard","slot":"ewf-patient-chart-laboratory-dashboard-slot","online":true,"offline":false},{"name":"ewf-radiology-and-imaging-dashboard-link","component":"radiologyAndImagingLeftPanelLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-radiology-and-imaging","title":"Radiology and Imaging","path":"radiology-and-imaging","slot":"ewf-patient-chart-radiology-and-imaging-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-radiology-and-imaging-dashboard","component":"radiologyAndImagingDashboard","slot":"ewf-patient-chart-radiology-and-imaging-dashboard-slot","online":true,"offline":false},{"name":"ewf-procedures-dashboard-link","component":"proceduresLeftPanelLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-procedures","title":"Procedures","path":"procedures","slot":"ewf-patient-chart-procedures-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-procedures-dashboard","component":"proceduresDashboard","slot":"ewf-patient-chart-procedures-dashboard-slot","online":true,"offline":false},{"name":"ewf-pharmacy-dashboard-link","component":"pharmacyPatientChartDashboardLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-pharmacy","title":"Prescription","path":"pharmacy","slot":"ewf-patient-chart-pharmacy-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-pharmacy-dashboard","component":"pharmacyTabs","slot":"ewf-patient-chart-pharmacy-dashboard-slot","online":true,"offline":false},{"name":"ewf-admissions-dashboard-link","component":"admissionsDashboardLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-admissions","title":"In-patient","path":"admissions","slot":"ewf-patient-chart-admissions-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-admissions-dashboard","component":"admissionsDashboard","slot":"ewf-patient-chart-admissions-dashboard-slot","online":true,"offline":false},{"name":"ewf-homepage-dashboard-link","component":"homepageDashboardLink","slot":"patient-chart-dashboard-slot","order":0},{"name":"ewf-encounter-details-dashboard","component":"encounterDetails","slot":"ewf-clinical-encounter-slot","online":true,"offline":false}],"workspaces":[{"name":"express-workflow-workspace","component":"expressWorkflowWorkspace","title":"Express Workflow Workspace","type":"workspace","canMaximize":true,"canHide":true}],"modals":[{"component":"otpVerificationModal","name":"otp-verification-modal"},{"component":"facilityDashboardAuthorizationModal","name":"facility-dashboard-authorization-form-modal"}],"workspaceGroups":[{"name":"express-workflow","members":["add-drug-order","order-basket","add-lab-order","express-workflow-workspace","start-visit-workspace-form"]}],"version":"5.4.3"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"fhir2":">=1.2","webservices.rest":">=2.24.0"},"pages":[{"component":"root","route":"express-workflow"},{"component":"queuesAdminHome","route":"queues-admin"},{"component":"appointmentsDashboard","route":"appointments"}],"extensions":[{"component":"appointmentsDashboardLink","name":"appointments-dashboard-link","slot":"appointments-left-panel-slot","meta":{"name":"appointments","slot":"appointments-dashboard-slot","title":"Appointments"}},{"component":"admissionsLeftPanelLink","name":"admissions-dashboard-link","slot":"express-workflow-left-panel-slot","order":9,"meta":{"name":"admissions","slot":"admissions-dashboard-slot","title":"Admissions"}},{"component":"admissionsDashboard","name":"admissions-dashboard","slot":"admissions-dashboard-slot"},{"component":"consultationLeftPanelLink","name":"consultation-dashboard-link","slot":"homepage-dashboard-slot","order":3,"meta":{"name":"consultation","slot":"consultation-dashboard-slot","title":"Consultation"}},{"component":"consultationDashboard","name":"consultation-dashboard","slot":"consultation-dashboard-slot"},{"component":"facilityLeftPanelLink","name":"facility-dashboard-link","slot":"homepage-dashboard-slot","order":0,"meta":{"name":"dashboard","slot":"facility-dashboard-slot","title":"Dashboard"}},{"component":"facilityDashboard","name":"facility-dashboard","slot":"facility-dashboard-slot"},{"component":"mchLeftPanelLink","name":"mch-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"mch","slot":"mch-dashboard-slot","title":"MCH"}},{"component":"mchDashboard","name":"mch-dashboard","slot":"mch-dashboard-slot"},{"component":"registrationLeftPanelLink","name":"registration-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"registration","slot":"registration-dashboard-slot","title":"Registration"}},{"component":"registrationDashboard","name":"registration-dashboard","slot":"registration-dashboard-slot"},{"component":"triageLeftPanelLink","name":"triage-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"triage","slot":"triage-dashboard-slot","title":"Triage"}},{"component":"triageDashboard","name":"triage-dashboard","slot":"triage-dashboard-slot"},{"name":"custom-visit-form-queue-fields","component":"checkinFormExtraExtension","slot":"visit-form-bottom-slot","order":0},{"component":"reportsDashboardLink","name":"reports-homepage-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"reports","slot":"reports-homepage-dashboard-slot","title":"Reports"}},{"component":"reportsDashboard","name":"reports-dashboard","slot":"reports-homepage-dashboard-slot"},{"name":"pharmacy-homepage-dashboard-link","component":"pharmacyLink","slot":"homepage-dashboard-slot","meta":{"name":"pharmacy","title":"Pharmacy","path":"pharmacy","slot":"pharmacy-homepage-dashboard-slot"}},{"name":"pharmacy-homepage-dashboard","component":"pharmacy","slot":"pharmacy-homepage-dashboard-slot","online":true,"offline":false},{"name":"ewf-pharmacy-orders-dashboard","component":"pharmacyOrders","slot":"ewf-orders-slot","online":true,"offline":false},{"name":"patient-summary-dashboard","component":"patientSummaryDashboard","slot":"patient-chart-summary-dashboard-slot"},{"name":"ewf-clinical-encounter-link","component":"clinicalEncounterLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-clinical-encounter","title":"Clinical Encounter","slot":"ewf-patient-chart-clinical-encounter-slot","path":"clinical-encounter","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-clinical-encounter-dashboard","component":"clinicalEncounter","slot":"ewf-patient-chart-clinical-encounter-slot","online":true,"offline":false},{"name":"ewf-laboratory-dashboard-link","component":"laboratoryLeftPanelLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-laboratory","title":"Lab Orders","path":"laboratory","slot":"ewf-patient-chart-laboratory-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-laboratory-dashboard","component":"laboratoryDashboard","slot":"ewf-patient-chart-laboratory-dashboard-slot","online":true,"offline":false},{"name":"ewf-radiology-and-imaging-dashboard-link","component":"radiologyAndImagingLeftPanelLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-radiology-and-imaging","title":"Radiology and Imaging","path":"radiology-and-imaging","slot":"ewf-patient-chart-radiology-and-imaging-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-radiology-and-imaging-dashboard","component":"radiologyAndImagingDashboard","slot":"ewf-patient-chart-radiology-and-imaging-dashboard-slot","online":true,"offline":false},{"name":"ewf-procedures-dashboard-link","component":"proceduresLeftPanelLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-procedures","title":"Procedures","path":"procedures","slot":"ewf-patient-chart-procedures-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-procedures-dashboard","component":"proceduresDashboard","slot":"ewf-patient-chart-procedures-dashboard-slot","online":true,"offline":false},{"name":"ewf-pharmacy-dashboard-link","component":"pharmacyPatientChartDashboardLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-pharmacy","title":"Prescription","path":"pharmacy","slot":"ewf-patient-chart-pharmacy-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-pharmacy-dashboard","component":"pharmacyTabs","slot":"ewf-patient-chart-pharmacy-dashboard-slot","online":true,"offline":false},{"name":"ewf-admissions-dashboard-link","component":"admissionsDashboardLink","slot":"patient-chart-dashboard-slot","meta":{"name":"ewf-admissions","title":"In-patient","path":"admissions","slot":"ewf-patient-chart-admissions-dashboard-slot","layoutMode":"anchored","hideDashboardTitle":true}},{"name":"ewf-admissions-dashboard","component":"admissionsDashboard","slot":"ewf-patient-chart-admissions-dashboard-slot","online":true,"offline":false},{"name":"ewf-homepage-dashboard-link","component":"homepageDashboardLink","slot":"patient-chart-dashboard-slot","order":0},{"name":"ewf-encounter-details-dashboard","component":"encounterDetails","slot":"ewf-clinical-encounter-slot","online":true,"offline":false}],"workspaces":[{"name":"express-workflow-workspace","component":"expressWorkflowWorkspace","title":"Express Workflow Workspace","type":"workspace","canMaximize":true,"canHide":true}],"modals":[{"component":"otpVerificationModal","name":"otp-verification-modal"},{"component":"facilityDashboardAuthorizationModal","name":"facility-dashboard-authorization-form-modal"}],"workspaceGroups":[{"name":"express-workflow","members":["add-drug-order","order-basket","add-lab-order","express-workflow-workspace","start-visit-workspace-form"]}],"version":"5.4.4-pre.12"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-express-workflow-app",
3
- "version": "5.4.3",
3
+ "version": "5.4.4-pre.12",
4
4
  "description": "Express workflow app for OpenMRS 3",
5
5
  "keywords": [
6
6
  "openmrs",
@@ -54,5 +54,6 @@
54
54
  },
55
55
  "publishConfig": {
56
56
  "access": "public"
57
- }
57
+ },
58
+ "stableVersion": "5.4.3"
58
59
  }
@@ -1,5 +1,4 @@
1
- import { IconButton, InlineLoading } from '@carbon/react';
2
- import { Renew } from '@carbon/react/icons';
1
+ import { InlineLoading } from '@carbon/react';
3
2
  import { ExtensionSlot, HomePictogram, PageHeader, PageHeaderContent, useConfig } from '@openmrs/esm-framework';
4
3
  import capitalize from 'lodash-es/capitalize';
5
4
  import React, { useCallback, useMemo, useState } from 'react';
@@ -7,8 +6,10 @@ import { useTranslation } from 'react-i18next';
7
6
  import { ExpressWorkflowConfig } from '../../config-schema';
8
7
  import { useQueues } from '../../hooks/useServiceQueues';
9
8
  import QueueTab from '../../shared/queue/queue-tab.component';
9
+ import QueueSummaryCards, { QueueSummaryCard } from '../../shared/queue/queue-summary-cards.component';
10
10
  import { Queue, QueueFilter } from '../../types';
11
11
  import { useConsultationQueueMetrics, useInvestigationStats, useTotalVisits } from './consultation.resource';
12
+ import { buildConsultationCards } from './consultation.utils';
12
13
  import styles from './consultation.scss';
13
14
 
14
15
  type ConsultationProps = {
@@ -22,7 +23,7 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
22
23
  queueServiceConceptUuids,
23
24
  } = useConfig<ExpressWorkflowConfig>();
24
25
  const [currQueue, setCurrQueue] = useState<Queue>();
25
- const { queues, isLoading, error } = useQueues();
26
+ const { queues, isLoading: isLoadingQueues, isValidating: isValidatingQueues } = useQueues();
26
27
  const [isRefreshing, setIsRefreshing] = useState(false);
27
28
 
28
29
  const consultationQueues = queues.filter(
@@ -34,8 +35,8 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
34
35
  const activeQueue = useMemo(() => currQueue ?? consultationQueues[0], [currQueue, consultationQueues]);
35
36
  const {
36
37
  waitingEntries,
37
- isLoading: isLoadingQueuemetrics,
38
- error: waitingError,
38
+ isLoading: isLoadingQueueMetrics,
39
+ isValidating: isValidatingQueueMetrics,
39
40
  emergencyEntries,
40
41
  urgentEntries,
41
42
  notUrgentEntries,
@@ -46,7 +47,6 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
46
47
  const {
47
48
  awaitingCount,
48
49
  completedCount,
49
- totalCount,
50
50
  lab,
51
51
  radiology,
52
52
  procedures,
@@ -67,168 +67,32 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
67
67
  }
68
68
  }, [refreshInvestigations]);
69
69
 
70
- const cards = useMemo(
71
- () => [
72
- {
73
- title: t('awaitingConsultation', 'Awaiting consultation'),
74
- value: waitingEntries.length.toString(),
75
- categories: [
76
- {
77
- label: t('emergency', 'Emergency'),
78
- value: emergencyEntries.length,
79
- onClick: () => {
80
- setFilters((prevFilters) => [
81
- ...prevFilters.filter((f) => f.key !== 'priority'),
82
- { key: 'priority', value: emergencyPriorityConceptUuid, label: t('emergency', 'Emergency') },
83
- ]);
84
- },
85
- },
86
- {
87
- label: t('urgent', 'Urgent'),
88
- value: urgentEntries.length,
89
- onClick: () => {
90
- setFilters((prevFilters) => [
91
- ...prevFilters.filter((f) => f.key !== 'priority'),
92
- { key: 'priority', value: urgentPriorityConceptUuid, label: t('urgent', 'Urgent') },
93
- ]);
94
- },
95
- },
96
- {
97
- label: t('notUrgent', 'Not Urgent'),
98
- value: notUrgentEntries.length,
99
- onClick: () => {
100
- setFilters((prevFilters) => [
101
- ...prevFilters.filter((f) => f.key !== 'priority'),
102
- { key: 'priority', value: notUrgentPriorityConceptUuid, label: t('notUrgent', 'Not Urgent') },
103
- ]);
104
- },
105
- },
106
- ],
107
- },
108
- {
109
- title: t('investigationAwaiting', 'Investigation Awaiting'),
110
- value: awaitingCount.toString(),
111
- categories: [
112
- {
113
- label: t('lab', 'Lab'),
114
- value: lab.awaiting,
115
- onClick: () => {
116
- setFilters((prevFilters) => [
117
- ...prevFilters.filter((f) => f.key !== 'service_awaiting'),
118
- {
119
- key: 'service_awaiting',
120
- value: investigationCategorizedEntries.newLabOrders.map((entry) => entry.patient.uuid).join(','),
121
- label: t('labAwaiting', 'Lab Awaiting'),
122
- },
123
- ]);
124
- },
125
- },
126
- {
127
- label: t('radiology', 'Radiology'),
128
- value: radiology.awaiting,
129
- onClick: () => {
130
- setFilters((prevFilters) => [
131
- ...prevFilters.filter((f) => f.key !== 'service_awaiting'),
132
- {
133
- key: 'service_awaiting',
134
- value: investigationCategorizedEntries.newRadiologyOrders
135
- .map((entry) => entry.patient.uuid)
136
- .join(','),
137
- label: t('radiologyAwaiting', 'Radiology Awaiting'),
138
- },
139
- ]);
140
- },
141
- },
142
- {
143
- label: t('procedures', 'Procedures'),
144
- value: procedures.awaiting,
145
- onClick: () => {
146
- setFilters((prevFilters) => [
147
- ...prevFilters.filter((f) => f.key !== 'service_awaiting'),
148
- {
149
- key: 'service_awaiting',
150
- value: investigationCategorizedEntries.newProcedureOrders
151
- .map((entry) => entry.patient.uuid)
152
- .join(','),
153
- label: t('proceduresAwaiting', 'Procedures Awaiting'),
154
- },
155
- ]);
156
- },
157
- },
158
- ],
159
- refreshButton:
160
- isRefreshing || isLoadingInvestigations ? (
161
- <InlineLoading description={t('refreshing', 'Refreshing...')} />
162
- ) : (
163
- <IconButton
164
- label={t('refreshInvestigations', 'Refresh investigations')}
165
- kind="ghost"
166
- size="sm"
167
- onClick={handleRefresh}
168
- className={styles.refreshButton}>
169
- <Renew size={16} />
170
- </IconButton>
171
- ),
172
- },
173
- {
174
- title: t('investigationCompleted', 'Investigation Completed'),
175
- value: completedCount.toString(),
176
- categories: [
177
- {
178
- label: t('lab', 'Lab'),
179
- value: lab.completed,
180
- onClick: () => {
181
- setFilters((prevFilters) => [
182
- ...prevFilters.filter((f) => f.key !== 'service_completed'),
183
- {
184
- key: 'service_completed',
185
- value: investigationCategorizedEntries.completedLabOrders
186
- .map((entry) => entry.patient.uuid)
187
- .join(','),
188
- label: t('labCompleted', 'Lab Completed'),
189
- },
190
- ]);
191
- },
192
- },
193
- {
194
- label: t('radiology', 'Radiology'),
195
- value: radiology.completed,
196
- onClick: () => {
197
- setFilters((prevFilters) => [
198
- ...prevFilters.filter((f) => f.key !== 'service_completed'),
199
- {
200
- key: 'service_completed',
201
- value: investigationCategorizedEntries.completedRadiologyOrders
202
- .map((entry) => entry.patient.uuid)
203
- .join(','),
204
- label: t('radiologyCompleted', 'Radiology Completed'),
205
- },
206
- ]);
207
- },
208
- },
209
- {
210
- label: t('procedures', 'Procedures'),
211
- value: procedures.completed,
212
- onClick: () => {
213
- setFilters((prevFilters) => [
214
- ...prevFilters.filter((f) => f.key !== 'service_completed'),
215
- {
216
- key: 'service_completed',
217
- value: investigationCategorizedEntries.completedProcedureOrders
218
- .map((entry) => entry.patient.uuid)
219
- .join(','),
220
- label: t('proceduresCompleted', 'Procedures Completed'),
221
- },
222
- ]);
223
- },
224
- },
225
- ],
226
- },
227
- {
228
- title: t('totalVisits', 'Total Visits'),
229
- value: totalVisits?.length?.toString() ?? '0',
230
- },
231
- ],
70
+ const cards: Array<QueueSummaryCard> = useMemo(
71
+ () =>
72
+ buildConsultationCards({
73
+ t,
74
+ waitingCount: waitingEntries.length,
75
+ emergencyCount: emergencyEntries.length,
76
+ urgentCount: urgentEntries.length,
77
+ notUrgentCount: notUrgentEntries.length,
78
+ awaitingCount,
79
+ completedCount,
80
+ labAwaiting: lab.awaiting,
81
+ labCompleted: lab.completed,
82
+ radiologyAwaiting: radiology.awaiting,
83
+ radiologyCompleted: radiology.completed,
84
+ proceduresAwaiting: procedures.awaiting,
85
+ proceduresCompleted: procedures.completed,
86
+ isRefreshing,
87
+ isLoadingInvestigations,
88
+ onRefreshInvestigations: handleRefresh,
89
+ totalVisitsCount: totalVisits?.length,
90
+ setFilters,
91
+ emergencyPriorityConceptUuid,
92
+ urgentPriorityConceptUuid,
93
+ notUrgentPriorityConceptUuid,
94
+ investigationCategorizedEntries,
95
+ }),
232
96
  [
233
97
  t,
234
98
  waitingEntries.length,
@@ -236,6 +100,7 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
236
100
  urgentEntries.length,
237
101
  notUrgentEntries.length,
238
102
  awaitingCount,
103
+ completedCount,
239
104
  lab.awaiting,
240
105
  lab.completed,
241
106
  radiology.awaiting,
@@ -245,21 +110,20 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
245
110
  isRefreshing,
246
111
  isLoadingInvestigations,
247
112
  handleRefresh,
248
- completedCount,
249
113
  totalVisits?.length,
250
114
  emergencyPriorityConceptUuid,
251
115
  urgentPriorityConceptUuid,
252
116
  notUrgentPriorityConceptUuid,
253
- investigationCategorizedEntries.newLabOrders,
254
- investigationCategorizedEntries.newRadiologyOrders,
255
- investigationCategorizedEntries.newProcedureOrders,
256
- investigationCategorizedEntries.completedLabOrders,
257
- investigationCategorizedEntries.completedRadiologyOrders,
258
- investigationCategorizedEntries.completedProcedureOrders,
117
+ investigationCategorizedEntries,
259
118
  ],
260
119
  );
261
120
 
262
- if (isLoading || isLoadingTotalVisits || isLoadingInvestigations || isLoadingQueuemetrics) {
121
+ const isLoading =
122
+ (isLoadingQueues || isLoadingQueueMetrics || isLoadingTotalVisits || isLoadingInvestigations) &&
123
+ !isValidatingQueues &&
124
+ !isValidatingQueueMetrics;
125
+
126
+ if (isLoading) {
263
127
  return <InlineLoading description={t('loadingQueues', 'Loading queues...')} />;
264
128
  }
265
129
 
@@ -269,9 +133,9 @@ const Consultation: React.FC<ConsultationProps> = ({ dashboardTitle }) => {
269
133
  <PageHeaderContent title={capitalize(dashboardTitle)} illustration={<HomePictogram />} />
270
134
  <ExtensionSlot name="provider-banner-info-slot" />
271
135
  </PageHeader>
136
+ <QueueSummaryCards cards={cards} />
272
137
  <QueueTab
273
138
  queues={consultationQueues}
274
- cards={cards}
275
139
  navigatePath="consultation"
276
140
  usePatientChart
277
141
  filters={filters}
@@ -2,7 +2,7 @@ import { openmrsFetch, restBaseUrl, useConfig, Visit } from '@openmrs/esm-framew
2
2
  import { FulfillerStatus, Order } from '@openmrs/esm-patient-common-lib';
3
3
  import dayjs from 'dayjs';
4
4
  import useSWR from 'swr';
5
- import { useMemo, useCallback } from 'react';
5
+ import { useMemo, useCallback, useEffect, useState } from 'react';
6
6
  import { Queue, QueueEntry } from '../../types';
7
7
  import { ExpressWorkflowConfig } from '../../config-schema';
8
8
  import { useQueueEntries } from '../../hooks/useServiceQueues';
@@ -25,17 +25,22 @@ export const useTotalVisits = () => {
25
25
  'YYYY-MM-DD',
26
26
  )}`;
27
27
 
28
- const { data, error, isLoading, mutate } = useSWR<{ data: { results: Array<Visit> } }>(visitsUrl, openmrsFetch, {
29
- revalidateOnFocus: false,
30
- revalidateOnReconnect: false,
31
- dedupingInterval: 60000,
32
- });
28
+ const { data, error, isLoading, mutate, isValidating } = useSWR<{ data: { results: Array<Visit> } }>(
29
+ visitsUrl,
30
+ openmrsFetch,
31
+ {
32
+ revalidateOnFocus: false,
33
+ revalidateOnReconnect: false,
34
+ dedupingInterval: 60000,
35
+ },
36
+ );
33
37
 
34
38
  return {
35
39
  data: data?.data?.results,
36
40
  error,
37
41
  isLoading,
38
42
  mutate,
43
+ isValidating,
39
44
  };
40
45
  };
41
46
 
@@ -199,7 +204,7 @@ function useProcedureOrders(fulfillerStatus?: string) {
199
204
  }
200
205
 
201
206
  export const useInvestigationStats = (queue?: Queue) => {
202
- const { isLoading: isLoadingQueueMetrics, waitingEntries } = useConsultationQueueMetrics(queue);
207
+ const { isLoading: isLoadingQueueMetrics, waitingEntries, isValidating } = useConsultationQueueMetrics(queue);
203
208
 
204
209
  // Fetch all investigation orders
205
210
  const {
@@ -381,6 +386,7 @@ export const useInvestigationStats = (queue?: Queue) => {
381
386
  isLoading,
382
387
  refresh,
383
388
  investigationCategorizedEntries,
389
+ isValidating,
384
390
  };
385
391
  };
386
392
 
@@ -389,21 +395,25 @@ export const useConsultationQueueMetrics = (queue?: Queue) => {
389
395
  const {
390
396
  queueEntries: waitingEntries,
391
397
  isLoading: isLoadingWaiting,
398
+ isValidating: isValidatingWaiting,
392
399
  error: waitingError,
393
400
  } = useQueueEntries({
394
401
  service: [queueServiceConceptUuids.consultationService],
395
402
  statuses: [queueStatusConceptUuids.waitingStatus, queueStatusConceptUuids.inServiceStatus],
396
403
  location: queue?.location?.uuid ? [queue.location.uuid] : undefined,
404
+ startedOnOrAfter: dayjs().subtract(24, 'hour').format('YYYY-MM-DD HH:mm:ss'),
397
405
  });
398
406
 
399
407
  const _waitingEntries = useMemo(
400
408
  () => waitingEntries.filter((entry) => entry?.queue?.uuid === queue?.uuid),
401
409
  [waitingEntries, queue],
402
410
  );
411
+
403
412
  return {
404
413
  isLoading: isLoadingWaiting,
405
414
  error: waitingError,
406
415
  waitingEntries: _waitingEntries,
416
+ isValidating: isValidatingWaiting,
407
417
  emergencyEntries: useMemo(
408
418
  () => _waitingEntries.filter((entry) => entry.priority.uuid === priorities?.emergencyPriorityConceptUuid),
409
419
  [_waitingEntries, priorities?.emergencyPriorityConceptUuid],
@@ -0,0 +1,222 @@
1
+ import React from 'react';
2
+ import { IconButton, InlineLoading } from '@carbon/react';
3
+ import { Renew } from '@carbon/react/icons';
4
+ import type { TFunction } from 'react-i18next';
5
+ import type { QueueSummaryCard } from '../../shared/queue/queue-summary-cards.component';
6
+ import type { QueueFilter, QueueEntry } from '../../types';
7
+ import styles from './consultation.scss';
8
+
9
+ type InvestigationCategorizedEntries = {
10
+ newLabOrders: QueueEntry[];
11
+ newRadiologyOrders: QueueEntry[];
12
+ newProcedureOrders: QueueEntry[];
13
+ completedLabOrders: QueueEntry[];
14
+ completedRadiologyOrders: QueueEntry[];
15
+ completedProcedureOrders: QueueEntry[];
16
+ };
17
+
18
+ type BuildConsultationCardsParams = {
19
+ t: TFunction;
20
+ waitingCount: number;
21
+ emergencyCount: number;
22
+ urgentCount: number;
23
+ notUrgentCount: number;
24
+ awaitingCount: number;
25
+ completedCount: number;
26
+ labAwaiting: number;
27
+ labCompleted: number;
28
+ radiologyAwaiting: number;
29
+ radiologyCompleted: number;
30
+ proceduresAwaiting: number;
31
+ proceduresCompleted: number;
32
+ isRefreshing: boolean;
33
+ isLoadingInvestigations: boolean;
34
+ onRefreshInvestigations: () => void;
35
+ totalVisitsCount?: number;
36
+ setFilters: React.Dispatch<React.SetStateAction<QueueFilter[]>>;
37
+ emergencyPriorityConceptUuid: string;
38
+ urgentPriorityConceptUuid: string;
39
+ notUrgentPriorityConceptUuid: string;
40
+ investigationCategorizedEntries: InvestigationCategorizedEntries;
41
+ };
42
+
43
+ export function buildConsultationCards({
44
+ t,
45
+ waitingCount,
46
+ emergencyCount,
47
+ urgentCount,
48
+ notUrgentCount,
49
+ awaitingCount,
50
+ completedCount,
51
+ labAwaiting,
52
+ labCompleted,
53
+ radiologyAwaiting,
54
+ radiologyCompleted,
55
+ proceduresAwaiting,
56
+ proceduresCompleted,
57
+ isRefreshing,
58
+ isLoadingInvestigations,
59
+ onRefreshInvestigations,
60
+ totalVisitsCount,
61
+ setFilters,
62
+ emergencyPriorityConceptUuid,
63
+ urgentPriorityConceptUuid,
64
+ notUrgentPriorityConceptUuid,
65
+ investigationCategorizedEntries,
66
+ }: BuildConsultationCardsParams): Array<QueueSummaryCard> {
67
+ return [
68
+ {
69
+ title: t('awaitingConsultation', 'Awaiting consultation'),
70
+ value: waitingCount.toString(),
71
+ categories: [
72
+ {
73
+ label: t('emergency', 'Emergency'),
74
+ value: emergencyCount,
75
+ onClick: () => {
76
+ setFilters((prevFilters) => [
77
+ ...prevFilters.filter((f) => f.key !== 'priority'),
78
+ { key: 'priority', value: emergencyPriorityConceptUuid, label: t('emergency', 'Emergency') },
79
+ ]);
80
+ },
81
+ },
82
+ {
83
+ label: t('urgent', 'Urgent'),
84
+ value: urgentCount,
85
+ onClick: () => {
86
+ setFilters((prevFilters) => [
87
+ ...prevFilters.filter((f) => f.key !== 'priority'),
88
+ { key: 'priority', value: urgentPriorityConceptUuid, label: t('urgent', 'Urgent') },
89
+ ]);
90
+ },
91
+ },
92
+ {
93
+ label: t('notUrgent', 'Not Urgent'),
94
+ value: notUrgentCount,
95
+ onClick: () => {
96
+ setFilters((prevFilters) => [
97
+ ...prevFilters.filter((f) => f.key !== 'priority'),
98
+ { key: 'priority', value: notUrgentPriorityConceptUuid, label: t('notUrgent', 'Not Urgent') },
99
+ ]);
100
+ },
101
+ },
102
+ ],
103
+ },
104
+ {
105
+ title: t('investigationAwaiting', 'Investigation Awaiting'),
106
+ value: awaitingCount.toString(),
107
+ categories: [
108
+ {
109
+ label: t('lab', 'Lab'),
110
+ value: labAwaiting,
111
+ onClick: () => {
112
+ setFilters((prevFilters) => [
113
+ ...prevFilters.filter((f) => f.key !== 'service_awaiting'),
114
+ {
115
+ key: 'service_awaiting',
116
+ value: investigationCategorizedEntries.newLabOrders.map((entry) => entry.patient.uuid).join(','),
117
+ label: t('labAwaiting', 'Lab Awaiting'),
118
+ },
119
+ ]);
120
+ },
121
+ },
122
+ {
123
+ label: t('radiology', 'Radiology'),
124
+ value: radiologyAwaiting,
125
+ onClick: () => {
126
+ setFilters((prevFilters) => [
127
+ ...prevFilters.filter((f) => f.key !== 'service_awaiting'),
128
+ {
129
+ key: 'service_awaiting',
130
+ value: investigationCategorizedEntries.newRadiologyOrders.map((entry) => entry.patient.uuid).join(','),
131
+ label: t('radiologyAwaiting', 'Radiology Awaiting'),
132
+ },
133
+ ]);
134
+ },
135
+ },
136
+ {
137
+ label: t('procedures', 'Procedures'),
138
+ value: proceduresAwaiting,
139
+ onClick: () => {
140
+ setFilters((prevFilters) => [
141
+ ...prevFilters.filter((f) => f.key !== 'service_awaiting'),
142
+ {
143
+ key: 'service_awaiting',
144
+ value: investigationCategorizedEntries.newProcedureOrders.map((entry) => entry.patient.uuid).join(','),
145
+ label: t('proceduresAwaiting', 'Procedures Awaiting'),
146
+ },
147
+ ]);
148
+ },
149
+ },
150
+ ],
151
+ refreshButton:
152
+ isRefreshing || isLoadingInvestigations ? (
153
+ <InlineLoading description={t('refreshing', 'Refreshing...')} />
154
+ ) : (
155
+ <IconButton
156
+ label={t('refreshInvestigations', 'Refresh investigations')}
157
+ kind="ghost"
158
+ size="sm"
159
+ onClick={onRefreshInvestigations}
160
+ className={styles.refreshButton}>
161
+ <Renew size={16} />
162
+ </IconButton>
163
+ ),
164
+ },
165
+ {
166
+ title: t('investigationCompleted', 'Investigation Completed'),
167
+ value: completedCount.toString(),
168
+ categories: [
169
+ {
170
+ label: t('lab', 'Lab'),
171
+ value: labCompleted,
172
+ onClick: () => {
173
+ setFilters((prevFilters) => [
174
+ ...prevFilters.filter((f) => f.key !== 'service_completed'),
175
+ {
176
+ key: 'service_completed',
177
+ value: investigationCategorizedEntries.completedLabOrders.map((entry) => entry.patient.uuid).join(','),
178
+ label: t('labCompleted', 'Lab Completed'),
179
+ },
180
+ ]);
181
+ },
182
+ },
183
+ {
184
+ label: t('radiology', 'Radiology'),
185
+ value: radiologyCompleted,
186
+ onClick: () => {
187
+ setFilters((prevFilters) => [
188
+ ...prevFilters.filter((f) => f.key !== 'service_completed'),
189
+ {
190
+ key: 'service_completed',
191
+ value: investigationCategorizedEntries.completedRadiologyOrders
192
+ .map((entry) => entry.patient.uuid)
193
+ .join(','),
194
+ label: t('radiologyCompleted', 'Radiology Completed'),
195
+ },
196
+ ]);
197
+ },
198
+ },
199
+ {
200
+ label: t('procedures', 'Procedures'),
201
+ value: proceduresCompleted,
202
+ onClick: () => {
203
+ setFilters((prevFilters) => [
204
+ ...prevFilters.filter((f) => f.key !== 'service_completed'),
205
+ {
206
+ key: 'service_completed',
207
+ value: investigationCategorizedEntries.completedProcedureOrders
208
+ .map((entry) => entry.patient.uuid)
209
+ .join(','),
210
+ label: t('proceduresCompleted', 'Procedures Completed'),
211
+ },
212
+ ]);
213
+ },
214
+ },
215
+ ],
216
+ },
217
+ {
218
+ title: t('totalVisits', 'Total Visits'),
219
+ value: totalVisitsCount?.toString() ?? '0',
220
+ },
221
+ ];
222
+ }