ui-soxo-bootstrap-core 2.6.1-dev.3 → 2.6.1-dev.31
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/core/components/extra-info/extra-info-details.js +2 -2
- package/core/components/index.js +2 -11
- package/core/components/landing-api/landing-api.js +216 -18
- package/core/components/landing-api/landing-api.scss +22 -0
- package/core/components/license-management/license-alert.js +97 -0
- package/core/lib/Store.js +8 -4
- package/core/lib/components/global-header/global-header.js +217 -242
- package/core/lib/components/index.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +19 -13
- package/core/lib/components/sidemenu/sidemenu.scss +1 -1
- package/core/lib/elements/basic/country-phone-input/country-phone-input.js +14 -9
- package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
- package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
- package/core/lib/models/forms/components/form-creator/form-creator.js +525 -468
- package/core/lib/models/forms/components/form-creator/form-creator.scss +30 -26
- package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
- package/core/lib/models/process/components/process-dashboard/process-dashboard.js +469 -3
- package/core/lib/models/process/components/process-dashboard/process-dashboard.scss +4 -0
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +28 -2
- package/core/lib/pages/change-password/change-password.js +17 -24
- package/core/lib/pages/change-password/change-password.scss +45 -48
- package/core/lib/pages/login/commnication-mode-selection.js +2 -2
- package/core/lib/pages/login/login.js +53 -64
- package/core/lib/pages/login/login.scss +9 -0
- package/core/lib/pages/login/reset-password.js +17 -17
- package/core/lib/pages/login/reset-password.scss +10 -1
- package/core/lib/pages/profile/themes.json +4 -4
- package/core/lib/utils/api/api.utils.js +53 -45
- package/core/lib/utils/common/common.utils.js +49 -35
- package/core/lib/utils/generic/generic.utils.js +2 -1
- package/core/lib/utils/http/http.utils.js +33 -4
- package/core/lib/utils/index.js +4 -1
- package/core/models/base/base.js +7 -3
- package/core/models/core-scripts/core-scripts.js +147 -126
- package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
- package/core/models/menus/components/menu-add/menu-add.js +1 -1
- package/core/models/menus/components/menu-lists/menu-lists.js +53 -54
- package/core/models/menus/menus.js +49 -2
- package/core/models/roles/components/role-add/role-add.js +92 -59
- package/core/models/roles/components/role-list/role-list.js +1 -1
- package/core/models/staff/components/staff-add/staff-add.js +20 -32
- package/core/models/users/components/assign-role/assign-role.js +145 -50
- package/core/models/users/components/assign-role/assign-role.scss +209 -45
- package/core/models/users/components/assign-role/avatar-props.js +45 -0
- package/core/models/users/components/user-add/user-add.js +46 -55
- package/core/models/users/components/user-add/user-edit.js +25 -4
- package/core/models/users/users.js +9 -1
- package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
- package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +174 -0
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +90 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +448 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +199 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +195 -822
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +43 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +517 -0
- package/core/modules/steps/action-buttons.js +30 -16
- package/core/modules/steps/action-buttons.scss +55 -9
- package/core/modules/steps/chat-assistant.js +141 -0
- package/core/modules/steps/openai-realtime.js +275 -0
- package/core/modules/steps/readme.md +167 -0
- package/core/modules/steps/steps.js +1286 -60
- package/core/modules/steps/steps.scss +703 -86
- package/core/modules/steps/timeline.js +21 -19
- package/core/modules/steps/voice-navigation.js +709 -0
- package/package.json +2 -1
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Table, Skeleton, Input,
|
|
3
|
+
import { Table, Skeleton, Input, Modal, message, Pagination, Tag } from 'antd';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import * as Icons from '@ant-design/icons';
|
|
8
|
-
|
|
9
|
-
import { Location, FormCreator, GlobalContext, ExportReactCSV, getExportData, Card, TableComponent, QrScanner } from './../../../../lib/';
|
|
5
|
+
import { Location, FormCreator, GlobalContext, Card } from './../../../../lib/';
|
|
10
6
|
|
|
11
7
|
import { CoreScripts } from './../../../../models/';
|
|
12
8
|
|
|
13
9
|
import moment from 'moment-timezone';
|
|
14
10
|
|
|
15
|
-
import Button from '../../../../lib/elements/basic/button/button';
|
|
16
|
-
|
|
17
|
-
import { Link } from 'react-router-dom';
|
|
18
|
-
|
|
19
11
|
import './reporting-dashboard.scss';
|
|
20
12
|
|
|
21
13
|
// import MenuDashBoard from '../../../../pages/homepage-api/menu-dashboard';
|
|
22
14
|
import MenuDashBoardComponent from '../../../../lib/elements/basic/menu-dashboard/menu-dashboard';
|
|
23
|
-
import { useHistory } from 'react-router-dom';
|
|
24
|
-
|
|
25
|
-
import * as ReportingDashboardComp from '../index';
|
|
26
|
-
|
|
27
|
-
// import { isPdfFile } from 'pdfjs-dist';
|
|
28
|
-
|
|
29
|
-
var genericComponents = require('./../../../../lib');
|
|
30
15
|
|
|
31
|
-
|
|
16
|
+
import ReportingTable from './reporting-table';
|
|
32
17
|
|
|
33
18
|
/**
|
|
34
19
|
* ReportingDashboard component renders the dashboard and handles patient details,
|
|
@@ -58,15 +43,20 @@ export default function ReportingDashboard({
|
|
|
58
43
|
const [config, setConfig] = useState({});
|
|
59
44
|
|
|
60
45
|
// State to manage the layout of the form
|
|
61
|
-
const [formLayout
|
|
46
|
+
const [formLayout] = useState('inline');
|
|
62
47
|
|
|
63
48
|
const [loading, setLoading] = useState(true);
|
|
64
49
|
|
|
65
50
|
const [cardLoading, setCardLoading] = useState(true);
|
|
66
51
|
|
|
52
|
+
const [searchParameters, setSearchParameters] = useState([]);
|
|
53
|
+
|
|
54
|
+
const [searchValues, setSearchValues] = useState({});
|
|
55
|
+
|
|
67
56
|
const [dashboardVisible, setDashBoardVisible] = useState(false);
|
|
68
57
|
|
|
69
58
|
const [formContents, setformContents] = useState({});
|
|
59
|
+
const [liveFormContents, setLiveFormContents] = useState({});
|
|
70
60
|
|
|
71
61
|
const scriptId = useRef(null);
|
|
72
62
|
|
|
@@ -80,9 +70,9 @@ export default function ReportingDashboard({
|
|
|
80
70
|
|
|
81
71
|
const [columns, setColumns] = useState([]); // To set columns
|
|
82
72
|
|
|
83
|
-
const [
|
|
73
|
+
const [, setSummaryColumns] = useState([]);
|
|
84
74
|
|
|
85
|
-
const [
|
|
75
|
+
const [reportRequestPayload, setReportRequestPayload] = useState(null);
|
|
86
76
|
|
|
87
77
|
const urlParams = Location.search();
|
|
88
78
|
|
|
@@ -103,20 +93,9 @@ export default function ReportingDashboard({
|
|
|
103
93
|
* @returns {Promise<void>} A promise that resolves when the patient details have been fetched and the state has been updated.
|
|
104
94
|
*/
|
|
105
95
|
async function getPatientDetails(idOverride) {
|
|
106
|
-
setPatients([]);
|
|
107
96
|
const fetchId = idOverride || id;
|
|
108
97
|
await CoreScripts.getRecord({ id: fetchId, dbPtr }).then(async ({ result }) => {
|
|
109
98
|
// Check if display columns are provided from backend
|
|
110
|
-
// if (result.display_columns) {
|
|
111
|
-
// // Parse and set columns from stored JSON
|
|
112
|
-
|
|
113
|
-
// setColumns(JSON.parse(result.display_columns));
|
|
114
|
-
// } else {
|
|
115
|
-
// console.log("ssssssssssssssssssss")
|
|
116
|
-
// // Reset columns if no display columns exist
|
|
117
|
-
|
|
118
|
-
// setColumns([]);
|
|
119
|
-
// }
|
|
120
99
|
let parsedColumns = [];
|
|
121
100
|
|
|
122
101
|
if (result.display_columns) {
|
|
@@ -124,7 +103,7 @@ export default function ReportingDashboard({
|
|
|
124
103
|
}
|
|
125
104
|
setColumns(parsedColumns);
|
|
126
105
|
|
|
127
|
-
await prepareInputParameters(result, parsedColumns);
|
|
106
|
+
await prepareInputParameters(result, parsedColumns, fetchId);
|
|
128
107
|
|
|
129
108
|
if (result.summary_columns) {
|
|
130
109
|
setSummaryColumns(JSON.parse(result.summary_columns));
|
|
@@ -169,7 +148,7 @@ export default function ReportingDashboard({
|
|
|
169
148
|
*/
|
|
170
149
|
|
|
171
150
|
//Prepare input parameters by mapping default values and binding models if needed
|
|
172
|
-
async function prepareInputParameters(record, parsedColumns) {
|
|
151
|
+
async function prepareInputParameters(record, parsedColumns, fetchId) {
|
|
173
152
|
setLoading(true);
|
|
174
153
|
let urlParams = Location.search();
|
|
175
154
|
|
|
@@ -178,9 +157,11 @@ export default function ReportingDashboard({
|
|
|
178
157
|
|
|
179
158
|
let otherDetails = record.other_details1 ? JSON.parse(record.other_details1) : null;
|
|
180
159
|
|
|
181
|
-
parameters = record.input_parameters ? JSON.parse(record.input_parameters) :
|
|
160
|
+
parameters = record.input_parameters ? JSON.parse(record.input_parameters) : [];
|
|
182
161
|
|
|
183
162
|
let formContent = {};
|
|
163
|
+
const searchFields = (parameters || []).filter((p) => p.type === 'search' && p.search_enabled === 'yes');
|
|
164
|
+
setSearchParameters([...searchFields]);
|
|
184
165
|
|
|
185
166
|
parameters = await parameters?.map((record) => {
|
|
186
167
|
// Only if the url params does have a matching value ,
|
|
@@ -224,11 +205,19 @@ export default function ReportingDashboard({
|
|
|
224
205
|
if (record.type === 'date' && !formContent[record.field]) {
|
|
225
206
|
formContent[record.field] = moment().tz(process.env.REACT_APP_TIMEZONE);
|
|
226
207
|
}
|
|
227
|
-
|
|
208
|
+
if (record.type === 'search') {
|
|
209
|
+
if (!formContent[record.field]) formContent[record.field] = [];
|
|
210
|
+
return {
|
|
211
|
+
...record,
|
|
212
|
+
reportId: fetchId,
|
|
213
|
+
onReset: () => getPatientDetails(fetchId),
|
|
214
|
+
required: record.required,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
228
217
|
if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
|
|
229
218
|
// let model = "";
|
|
230
219
|
let model = CustomModels[record.modelName];
|
|
231
|
-
|
|
220
|
+
|
|
232
221
|
return {
|
|
233
222
|
...record,
|
|
234
223
|
model,
|
|
@@ -243,6 +232,7 @@ export default function ReportingDashboard({
|
|
|
243
232
|
});
|
|
244
233
|
// Update form content state
|
|
245
234
|
setformContents(formContent);
|
|
235
|
+
setLiveFormContents(formContent);
|
|
246
236
|
|
|
247
237
|
// Trigger form submission
|
|
248
238
|
onFinish(formContent, null, record.input_parameters, parsedColumns);
|
|
@@ -254,130 +244,169 @@ export default function ReportingDashboard({
|
|
|
254
244
|
// If enabled, clear the details array
|
|
255
245
|
setDetails([]);
|
|
256
246
|
} else {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
let filter = parameters.filter((ele) => ele.type);
|
|
260
|
-
// Update the "details" state with the filtered results
|
|
261
|
-
|
|
262
|
-
setDetails([...filter]);
|
|
247
|
+
// Keep all parameters with a type (including search) to render in FormCreator
|
|
248
|
+
setDetails([...parameters.filter((ele) => ele.type)]);
|
|
263
249
|
}
|
|
264
250
|
}
|
|
265
251
|
|
|
266
252
|
// Refresh patient details.
|
|
267
253
|
|
|
268
254
|
function refresh() {
|
|
269
|
-
getPatientDetails();
|
|
255
|
+
getPatientDetails(scriptId.current || id);
|
|
270
256
|
}
|
|
271
257
|
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
const coreScriptId = scriptId.current ? scriptId.current : id;
|
|
258
|
+
const buildReportRequestPayload = (values = {}, paginationOverride) => {
|
|
259
|
+
const pager = paginationOverride || pagination;
|
|
260
|
+
const formattedValues = {};
|
|
276
261
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (val !== undefined) {
|
|
290
|
-
formattedValues[field] = moment.isMoment(val) ? val.format('YYYY-MM-DD') : val;
|
|
291
|
-
}
|
|
292
|
-
// field missing in values → set null
|
|
293
|
-
else {
|
|
294
|
-
formattedValues[field] = null;
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
// });
|
|
298
|
-
// Pagination Data
|
|
299
|
-
const paginationData = {
|
|
300
|
-
page: pagination.current || 1,
|
|
301
|
-
limit: pagination.pageSize || 10,
|
|
302
|
-
};
|
|
303
|
-
// Combine form data + pagination
|
|
304
|
-
let formBody = {
|
|
262
|
+
Object.keys(values || {}).forEach((key) => {
|
|
263
|
+
const val = values[key];
|
|
264
|
+
formattedValues[key] = moment.isMoment(val) ? val.format('YYYY-MM-DD') : val;
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const paginationData = {
|
|
268
|
+
page: pager.current || 1,
|
|
269
|
+
limit: pager.pageSize || 10,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
if (scope) {
|
|
273
|
+
return {
|
|
305
274
|
body: {
|
|
306
|
-
...
|
|
275
|
+
...scope,
|
|
307
276
|
...paginationData,
|
|
308
277
|
},
|
|
309
278
|
};
|
|
310
|
-
|
|
311
|
-
if (scope) {
|
|
312
|
-
formBody = { body: { ...scope, ...paginationData } };
|
|
313
|
-
}
|
|
279
|
+
}
|
|
314
280
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
281
|
+
return {
|
|
282
|
+
body: {
|
|
283
|
+
...formattedValues,
|
|
284
|
+
...paginationData,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const handleSubmit = (values) => {
|
|
290
|
+
// Extract search fields from the form values
|
|
291
|
+
const searchKeys = searchParameters.map((p) => p.field);
|
|
292
|
+
const currentSearchValues = {};
|
|
293
|
+
const formValues = {};
|
|
294
|
+
|
|
295
|
+
Object.keys(values).forEach((key) => {
|
|
296
|
+
if (searchKeys.includes(key)) {
|
|
297
|
+
currentSearchValues[key] = values[key];
|
|
298
|
+
} else {
|
|
299
|
+
formValues[key] = values[key];
|
|
334
300
|
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const hasSearchValues = Object.values(currentSearchValues).some((v) => Array.isArray(v) && v.length > 0);
|
|
335
304
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
305
|
+
if (!hasSearchValues) {
|
|
306
|
+
runSubmit(formValues);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
339
309
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
310
|
+
// Check if main form values (non-search) changed
|
|
311
|
+
const formChanged = Object.keys(formValues).some((key) => {
|
|
312
|
+
const newVal = formValues[key];
|
|
313
|
+
const oldVal = formContents[key];
|
|
314
|
+
|
|
315
|
+
if (moment.isMoment(newVal) && moment.isMoment(oldVal)) {
|
|
316
|
+
return !newVal.isSame(oldVal, 'day');
|
|
346
317
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
318
|
+
|
|
319
|
+
return newVal !== oldVal;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (formChanged) {
|
|
323
|
+
Modal.confirm({
|
|
324
|
+
title: 'Filters changed',
|
|
325
|
+
content: 'You changed some filters. Do you want to search using these filters also?',
|
|
326
|
+
okText: 'Yes',
|
|
327
|
+
cancelText: 'No',
|
|
328
|
+
|
|
329
|
+
onOk() {
|
|
330
|
+
// YES → send form values + search condition
|
|
331
|
+
const finalValues = {
|
|
332
|
+
...formValues,
|
|
333
|
+
search_values: currentSearchValues,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
runSubmit(finalValues);
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
onCancel() {
|
|
340
|
+
// NO → reset form values
|
|
341
|
+
const resetValues = {};
|
|
342
|
+
Object.keys(formValues).forEach((key) => {
|
|
343
|
+
resetValues[key] = null;
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
const finalValues = {
|
|
347
|
+
...resetValues,
|
|
348
|
+
search_values: currentSearchValues,
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
runSubmit(finalValues);
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
// no form change
|
|
356
|
+
const resetValues = {};
|
|
357
|
+
Object.keys(formValues).forEach((key) => {
|
|
358
|
+
resetValues[key] = null;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const finalValues = {
|
|
362
|
+
...resetValues,
|
|
363
|
+
search_values: currentSearchValues,
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
runSubmit(finalValues);
|
|
353
367
|
}
|
|
354
368
|
};
|
|
355
369
|
|
|
356
|
-
|
|
357
|
-
const handleSubmit = (values) => {
|
|
370
|
+
const runSubmit = (finalValues) => {
|
|
358
371
|
const { pageSize } = pagination;
|
|
359
372
|
const resetPage = 1;
|
|
360
373
|
|
|
361
|
-
// Reset script id on Submit
|
|
362
374
|
scriptId.current = null;
|
|
363
375
|
|
|
364
|
-
|
|
365
|
-
const currentUrlParams = Location.search();
|
|
366
|
-
const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
376
|
+
const hasSearchValues = finalValues.search_values && Object.values(finalValues.search_values).some((v) => Array.isArray(v) && v.length > 0);
|
|
367
377
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
...cleanParams
|
|
371
|
-
current: resetPage,
|
|
372
|
-
pageSize,
|
|
373
|
-
});
|
|
378
|
+
if (!hasSearchValues) {
|
|
379
|
+
const currentUrlParams = Location.search();
|
|
380
|
+
const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
374
381
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
382
|
+
const newParams = new URLSearchParams({
|
|
383
|
+
...cleanParams,
|
|
384
|
+
current: resetPage,
|
|
385
|
+
pageSize,
|
|
386
|
+
});
|
|
378
387
|
|
|
379
|
-
|
|
380
|
-
|
|
388
|
+
const newUrl = `${window.location.pathname}?${newParams.toString()}`;
|
|
389
|
+
window.history.replaceState({}, '', newUrl);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
onFinish(finalValues, resetPage);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const selectedSearchFields = details.filter((field) => {
|
|
396
|
+
if (field.type !== 'search') return false;
|
|
397
|
+
|
|
398
|
+
const value = liveFormContents[field.field];
|
|
399
|
+
return Array.isArray(value) ? value.length > 0 : !!value;
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const handleRemoveSearchField = (fieldName) => {
|
|
403
|
+
const updatedValues = {
|
|
404
|
+
...liveFormContents,
|
|
405
|
+
[fieldName]: [],
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
setLiveFormContents(updatedValues);
|
|
409
|
+
handleSubmit(updatedValues);
|
|
381
410
|
};
|
|
382
411
|
|
|
383
412
|
/**
|
|
@@ -412,6 +441,12 @@ export default function ReportingDashboard({
|
|
|
412
441
|
//Getting url friendly value by matching the url values and input_parameter values
|
|
413
442
|
let value = values[parameter.field];
|
|
414
443
|
|
|
444
|
+
// If the value is missing at the root level (which happens when search fields are moved
|
|
445
|
+
// into search_values during submission), try to retrieve it from the nested object.
|
|
446
|
+
if (value === undefined && values.search_values && values.search_values[parameter.field] !== undefined) {
|
|
447
|
+
value = values.search_values[parameter.field];
|
|
448
|
+
}
|
|
449
|
+
|
|
415
450
|
// Keep Moment object in state for picker
|
|
416
451
|
if (parameter.type === 'date' && value) {
|
|
417
452
|
formContent[parameter.field] = value.isValid ? value : moment(value); // ensure Moment
|
|
@@ -432,7 +467,8 @@ export default function ReportingDashboard({
|
|
|
432
467
|
Object.entries(urlsToUpdate).filter(([_, value]) => value !== undefined && value !== null && value !== '')
|
|
433
468
|
);
|
|
434
469
|
|
|
435
|
-
setformContents(
|
|
470
|
+
setformContents(formContent);
|
|
471
|
+
setLiveFormContents(formContent);
|
|
436
472
|
Location.search({ ...Location.search(), ...filteredParams });
|
|
437
473
|
}
|
|
438
474
|
|
|
@@ -445,17 +481,12 @@ export default function ReportingDashboard({
|
|
|
445
481
|
|
|
446
482
|
// Call API
|
|
447
483
|
try {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
// Pagination Handler
|
|
456
|
-
const handlePagination = async (newPagination) => {
|
|
457
|
-
try {
|
|
458
|
-
await fetchReportData(id, formContents, dbPtr, newPagination);
|
|
484
|
+
setPagination((prev) => ({
|
|
485
|
+
...prev,
|
|
486
|
+
current: paginationData.current,
|
|
487
|
+
pageSize: paginationData.pageSize,
|
|
488
|
+
}));
|
|
489
|
+
setReportRequestPayload(buildReportRequestPayload(values, paginationData));
|
|
459
490
|
} finally {
|
|
460
491
|
setLoading(false);
|
|
461
492
|
setCardLoading(false);
|
|
@@ -470,23 +501,6 @@ export default function ReportingDashboard({
|
|
|
470
501
|
<Card className="reporting-dashboard card card-shadow">
|
|
471
502
|
{/** If dashBoardIds exist and contain elements, render MenuDashBoard*/}
|
|
472
503
|
|
|
473
|
-
{/* Page Header */}
|
|
474
|
-
{/* <div className="page-header">
|
|
475
|
-
<div>
|
|
476
|
-
<Title style={{ marginBottom: '0px' }} level={4}>
|
|
477
|
-
{config.caption || 'Report'}
|
|
478
|
-
</Title>
|
|
479
|
-
</div>
|
|
480
|
-
|
|
481
|
-
<div className="right">
|
|
482
|
-
<div className="date-and-fltr">
|
|
483
|
-
<Button onClick={refresh} type="secondary" size={'small'}>
|
|
484
|
-
<ReloadOutlined />
|
|
485
|
-
</Button>
|
|
486
|
-
</div>
|
|
487
|
-
</div>
|
|
488
|
-
</div> */}
|
|
489
|
-
{/* Page Header Ends */}
|
|
490
504
|
{dashBoardIds?.length > 0 ? (
|
|
491
505
|
<MenuDashBoardComponent
|
|
492
506
|
dashBoardIds={dashBoardIds} //Pass the available dashboard IDs to the componen
|
|
@@ -545,691 +559,50 @@ export default function ReportingDashboard({
|
|
|
545
559
|
}}
|
|
546
560
|
styles={{ paddingRight: '15px', alignItems: 'center' }}
|
|
547
561
|
fields={details}
|
|
562
|
+
reportId={id}
|
|
548
563
|
formContent={formContents}
|
|
549
564
|
// formContent={{ [model]: {} }}
|
|
550
565
|
modelIndex="requestId"
|
|
551
566
|
model={model}
|
|
552
567
|
onSubmit={handleSubmit}
|
|
568
|
+
onFormValuesChange={setLiveFormContents}
|
|
553
569
|
callback={() => {
|
|
554
570
|
// history.goBack();
|
|
555
571
|
}}
|
|
556
572
|
/>
|
|
557
573
|
) : null}
|
|
558
|
-
{/* </Card> */}
|
|
559
574
|
</div>
|
|
560
575
|
|
|
561
|
-
|
|
562
|
-
<GuestList
|
|
563
|
-
patients={patients}
|
|
576
|
+
<ReportingTable
|
|
564
577
|
columns={columns}
|
|
565
|
-
summaryColumns={summaryColumns}
|
|
566
578
|
isFixedIndex={isFixedIndex}
|
|
567
579
|
showScanner={showScanner}
|
|
580
|
+
reportId={reportId}
|
|
581
|
+
requestId={scriptId.current ? scriptId.current : id}
|
|
582
|
+
requestPayload={reportRequestPayload}
|
|
583
|
+
dbPtr={dbPtr}
|
|
568
584
|
barcodeFilterKey={barcodeFilterKey}
|
|
569
|
-
CustomComponents={
|
|
585
|
+
CustomComponents={CustomComponents}
|
|
570
586
|
refresh={refresh}
|
|
571
587
|
config={config}
|
|
588
|
+
|
|
572
589
|
loading={cardLoading}
|
|
573
590
|
pagination={pagination}
|
|
574
|
-
|
|
591
|
+
onPaginationChange={(nextPagination) => {
|
|
592
|
+
Location.search({
|
|
593
|
+
...Location.search(),
|
|
594
|
+
current: nextPagination.current,
|
|
595
|
+
pageSize: nextPagination.pageSize,
|
|
596
|
+
});
|
|
597
|
+
setPagination((prev) => ({
|
|
598
|
+
...prev,
|
|
599
|
+
...nextPagination,
|
|
600
|
+
}));
|
|
601
|
+
}}
|
|
575
602
|
attributes={attributes}
|
|
576
|
-
fetchReportData={(paginationUpdate) => fetchReportData(id, formContents, dbPtr, paginationUpdate || pagination)}
|
|
577
603
|
/>
|
|
578
|
-
{/** GuestList component end*/}
|
|
579
604
|
</>
|
|
580
605
|
)}
|
|
581
606
|
</Card>
|
|
582
607
|
);
|
|
583
608
|
}
|
|
584
|
-
|
|
585
|
-
/**
|
|
586
|
-
*
|
|
587
|
-
* @param root0
|
|
588
|
-
* @param root0.patients
|
|
589
|
-
* @param root0.CustomComponents
|
|
590
|
-
* @param root0.summaryColumns
|
|
591
|
-
* @param root0.refresh
|
|
592
|
-
* @param root0.isFixedIndex
|
|
593
|
-
* @returns {*}
|
|
594
|
-
*/
|
|
595
|
-
//Renders a table displaying a list of patients with dynamic columns
|
|
596
|
-
function GuestList({
|
|
597
|
-
patients,
|
|
598
|
-
columns,
|
|
599
|
-
loading,
|
|
600
|
-
CustomComponents,
|
|
601
|
-
refresh,
|
|
602
|
-
isFixedIndex,
|
|
603
|
-
barcodeFilterKey,
|
|
604
|
-
showScanner,
|
|
605
|
-
config,
|
|
606
|
-
pagination,
|
|
607
|
-
handlePagination,
|
|
608
|
-
attributes,
|
|
609
|
-
fetchReportData,
|
|
610
|
-
}) {
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* @param {*} propValues
|
|
614
|
-
*/
|
|
615
|
-
const propValues = (attributes && JSON.parse(attributes)) || {};
|
|
616
|
-
|
|
617
|
-
const { buttonAttributes = [] } = propValues;
|
|
618
|
-
|
|
619
|
-
var [query, setQuery] = useState('');
|
|
620
|
-
|
|
621
|
-
const [exportData, setExportData] = useState({});
|
|
622
|
-
|
|
623
|
-
// const [data, setData] = useState([]);
|
|
624
|
-
|
|
625
|
-
//visibility of the QR scanner modal.
|
|
626
|
-
const [isScannerVisible, setScannerVisible] = useState(false);
|
|
627
|
-
|
|
628
|
-
// Stores the patients filtered specifically by QR scan match.
|
|
629
|
-
const [filteredPatients, setFilteredPatients] = useState([]); // Show all initially
|
|
630
|
-
|
|
631
|
-
// patient object to redirect to upon successful QR scan.
|
|
632
|
-
const [redirectPatient, setRedirectPatient] = useState(null);
|
|
633
|
-
|
|
634
|
-
const [visible, setVisible] = useState(false);
|
|
635
|
-
|
|
636
|
-
const [ActiveComponent, setActiveComponent] = useState(null);
|
|
637
|
-
|
|
638
|
-
let history = useHistory();
|
|
639
|
-
|
|
640
|
-
const { isMobile, dispatch } = useContext(GlobalContext);
|
|
641
|
-
const [single, setSingle] = useState({});
|
|
642
|
-
|
|
643
|
-
const getRedirectLink = (entry, record) => {
|
|
644
|
-
let redirectLink = entry.redirect_link;
|
|
645
|
-
if (entry.replace_variables) {
|
|
646
|
-
entry.replace_variables.forEach((replacement) => {
|
|
647
|
-
const value = record[replacement.field] || '';
|
|
648
|
-
redirectLink = redirectLink.replace(new RegExp(`@${replacement.field};`, 'g'), value);
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
return redirectLink;
|
|
652
|
-
};
|
|
653
|
-
|
|
654
|
-
// const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
|
|
655
|
-
const cols = [
|
|
656
|
-
...[
|
|
657
|
-
{
|
|
658
|
-
title: '#',
|
|
659
|
-
dataIndex: 'index',
|
|
660
|
-
key: 'ColumnIndex',
|
|
661
|
-
width: 60,
|
|
662
|
-
render: (value, item, index) => index + 1,
|
|
663
|
-
key: 'ColumnIndex',
|
|
664
|
-
fixed: isFixedIndex ? 'left' : null,
|
|
665
|
-
},
|
|
666
|
-
],
|
|
667
|
-
...columns.map((entry) => {
|
|
668
|
-
// if (entry.sort) {
|
|
669
|
-
// return {
|
|
670
|
-
// render: (record) => {
|
|
671
|
-
|
|
672
|
-
// if (entry.render) {
|
|
673
|
-
|
|
674
|
-
// return entry.render(record);
|
|
675
|
-
// } else {
|
|
676
|
-
// return record[entry.dataIndex]
|
|
677
|
-
// }
|
|
678
|
-
// },
|
|
679
|
-
// title: entry.title,
|
|
680
|
-
// key: entry.field,
|
|
681
|
-
// sorter: (a, b) => entry.sort(a, b),
|
|
682
|
-
// sortDirections: ['ascend', 'descend', 'ascend'],
|
|
683
|
-
// };
|
|
684
|
-
// } else {
|
|
685
|
-
return {
|
|
686
|
-
render: (record) => {
|
|
687
|
-
let textColor = 'inherit';
|
|
688
|
-
|
|
689
|
-
if (entry.color) {
|
|
690
|
-
textColor = entry.color;
|
|
691
|
-
}
|
|
692
|
-
/** We can have x types of components that is to be rendered here */
|
|
693
|
-
|
|
694
|
-
/**1. Column Data */
|
|
695
|
-
|
|
696
|
-
/**2. Action */
|
|
697
|
-
|
|
698
|
-
/**3. Custom Component - In future . */
|
|
699
|
-
|
|
700
|
-
if (entry.render) {
|
|
701
|
-
return entry.render(record);
|
|
702
|
-
|
|
703
|
-
// The type of component can be differentiated by type/ we can also reuse
|
|
704
|
-
// any field present in the columns to avoid any additional field
|
|
705
|
-
} else if (entry.type === 'link') {
|
|
706
|
-
//Cheacking type of action to be done ie,If it contains a type then it will match with the record
|
|
707
|
-
// for example initally we are implementing it for a type link
|
|
708
|
-
// ie, we will return a link in query with the field as link and type link in display_columns
|
|
709
|
-
//So here we match the field returned by query with the type in display_columns
|
|
710
|
-
|
|
711
|
-
if (record[entry.field]) {
|
|
712
|
-
return (
|
|
713
|
-
<a href={record[entry.field]} target="_blank" rel="noopener noreferrer">
|
|
714
|
-
View
|
|
715
|
-
</a>
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
} else if (entry.field === 'action') {
|
|
719
|
-
let redirectLink = entry.redirect_link;
|
|
720
|
-
|
|
721
|
-
// The variables to be replaced can be maintained
|
|
722
|
-
// as a configuration in the entry or the column configuration
|
|
723
|
-
|
|
724
|
-
// We iterate through all the variables that are present in the configuration
|
|
725
|
-
// and replace the link to generate the final link
|
|
726
|
-
|
|
727
|
-
entry.replace_variables.forEach((replacement) => {
|
|
728
|
-
redirectLink = redirectLink.replace(new RegExp('@' + replacement.field + ';', 'g'), record[replacement.field]);
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
return <Link to={`${redirectLink}`}>{entry.display_name_link ? entry.display_name_link : 'View'}</Link>;
|
|
732
|
-
} else if (entry.field === 'custom') {
|
|
733
|
-
// Make all the components in modules available for use in custom column of core script
|
|
734
|
-
// var genericComponents = require('./../../../../../../../nura-api-new/nura-desk/src/modules');
|
|
735
|
-
var genericComponents = CustomComponents;
|
|
736
|
-
|
|
737
|
-
// Arrive the component name
|
|
738
|
-
let componentName = entry.component;
|
|
739
|
-
|
|
740
|
-
let LoadedComponent = null;
|
|
741
|
-
// If there is custom components mensioned in the display_columns,
|
|
742
|
-
// then matching the custom components with the generic components
|
|
743
|
-
if (componentName) {
|
|
744
|
-
if (componentName && genericComponents[componentName]) {
|
|
745
|
-
LoadedComponent = genericComponents[componentName];
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
let propValue = {};
|
|
750
|
-
// If there is props value
|
|
751
|
-
if (entry.props) {
|
|
752
|
-
// Looping the props from the props mensioned in display_columns,
|
|
753
|
-
// Matching the field of record props field with the and return the
|
|
754
|
-
entry.props.forEach((values) => {
|
|
755
|
-
// Matching the fields of record with the props field
|
|
756
|
-
let valueCreation = record[values.field];
|
|
757
|
-
// Returning the values of field matched by fields
|
|
758
|
-
propValue[values.value] = valueCreation;
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
return (
|
|
763
|
-
<LoadedComponent
|
|
764
|
-
// Configuration will define
|
|
765
|
-
{...entry.config}
|
|
766
|
-
callback={() => {
|
|
767
|
-
refresh();
|
|
768
|
-
}}
|
|
769
|
-
// record={record}
|
|
770
|
-
|
|
771
|
-
{...record}
|
|
772
|
-
// assigning the props
|
|
773
|
-
{...propValue}
|
|
774
|
-
/>
|
|
775
|
-
);
|
|
776
|
-
} else {
|
|
777
|
-
// Check if both `color_code` exists in the record and `enableColor` is true
|
|
778
|
-
if (record.color_code && entry.enableColor) {
|
|
779
|
-
// If the column type is 'tag', render the field inside an Ant Design <Tag> with color
|
|
780
|
-
if (entry.columnType === 'tag') {
|
|
781
|
-
return <Tag color={record.color_code}>{record[entry.field]}</Tag>;
|
|
782
|
-
|
|
783
|
-
// If the column type is 'span', render the field inside a <span> with inline color style
|
|
784
|
-
} else if (entry.columnType === 'span') {
|
|
785
|
-
return <span style={{ color: record.color_code, overflowWrap: 'break-word', WebkitLineClamp: 3 }}>{record[entry.field]}</span>;
|
|
786
|
-
}
|
|
787
|
-
} else {
|
|
788
|
-
/**
|
|
789
|
-
* This code dynamically displays icons based on data and a configuration
|
|
790
|
-
* When a column's configuration includes a displayIcons JSON, the code will check the corresponding data field.
|
|
791
|
-
* If the field's value (e.g., "Pending") matches a key in that JSON,
|
|
792
|
-
* the system will display the specified icon with its defined color and size.
|
|
793
|
-
*/
|
|
794
|
-
if (entry.displayIcons) {
|
|
795
|
-
// Get the field value
|
|
796
|
-
const fieldValue = record[entry.field]?.toString();
|
|
797
|
-
|
|
798
|
-
// Get the configuration for the icon from the JSON.
|
|
799
|
-
const displayConfig = entry.displayIcons[fieldValue];
|
|
800
|
-
if (displayConfig) {
|
|
801
|
-
// Look up the actual component from our iconMap using the name from the JSON.
|
|
802
|
-
const DynamicIcon = Icons[displayConfig.icon];
|
|
803
|
-
// If a component is found, render it with the specified color and size.
|
|
804
|
-
if (DynamicIcon) {
|
|
805
|
-
return (
|
|
806
|
-
<DynamicIcon
|
|
807
|
-
style={{
|
|
808
|
-
color: displayConfig.color,
|
|
809
|
-
fontSize: displayConfig.size,
|
|
810
|
-
}}
|
|
811
|
-
/>
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
} else {
|
|
816
|
-
//If the value is neither 'Y' nor 'N', return the actual field value
|
|
817
|
-
return <span style={{ color: textColor, whiteSpace: 'pre-wrap', overflowWrap: 'break-word' }}>{record[entry.field]}</span>;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
},
|
|
822
|
-
field: entry.field,
|
|
823
|
-
// title: entry.title,
|
|
824
|
-
// title: (
|
|
825
|
-
// <Tooltip title={entry.title}>
|
|
826
|
-
// {entry.title}
|
|
827
|
-
// </Tooltip>
|
|
828
|
-
// ),
|
|
829
|
-
title: (
|
|
830
|
-
<Tooltip title={entry.tooltip || entry.title}>
|
|
831
|
-
<span>{entry.title}</span>
|
|
832
|
-
</Tooltip>
|
|
833
|
-
),
|
|
834
|
-
key: entry.field,
|
|
835
|
-
width: entry.width ? parseInt(entry.width) : 160,
|
|
836
|
-
fixed: entry.isFixedColumn ? entry.isFixedColumn : null, // Conditionally setting the 'fixed' key to 'left' if 'isColumnStatic' is true; otherwise, setting it to null.
|
|
837
|
-
// Check if filtering is enabled and patients is an array
|
|
838
|
-
filters:
|
|
839
|
-
entry.isFilterEnabled && Array.isArray(patients)
|
|
840
|
-
? [...new Set(patients.map((item) => item[entry.field]).filter(Boolean))].map((value) => ({ text: value, value }))
|
|
841
|
-
: null,
|
|
842
|
-
// Apply the filter only if it's enabled for the column
|
|
843
|
-
onFilter: entry.isFilterEnabled ? (value, record) => record[entry.field] === value : null,
|
|
844
|
-
//If sorting is enabled for this entry, provide a sorter function
|
|
845
|
-
sorter: entry.isSortingEnabled ? (a, b) => String(a[entry.field]).localeCompare(String(b[entry.field])) : null,
|
|
846
|
-
|
|
847
|
-
// Apply the filter search
|
|
848
|
-
filterSearch: entry.isFilterEnabled ? entry.isFilterEnabled : false,
|
|
849
|
-
exportDefinition: (record) => {
|
|
850
|
-
//Custom components should not be downloaded
|
|
851
|
-
// return entry.field === 'custom' ? null : record[entry.field];
|
|
852
|
-
if (entry.field === 'custom') {
|
|
853
|
-
// Find the field key in props that corresponds to 'description'
|
|
854
|
-
const description = entry.props?.find((p) => p.value === 'description')?.field;
|
|
855
|
-
|
|
856
|
-
// Return the value from record.props if it exists
|
|
857
|
-
return description && record[description] ? record[description] : null;
|
|
858
|
-
}
|
|
859
|
-
return record[entry.field];
|
|
860
|
-
},
|
|
861
|
-
// Add align property based on column type
|
|
862
|
-
align: entry.type === 'number' ? 'right' : 'left',
|
|
863
|
-
};
|
|
864
|
-
// }
|
|
865
|
-
}),
|
|
866
|
-
// ...[
|
|
867
|
-
// {
|
|
868
|
-
// title: '',
|
|
869
|
-
// key: 'action',
|
|
870
|
-
// render: (text, record) => {
|
|
871
|
-
// let detail = model.slice(0, model.length - 1);
|
|
872
|
-
|
|
873
|
-
// return (
|
|
874
|
-
// <Space size="middle">
|
|
875
|
-
// {!schema.hideView && !actions.length ? <Link to={`/${city}/${model}/${text.id}`}>View</Link> : null}
|
|
876
|
-
|
|
877
|
-
// {actions.map((action) => (
|
|
878
|
-
// <Link to={action.url(record)}>{action.caption}</Link>
|
|
879
|
-
// ))}
|
|
880
|
-
// </Space>
|
|
881
|
-
// );
|
|
882
|
-
// },
|
|
883
|
-
// },
|
|
884
|
-
// ],
|
|
885
|
-
|
|
886
|
-
// ...[
|
|
887
|
-
// {
|
|
888
|
-
// title: '',
|
|
889
|
-
// key: 'action',
|
|
890
|
-
// render: (text, record) => {
|
|
891
|
-
// let detail = model.slice(0, model.length - 1);
|
|
892
|
-
|
|
893
|
-
// return (
|
|
894
|
-
// <Link to={`${menu.path.replace(':id',visitid)}?op_no=${OpNo}`}>
|
|
895
|
-
// <Button size={'small'}>
|
|
896
|
-
// <PlayCircleOutlined />
|
|
897
|
-
// Go to Menu
|
|
898
|
-
// </Button>
|
|
899
|
-
// </Link>
|
|
900
|
-
// );
|
|
901
|
-
// },
|
|
902
|
-
// },
|
|
903
|
-
// ],
|
|
904
|
-
];
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
*
|
|
908
|
-
* @param {*} result
|
|
909
|
-
*/
|
|
910
|
-
|
|
911
|
-
// function changeView(result) {
|
|
912
|
-
// setView(result);
|
|
913
|
-
// }
|
|
914
|
-
|
|
915
|
-
/**
|
|
916
|
-
*
|
|
917
|
-
* @param {*} event
|
|
918
|
-
*/
|
|
919
|
-
|
|
920
|
-
function onSearch(event) {
|
|
921
|
-
setQuery(event.target.value);
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
/**
|
|
925
|
-
*
|
|
926
|
-
*/
|
|
927
|
-
|
|
928
|
-
useEffect(() => {
|
|
929
|
-
//Cheaking if there is patient data exists
|
|
930
|
-
if (patients) {
|
|
931
|
-
// Commented due to a production issue:
|
|
932
|
-
// When displayColumns is not defined, rowIndex and dispatch fields were getting displayed in the table.
|
|
933
|
-
// Also, the `data` variable is not used in the code below, and the setData state is already commented out.
|
|
934
|
-
|
|
935
|
-
// let data = patients?.map((entry) => {
|
|
936
|
-
// entry.rowIndex = entry.opb_id;
|
|
937
|
-
|
|
938
|
-
// entry.dispatch = dispatch;
|
|
939
|
-
|
|
940
|
-
// return entry;
|
|
941
|
-
// });
|
|
942
|
-
|
|
943
|
-
// setData(data);
|
|
944
|
-
|
|
945
|
-
// Define export data
|
|
946
|
-
// Sanitize cols for export to ensure titles are strings
|
|
947
|
-
const exportCols = cols.map((col) => {
|
|
948
|
-
if (col.title && typeof col.title === 'object' && col.title.props) {
|
|
949
|
-
return { ...col, title: col.title.props.title };
|
|
950
|
-
}
|
|
951
|
-
return col;
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
let exportDatas = getExportData(patients, exportCols);
|
|
955
|
-
|
|
956
|
-
if (exportDatas.exportDataColumns.length && exportDatas.exportDataHeaders.length) {
|
|
957
|
-
setExportData({ exportDatas });
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
}, [patients, columns]);
|
|
961
|
-
|
|
962
|
-
let filtered;
|
|
963
|
-
|
|
964
|
-
if (patients) {
|
|
965
|
-
filtered = patients.filter((record) => {
|
|
966
|
-
if (query) {
|
|
967
|
-
// Keys
|
|
968
|
-
let keys = Object.keys(record);
|
|
969
|
-
|
|
970
|
-
let flag = false;
|
|
971
|
-
|
|
972
|
-
keys.forEach((key) => {
|
|
973
|
-
let ele = record[key];
|
|
974
|
-
|
|
975
|
-
if (ele && typeof ele === 'string' && ele.toLowerCase().indexOf(query.toLowerCase()) !== -1) {
|
|
976
|
-
flag = true;
|
|
977
|
-
}
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
/**Will return flag */
|
|
981
|
-
return flag;
|
|
982
|
-
} else {
|
|
983
|
-
return true;
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
/**
|
|
989
|
-
* Checks for a match in the filtered patient list based on a scanned code,
|
|
990
|
-
* updates the relevant state if a match is found, and redirects to the
|
|
991
|
-
* patient's detail page. Displays a warning if no match is found.
|
|
992
|
-
*
|
|
993
|
-
* @param {string} code - The scanned code to match against a specific field of each patient.
|
|
994
|
-
*/
|
|
995
|
-
|
|
996
|
-
const handleScanSuccess = (code) => {
|
|
997
|
-
// Filters patients based on the scanned code and the selected barcode key(using attributes 'barcodeFilterKey')
|
|
998
|
-
const matched = filtered.filter((patient) => patient[barcodeFilterKey] === code);
|
|
999
|
-
|
|
1000
|
-
if (matched.length) {
|
|
1001
|
-
const patient = matched[0];
|
|
1002
|
-
setFilteredPatients(matched);
|
|
1003
|
-
setRedirectPatient(matched);
|
|
1004
|
-
message.success(`Match found for ${code}, redirecting...`);
|
|
1005
|
-
|
|
1006
|
-
const actionColumn = columns.find((col) => col.field === 'action');
|
|
1007
|
-
if (actionColumn) {
|
|
1008
|
-
const redirectLink = getRedirectLink(actionColumn, patient);
|
|
1009
|
-
// history.push(redirectLink);
|
|
1010
|
-
window.location.href = redirectLink;
|
|
1011
|
-
}
|
|
1012
|
-
} else {
|
|
1013
|
-
Modal.warning({
|
|
1014
|
-
title: 'No matching records.',
|
|
1015
|
-
content: `No match for scanned code: ${code}`,
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1018
|
-
};
|
|
1019
|
-
|
|
1020
|
-
//open the edit modal
|
|
1021
|
-
const handleOpenEdit = (button) => {
|
|
1022
|
-
const componentName = button.component;
|
|
1023
|
-
const ComponentToRender = ReportingDashboardComp[componentName];
|
|
1024
|
-
|
|
1025
|
-
if (!ComponentToRender) {
|
|
1026
|
-
console.error(`Component ${componentName} not found!`);
|
|
1027
|
-
return;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
setSingle({});
|
|
1031
|
-
setActiveComponent(() => ComponentToRender);
|
|
1032
|
-
setVisible(true);
|
|
1033
|
-
};
|
|
1034
|
-
|
|
1035
|
-
// close the edit modal
|
|
1036
|
-
const handleCloseEdit = () => {
|
|
1037
|
-
setShowEdit(false);
|
|
1038
|
-
};
|
|
1039
|
-
return (
|
|
1040
|
-
<>
|
|
1041
|
-
<div className="table-header">
|
|
1042
|
-
<div className="table-left">
|
|
1043
|
-
{/* shwoing caption is not correct so this commented */}
|
|
1044
|
-
{/* <span className="menu-caption">{config.caption}</span> */}
|
|
1045
|
-
<Search placeholder="Enter Search Value" allowClear onChange={onSearch} />
|
|
1046
|
-
{/* <p className="size-hint">{patients.length} records.</p> */}
|
|
1047
|
-
</div>
|
|
1048
|
-
|
|
1049
|
-
<div className="table-right">
|
|
1050
|
-
{/* QR Scan start */}
|
|
1051
|
-
{showScanner ? (
|
|
1052
|
-
<Button size="small" type="primary" icon={<QrcodeOutlined />} onClick={() => setScannerVisible(true)}>
|
|
1053
|
-
Scan QR
|
|
1054
|
-
</Button>
|
|
1055
|
-
) : null}
|
|
1056
|
-
{/** Add User button */}
|
|
1057
|
-
{Array.isArray(buttonAttributes) &&
|
|
1058
|
-
buttonAttributes.map((btn, index) => (
|
|
1059
|
-
<Button key={index} size="small" type="primary" style={{ marginLeft: 8 }} onClick={() => handleOpenEdit(btn)}>
|
|
1060
|
-
{btn.title}
|
|
1061
|
-
</Button>
|
|
1062
|
-
))}
|
|
1063
|
-
|
|
1064
|
-
<Modal open={visible} onCancel={() => setVisible(false)} footer={null} destroyOnClose width={950} style={{ top: 10 }}>
|
|
1065
|
-
{ActiveComponent && (
|
|
1066
|
-
<ActiveComponent
|
|
1067
|
-
formContent={single}
|
|
1068
|
-
callback={() => {
|
|
1069
|
-
setVisible(false);
|
|
1070
|
-
refresh();
|
|
1071
|
-
setVisible(false);
|
|
1072
|
-
fetchReportData();
|
|
1073
|
-
}}
|
|
1074
|
-
// {...dynamicProps}
|
|
1075
|
-
/>
|
|
1076
|
-
)}
|
|
1077
|
-
</Modal>
|
|
1078
|
-
|
|
1079
|
-
<Modal open={isScannerVisible} title="Scan QR Code" footer={null} onCancel={() => setScannerVisible(false)} destroyOnClose>
|
|
1080
|
-
<QrScanner onScanSuccess={handleScanSuccess} onClose={() => setScannerVisible(false)} />
|
|
1081
|
-
</Modal>
|
|
1082
|
-
|
|
1083
|
-
<div>
|
|
1084
|
-
{/* QR Scan End */}
|
|
1085
|
-
{/*table data export to csc component*/}
|
|
1086
|
-
{exportData.exportDatas && (
|
|
1087
|
-
<ExportReactCSV
|
|
1088
|
-
title={config.caption}
|
|
1089
|
-
headers={exportData.exportDatas.exportDataHeaders}
|
|
1090
|
-
csvData={exportData.exportDatas.exportDataColumns}
|
|
1091
|
-
/>
|
|
1092
|
-
)}
|
|
1093
|
-
</div>
|
|
1094
|
-
</div>
|
|
1095
|
-
</div>
|
|
1096
|
-
|
|
1097
|
-
<div>
|
|
1098
|
-
{/* {view ? (
|
|
1099
|
-
<>
|
|
1100
|
-
<CardList dataSource={data} columns={columns} />
|
|
1101
|
-
</>
|
|
1102
|
-
) : (
|
|
1103
|
-
<> */}
|
|
1104
|
-
<Card>
|
|
1105
|
-
{loading ? (
|
|
1106
|
-
<>
|
|
1107
|
-
<Skeleton active paragraph={{ rows: 6 }} />
|
|
1108
|
-
</>
|
|
1109
|
-
) : (
|
|
1110
|
-
<TableComponent
|
|
1111
|
-
size="small"
|
|
1112
|
-
scroll={{ x: 'max-content', y: '60vh' }}
|
|
1113
|
-
tableLayout="fixed"
|
|
1114
|
-
sticky
|
|
1115
|
-
rowKey={(record) => record.OpNo}
|
|
1116
|
-
dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
|
|
1117
|
-
columns={cols}
|
|
1118
|
-
pagination={false}
|
|
1119
|
-
// title={config.caption}
|
|
1120
|
-
summary={(pageData) => {
|
|
1121
|
-
// Variable to save the summary data
|
|
1122
|
-
let summary = {};
|
|
1123
|
-
|
|
1124
|
-
let summaryColumns = [
|
|
1125
|
-
{ field: 'opb_amt', title: 'Amount' },
|
|
1126
|
-
{ field: 'opb_netamt', title: 'Net Amount' },
|
|
1127
|
-
];
|
|
1128
|
-
|
|
1129
|
-
let tableColumns = cols;
|
|
1130
|
-
|
|
1131
|
-
// Creating a copy of columns to append the summary configuration that is needed to set
|
|
1132
|
-
tableColumns.forEach((record, index) => {
|
|
1133
|
-
summaryColumns.forEach((inner) => {
|
|
1134
|
-
if (record.field === inner.field) {
|
|
1135
|
-
tableColumns[index].summary = inner;
|
|
1136
|
-
}
|
|
1137
|
-
});
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
|
-
// Initialize
|
|
1141
|
-
summaryColumns.map((item) => {
|
|
1142
|
-
return (summary[item.field] = 0);
|
|
1143
|
-
});
|
|
1144
|
-
|
|
1145
|
-
// Find the total
|
|
1146
|
-
summaryColumns.map((item) => {
|
|
1147
|
-
pageData.forEach((entry) => {
|
|
1148
|
-
return (summary[item.field] = summary[item.field] + entry[item.field]);
|
|
1149
|
-
});
|
|
1150
|
-
});
|
|
1151
|
-
|
|
1152
|
-
return (
|
|
1153
|
-
<>
|
|
1154
|
-
<Table.Summary.Row>
|
|
1155
|
-
{tableColumns.map((column, key) => {
|
|
1156
|
-
return <Table.Summary.Cell key={key}>{column.summary ? <>{summary[column.summary.field]}</> : null}</Table.Summary.Cell>;
|
|
1157
|
-
})}
|
|
1158
|
-
</Table.Summary.Row>
|
|
1159
|
-
</>
|
|
1160
|
-
);
|
|
1161
|
-
}}
|
|
1162
|
-
/>
|
|
1163
|
-
)}
|
|
1164
|
-
|
|
1165
|
-
{/* Pagination aligned to the right */}
|
|
1166
|
-
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 8 }}>
|
|
1167
|
-
<Pagination
|
|
1168
|
-
showSizeChanger
|
|
1169
|
-
current={pagination.current}
|
|
1170
|
-
pageSize={pagination.pageSize}
|
|
1171
|
-
total={pagination.total}
|
|
1172
|
-
pageSizeOptions={[20, 30, 50, 100]}
|
|
1173
|
-
onChange={(page, pageSize) => handlePagination({ current: page, pageSize })}
|
|
1174
|
-
/>
|
|
1175
|
-
</div>
|
|
1176
|
-
|
|
1177
|
-
{/*If patient data exists show the number else to 0 */}
|
|
1178
|
-
<p className="size-hint">{patients ? patients.length : 0} records. </p>
|
|
1179
|
-
</Card>
|
|
1180
|
-
{/* </> */}
|
|
1181
|
-
{/* )} */}
|
|
1182
|
-
</div>
|
|
1183
|
-
</>
|
|
1184
|
-
);
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
// //Mobile view card Section
|
|
1188
|
-
// function CardList({ dataSource, url }) {
|
|
1189
|
-
// const { user = {}, dispatch } = useContext(GlobalContext);
|
|
1190
|
-
|
|
1191
|
-
// function onClick(item) {
|
|
1192
|
-
// Location.navigate({
|
|
1193
|
-
// url: `/lab-detail/${item.BillID}`,
|
|
1194
|
-
// });
|
|
1195
|
-
|
|
1196
|
-
// dispatch({ type: 'index', payload: item.rowIndex });
|
|
1197
|
-
// }
|
|
1198
|
-
|
|
1199
|
-
// return dataSource.map((item, index) => {
|
|
1200
|
-
// // to={`/lab-detail/${item.BillID}`}
|
|
1201
|
-
// return (
|
|
1202
|
-
// <div
|
|
1203
|
-
// key={index}
|
|
1204
|
-
// className="report-item"
|
|
1205
|
-
// onClick={() => {
|
|
1206
|
-
// onClick(item);
|
|
1207
|
-
// }}
|
|
1208
|
-
// >
|
|
1209
|
-
// <GuestCard record={item} />
|
|
1210
|
-
// </div>
|
|
1211
|
-
// );
|
|
1212
|
-
// });
|
|
1213
|
-
// }
|
|
1214
|
-
|
|
1215
|
-
// function GuestCard({ record }) {
|
|
1216
|
-
// return (
|
|
1217
|
-
// <Card className="card vehicle-card">
|
|
1218
|
-
// <div className="card">
|
|
1219
|
-
// <h2 className="title">{record.PName}</h2>
|
|
1220
|
-
|
|
1221
|
-
// <h4 className="values">{record.OpNo}</h4>
|
|
1222
|
-
|
|
1223
|
-
// <h3 className="values">{record.Test}</h3>
|
|
1224
|
-
|
|
1225
|
-
// <h3 className="values">Primary Result : {record.PrimaryResult || 'Pending'}</h3>
|
|
1226
|
-
|
|
1227
|
-
// <Text type="secondary">{record.Mobile}</Text>
|
|
1228
|
-
|
|
1229
|
-
// <h4 className="values">{record.Date}</h4>
|
|
1230
|
-
// </div>
|
|
1231
|
-
// </Card>
|
|
1232
|
-
// );
|
|
1233
|
-
// }
|
|
1234
|
-
// );
|
|
1235
|
-
// }
|