ui-soxo-bootstrap-core 2.6.0 → 2.6.1-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +49 -19
- package/core/components/extra-info/extra-info-details.js +2 -2
- package/core/components/menu-template-api/menu-template-api.js +2 -2
- package/core/lib/Store.js +3 -3
- package/core/lib/components/global-header/global-header.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +19 -13
- package/core/lib/elements/basic/country-phone-input/country-phone-input.js +35 -60
- package/core/lib/elements/basic/country-phone-input/phone-input.scss +14 -0
- 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 +468 -502
- package/core/lib/models/forms/components/form-creator/form-creator.scss +5 -4
- package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
- 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 +46 -0
- package/core/lib/pages/login/communication-mode-selection.scss +60 -0
- package/core/lib/pages/login/login.js +153 -24
- package/core/lib/pages/login/login.scss +229 -334
- package/core/lib/pages/login/reset-password.js +124 -0
- package/core/lib/pages/login/reset-password.scss +31 -0
- package/core/lib/pages/profile/themes.json +4 -4
- package/core/lib/utils/api/api.utils.js +71 -48
- package/core/lib/utils/common/common.utils.js +109 -0
- package/core/lib/utils/http/http.utils.js +1 -0
- package/core/lib/utils/index.js +25 -28
- package/core/models/base/base.js +7 -3
- package/core/models/core-scripts/core-scripts.js +9 -0
- 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 +5 -9
- package/core/models/menus/menus.js +21 -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/roles/roles.js +9 -0
- 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 +47 -56
- package/core/models/users/components/user-add/user-edit.js +25 -4
- package/core/models/users/users.js +34 -8
- 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 +120 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +75 -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 +252 -0
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +222 -376
- package/core/modules/steps/action-buttons.js +47 -45
- package/core/modules/steps/action-buttons.scss +35 -6
- package/core/modules/steps/steps.js +12 -10
- package/core/modules/steps/steps.scss +229 -31
- package/core/modules/steps/timeline.js +21 -19
- package/package.json +3 -2
- package/core/components/external-window/DEVELOPER_GUIDE.md +0 -705
|
@@ -1,11 +1,9 @@
|
|
|
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 } from 'antd';
|
|
4
4
|
|
|
5
5
|
import { QrcodeOutlined } from '@ant-design/icons';
|
|
6
6
|
|
|
7
|
-
import * as Icons from '@ant-design/icons';
|
|
8
|
-
|
|
9
7
|
import { Location, FormCreator, GlobalContext, ExportReactCSV, getExportData, Card, TableComponent, QrScanner } from './../../../../lib/';
|
|
10
8
|
|
|
11
9
|
import { CoreScripts } from './../../../../models/';
|
|
@@ -14,15 +12,15 @@ import moment from 'moment-timezone';
|
|
|
14
12
|
|
|
15
13
|
import Button from '../../../../lib/elements/basic/button/button';
|
|
16
14
|
|
|
17
|
-
import { Link } from 'react-router-dom';
|
|
18
|
-
|
|
19
15
|
import './reporting-dashboard.scss';
|
|
20
16
|
|
|
21
17
|
// import MenuDashBoard from '../../../../pages/homepage-api/menu-dashboard';
|
|
22
18
|
import MenuDashBoardComponent from '../../../../lib/elements/basic/menu-dashboard/menu-dashboard';
|
|
23
19
|
import { useHistory } from 'react-router-dom';
|
|
24
|
-
|
|
25
20
|
import * as ReportingDashboardComp from '../index';
|
|
21
|
+
import buildDisplayColumns from './display-columns/build-display-columns';
|
|
22
|
+
import { getRedirectLink } from './display-columns/display-cell-renderer';
|
|
23
|
+
import AdvancedSearchSelect from './adavance-search/advance-search';
|
|
26
24
|
|
|
27
25
|
// import { isPdfFile } from 'pdfjs-dist';
|
|
28
26
|
|
|
@@ -64,6 +62,10 @@ export default function ReportingDashboard({
|
|
|
64
62
|
|
|
65
63
|
const [cardLoading, setCardLoading] = useState(true);
|
|
66
64
|
|
|
65
|
+
const [searchParameters, setSearchParameters] = useState([]);
|
|
66
|
+
|
|
67
|
+
const [searchValues, setSearchValues] = useState({});
|
|
68
|
+
|
|
67
69
|
const [dashboardVisible, setDashBoardVisible] = useState(false);
|
|
68
70
|
|
|
69
71
|
const [formContents, setformContents] = useState({});
|
|
@@ -107,16 +109,14 @@ export default function ReportingDashboard({
|
|
|
107
109
|
const fetchId = idOverride || id;
|
|
108
110
|
await CoreScripts.getRecord({ id: fetchId, dbPtr }).then(async ({ result }) => {
|
|
109
111
|
// Check if display columns are provided from backend
|
|
110
|
-
|
|
111
|
-
// Parse and set columns from stored JSON
|
|
112
|
+
let parsedColumns = [];
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// Reset columns if no display columns exist
|
|
116
|
-
|
|
117
|
-
setColumns([]);
|
|
114
|
+
if (result.display_columns) {
|
|
115
|
+
parsedColumns = JSON.parse(result.display_columns);
|
|
118
116
|
}
|
|
119
|
-
|
|
117
|
+
setColumns(parsedColumns);
|
|
118
|
+
|
|
119
|
+
await prepareInputParameters(result, parsedColumns);
|
|
120
120
|
|
|
121
121
|
if (result.summary_columns) {
|
|
122
122
|
setSummaryColumns(JSON.parse(result.summary_columns));
|
|
@@ -161,7 +161,7 @@ export default function ReportingDashboard({
|
|
|
161
161
|
*/
|
|
162
162
|
|
|
163
163
|
//Prepare input parameters by mapping default values and binding models if needed
|
|
164
|
-
async function prepareInputParameters(record) {
|
|
164
|
+
async function prepareInputParameters(record, parsedColumns) {
|
|
165
165
|
setLoading(true);
|
|
166
166
|
let urlParams = Location.search();
|
|
167
167
|
|
|
@@ -173,6 +173,8 @@ export default function ReportingDashboard({
|
|
|
173
173
|
parameters = record.input_parameters ? JSON.parse(record.input_parameters) : null;
|
|
174
174
|
|
|
175
175
|
let formContent = {};
|
|
176
|
+
const searchFields = parameters.filter((p) => p.type === 'search');
|
|
177
|
+
setSearchParameters([...searchFields]);
|
|
176
178
|
|
|
177
179
|
parameters = await parameters?.map((record) => {
|
|
178
180
|
// Only if the url params does have a matching value ,
|
|
@@ -216,7 +218,13 @@ export default function ReportingDashboard({
|
|
|
216
218
|
if (record.type === 'date' && !formContent[record.field]) {
|
|
217
219
|
formContent[record.field] = moment().tz(process.env.REACT_APP_TIMEZONE);
|
|
218
220
|
}
|
|
219
|
-
|
|
221
|
+
// if (record.type === 'search') {
|
|
222
|
+
// return {
|
|
223
|
+
// ...record,
|
|
224
|
+
// component: AdvancedSearchSelect,
|
|
225
|
+
// required: record.required,
|
|
226
|
+
// };
|
|
227
|
+
// }
|
|
220
228
|
if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
|
|
221
229
|
// let model = "";
|
|
222
230
|
let model = CustomModels[record.modelName];
|
|
@@ -237,7 +245,7 @@ export default function ReportingDashboard({
|
|
|
237
245
|
setformContents(formContent);
|
|
238
246
|
|
|
239
247
|
// Trigger form submission
|
|
240
|
-
onFinish(formContent, null, record.input_parameters);
|
|
248
|
+
onFinish(formContent, null, record.input_parameters, parsedColumns);
|
|
241
249
|
|
|
242
250
|
setLoading(false);
|
|
243
251
|
|
|
@@ -248,7 +256,7 @@ export default function ReportingDashboard({
|
|
|
248
256
|
} else {
|
|
249
257
|
//// Filter the array "parameters" and keep only elements where "type" is present (truthy)
|
|
250
258
|
//Keep only parameters that have a "type" → used for UI display
|
|
251
|
-
let filter = parameters.filter((ele) => ele.type);
|
|
259
|
+
let filter = parameters.filter((ele) => ele.type && ele.type !== 'search');
|
|
252
260
|
// Update the "details" state with the filtered results
|
|
253
261
|
|
|
254
262
|
setDetails([...filter]);
|
|
@@ -261,7 +269,7 @@ export default function ReportingDashboard({
|
|
|
261
269
|
getPatientDetails();
|
|
262
270
|
}
|
|
263
271
|
|
|
264
|
-
const fetchReportData = async (id, values, dbPtr, pagination) => {
|
|
272
|
+
const fetchReportData = async (id, values, dbPtr, pagination, parsedColumns) => {
|
|
265
273
|
const { current, pageSize } = pagination || {};
|
|
266
274
|
// If card script id is exist load that id otherwise load id
|
|
267
275
|
const coreScriptId = scriptId.current ? scriptId.current : id;
|
|
@@ -290,17 +298,23 @@ export default function ReportingDashboard({
|
|
|
290
298
|
if (scope) {
|
|
291
299
|
formBody = { body: { ...scope, ...paginationData } };
|
|
292
300
|
}
|
|
301
|
+
|
|
293
302
|
// Fetch result
|
|
294
303
|
const result = await CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr);
|
|
304
|
+
|
|
305
|
+
const apiData = Array.isArray(result) ? result : Array.isArray(result?.result) ? result.result : [];
|
|
306
|
+
|
|
295
307
|
// Handle both result formats
|
|
296
|
-
let resultDetails =
|
|
308
|
+
let resultDetails = apiData[0] || [];
|
|
297
309
|
if (result?.result && result?.result[0]) {
|
|
298
310
|
resultDetails = result.result[0];
|
|
299
311
|
}
|
|
300
312
|
// Update patients
|
|
301
313
|
setPatients(resultDetails || []);
|
|
314
|
+
console.log(parsedColumns);
|
|
315
|
+
|
|
302
316
|
// Check if columns are not yet defined
|
|
303
|
-
if (
|
|
317
|
+
if (parsedColumns.length === 0 && resultDetails.length > 0) {
|
|
304
318
|
// Create columns dynamically from resultDetails keys
|
|
305
319
|
setColumns((prev) => {
|
|
306
320
|
if (prev.length > 0) return prev;
|
|
@@ -324,46 +338,183 @@ export default function ReportingDashboard({
|
|
|
324
338
|
}
|
|
325
339
|
} catch (error) {
|
|
326
340
|
console.error('Error fetching report data:', error);
|
|
327
|
-
message.warn('Please try again');
|
|
328
341
|
} finally {
|
|
329
342
|
// Always runs, success or error
|
|
330
343
|
setLoading(false);
|
|
331
344
|
}
|
|
332
345
|
};
|
|
333
346
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
347
|
+
/**
|
|
348
|
+
*
|
|
349
|
+
* @param {*} searchValues
|
|
350
|
+
* @param {*} searchParameters
|
|
351
|
+
* @returns
|
|
352
|
+
*/
|
|
353
|
+
const buildSearchCondition = (searchValues, searchParameters) => {
|
|
354
|
+
let searchCondition = "";
|
|
355
|
+
|
|
356
|
+
Object.keys(searchValues).forEach((key) => {
|
|
357
|
+
const arr = searchValues[key];
|
|
358
|
+
if (!Array.isArray(arr) || arr.length === 0) return;
|
|
359
|
+
|
|
360
|
+
const param = searchParameters.find((p) => p.field === key);
|
|
361
|
+
if (!param) return;
|
|
362
|
+
|
|
363
|
+
const operator = (param.search_operator || "").trim().toUpperCase();
|
|
364
|
+
const column = param.db_column || key;
|
|
365
|
+
|
|
366
|
+
if (operator === "LIKE") {
|
|
367
|
+
const likeConditions = arr
|
|
368
|
+
.map((v) => `${column} LIKE '${v}'`)
|
|
369
|
+
.join(" OR ");
|
|
370
|
+
searchCondition += ` AND (${likeConditions})`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
else if (operator === "IN") {
|
|
374
|
+
const valueList = arr.map((v) => `'${v}'`).join(",");
|
|
375
|
+
searchCondition += ` AND ${column} IN (${valueList})`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
else if (operator === "=") {
|
|
379
|
+
const eqConditions = arr
|
|
380
|
+
.map((v) => `${column} = '${v}'`)
|
|
381
|
+
.join(" OR ");
|
|
382
|
+
searchCondition += ` AND (${eqConditions})`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
else if (operator === "!=") {
|
|
386
|
+
const neqConditions = arr
|
|
387
|
+
.map((v) => `${column} != '${v}'`)
|
|
388
|
+
.join(" AND ");
|
|
389
|
+
searchCondition += ` AND (${neqConditions})`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
else {
|
|
393
|
+
// fallback for other operators like > < >= <=
|
|
394
|
+
const conditions = arr
|
|
395
|
+
.map((v) => `${column} ${operator} '${v}'`)
|
|
396
|
+
.join(" OR ");
|
|
397
|
+
searchCondition += ` AND (${conditions})`;
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
return searchCondition;
|
|
402
|
+
};
|
|
403
|
+
const handleSubmit = (values) => {
|
|
404
|
+
|
|
405
|
+
const hasSearchValues = Object.values(searchValues).some(
|
|
406
|
+
(v) => Array.isArray(v) && v.length > 0
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
let finalValues = values;
|
|
410
|
+
|
|
411
|
+
if (hasSearchValues) {
|
|
412
|
+
|
|
413
|
+
const searchCondition = buildSearchCondition(
|
|
414
|
+
searchValues,
|
|
415
|
+
searchParameters
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const resetValues = {};
|
|
419
|
+
Object.keys(values).forEach((key) => {
|
|
420
|
+
resetValues[key] = null;
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
finalValues = {
|
|
424
|
+
...resetValues,
|
|
425
|
+
search_condition: searchCondition,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
338
428
|
|
|
339
|
-
|
|
340
|
-
|
|
429
|
+
const { pageSize } = pagination;
|
|
430
|
+
const resetPage = 1;
|
|
341
431
|
|
|
342
|
-
|
|
432
|
+
scriptId.current = null;
|
|
433
|
+
|
|
434
|
+
if (!hasSearchValues) {
|
|
343
435
|
const currentUrlParams = Location.search();
|
|
344
436
|
const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
345
437
|
|
|
346
|
-
// Construct new query string
|
|
347
438
|
const newParams = new URLSearchParams({
|
|
348
439
|
...cleanParams,
|
|
349
440
|
current: resetPage,
|
|
350
441
|
pageSize,
|
|
351
442
|
});
|
|
352
443
|
|
|
353
|
-
// Replace URL
|
|
354
444
|
const newUrl = `${window.location.pathname}?${newParams.toString()}`;
|
|
355
|
-
window.history.replaceState({},
|
|
445
|
+
window.history.replaceState({}, "", newUrl);
|
|
446
|
+
}
|
|
356
447
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
448
|
+
onFinish(finalValues, resetPage);
|
|
449
|
+
};
|
|
450
|
+
// const handleSubmit = (values) => {
|
|
451
|
+
|
|
452
|
+
// // Check if any advanced search value exists
|
|
453
|
+
// const hasSearchValues = Object.values(searchValues).some((v) => Array.isArray(v) && v.length > 0);
|
|
454
|
+
|
|
455
|
+
// let finalValues = values;
|
|
456
|
+
// let searchCondition = '';
|
|
457
|
+
// if (hasSearchValues) {
|
|
458
|
+
// Object.keys(searchValues).forEach((key) => {
|
|
459
|
+
// const arr = searchValues[key];
|
|
460
|
+
// if (!Array.isArray(arr) || arr.length === 0) return;
|
|
461
|
+
|
|
462
|
+
// // Find input parameter configuration
|
|
463
|
+
// const param = searchParameters.find((p) => p.field === key);
|
|
464
|
+
// if (!param) return;
|
|
465
|
+
|
|
466
|
+
// const operator = (param.search_operator || '').trim(); // LIKE / IN / =
|
|
467
|
+
// const column = param.db_column || key; // DB column mapping if available
|
|
468
|
+
|
|
469
|
+
// // Build value list
|
|
470
|
+
// const valueList = arr.map((v) => `'${v}'`).join(',');
|
|
471
|
+
|
|
472
|
+
// // Build SQL condition dynamically
|
|
473
|
+
// searchCondition += ` AND ${column} ${operator} (${valueList})`;
|
|
474
|
+
// });
|
|
475
|
+
|
|
476
|
+
// // Reset normal form parameters
|
|
477
|
+
// const resetValues = {};
|
|
478
|
+
// Object.keys(values).forEach((key) => {
|
|
479
|
+
// resetValues[key] = null;
|
|
480
|
+
// });
|
|
481
|
+
|
|
482
|
+
// // Final values sent to API
|
|
483
|
+
// finalValues = {
|
|
484
|
+
// ...resetValues,
|
|
485
|
+
// search_condition: searchCondition,
|
|
486
|
+
// };
|
|
487
|
+
// }
|
|
488
|
+
|
|
489
|
+
// const { pageSize } = pagination;
|
|
490
|
+
// const resetPage = 1;
|
|
491
|
+
|
|
492
|
+
// scriptId.current = null;
|
|
360
493
|
|
|
494
|
+
// // Update URL only when NOT using advanced search
|
|
495
|
+
// if (!hasSearchValues) {
|
|
496
|
+
// const currentUrlParams = Location.search();
|
|
497
|
+
// const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
498
|
+
|
|
499
|
+
// const newParams = new URLSearchParams({
|
|
500
|
+
// ...cleanParams,
|
|
501
|
+
// current: resetPage,
|
|
502
|
+
// pageSize,
|
|
503
|
+
// });
|
|
504
|
+
|
|
505
|
+
// const newUrl = `${window.location.pathname}?${newParams.toString()}`;
|
|
506
|
+
// window.history.replaceState({}, '', newUrl);
|
|
507
|
+
// }
|
|
508
|
+
|
|
509
|
+
// // Call report API
|
|
510
|
+
// onFinish(finalValues, resetPage);
|
|
511
|
+
// };
|
|
361
512
|
/**
|
|
362
513
|
*
|
|
363
514
|
* @param {*} values
|
|
364
515
|
*/
|
|
365
516
|
|
|
366
|
-
const onFinish = async (values, resetPage, inputParamsString) => {
|
|
517
|
+
const onFinish = async (values, resetPage, inputParamsString, parsedColumns) => {
|
|
367
518
|
setLoading(true);
|
|
368
519
|
setCardLoading(true);
|
|
369
520
|
// // Check if the dashboard is visible and both start and end values are provided
|
|
@@ -423,7 +574,7 @@ export default function ReportingDashboard({
|
|
|
423
574
|
|
|
424
575
|
// Call API
|
|
425
576
|
try {
|
|
426
|
-
await fetchReportData(id, values, dbPtr, paginationData);
|
|
577
|
+
await fetchReportData(id, values, dbPtr, paginationData, parsedColumns);
|
|
427
578
|
} finally {
|
|
428
579
|
setLoading(false);
|
|
429
580
|
setCardLoading(false);
|
|
@@ -448,23 +599,6 @@ export default function ReportingDashboard({
|
|
|
448
599
|
<Card className="reporting-dashboard card card-shadow">
|
|
449
600
|
{/** If dashBoardIds exist and contain elements, render MenuDashBoard*/}
|
|
450
601
|
|
|
451
|
-
{/* Page Header */}
|
|
452
|
-
{/* <div className="page-header">
|
|
453
|
-
<div>
|
|
454
|
-
<Title style={{ marginBottom: '0px' }} level={4}>
|
|
455
|
-
{config.caption || 'Report'}
|
|
456
|
-
</Title>
|
|
457
|
-
</div>
|
|
458
|
-
|
|
459
|
-
<div className="right">
|
|
460
|
-
<div className="date-and-fltr">
|
|
461
|
-
<Button onClick={refresh} type="secondary" size={'small'}>
|
|
462
|
-
<ReloadOutlined />
|
|
463
|
-
</Button>
|
|
464
|
-
</div>
|
|
465
|
-
</div>
|
|
466
|
-
</div> */}
|
|
467
|
-
{/* Page Header Ends */}
|
|
468
602
|
{dashBoardIds?.length > 0 ? (
|
|
469
603
|
<MenuDashBoardComponent
|
|
470
604
|
dashBoardIds={dashBoardIds} //Pass the available dashboard IDs to the componen
|
|
@@ -534,6 +668,24 @@ export default function ReportingDashboard({
|
|
|
534
668
|
/>
|
|
535
669
|
) : null}
|
|
536
670
|
{/* </Card> */}
|
|
671
|
+
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
|
|
672
|
+
{searchParameters.length > 0
|
|
673
|
+
? searchParameters.map((param) => (
|
|
674
|
+
<AdvancedSearchSelect
|
|
675
|
+
key={param.field}
|
|
676
|
+
parameter={param}
|
|
677
|
+
field={param}
|
|
678
|
+
value={searchValues[param.field] || []}
|
|
679
|
+
onChange={(value) => {
|
|
680
|
+
setSearchValues((prev) => ({
|
|
681
|
+
...prev,
|
|
682
|
+
[param.field]: value,
|
|
683
|
+
}));
|
|
684
|
+
}}
|
|
685
|
+
/>
|
|
686
|
+
))
|
|
687
|
+
: null}
|
|
688
|
+
</div>
|
|
537
689
|
</div>
|
|
538
690
|
|
|
539
691
|
{/** GuestList component start*/}
|
|
@@ -617,266 +769,14 @@ function GuestList({
|
|
|
617
769
|
const { isMobile, dispatch } = useContext(GlobalContext);
|
|
618
770
|
const [single, setSingle] = useState({});
|
|
619
771
|
|
|
620
|
-
const getRedirectLink = (entry, record) => {
|
|
621
|
-
let redirectLink = entry.redirect_link;
|
|
622
|
-
if (entry.replace_variables) {
|
|
623
|
-
entry.replace_variables.forEach((replacement) => {
|
|
624
|
-
const value = record[replacement.field] || '';
|
|
625
|
-
redirectLink = redirectLink.replace(new RegExp(`@${replacement.field};`, 'g'), value);
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
return redirectLink;
|
|
629
|
-
};
|
|
630
|
-
|
|
631
772
|
// const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
|
|
632
|
-
const cols =
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
fixed: isFixedIndex ? 'left' : null,
|
|
640
|
-
},
|
|
641
|
-
],
|
|
642
|
-
...columns.map((entry) => {
|
|
643
|
-
// if (entry.sort) {
|
|
644
|
-
// return {
|
|
645
|
-
// render: (record) => {
|
|
646
|
-
|
|
647
|
-
// if (entry.render) {
|
|
648
|
-
|
|
649
|
-
// return entry.render(record);
|
|
650
|
-
// } else {
|
|
651
|
-
// return record[entry.dataIndex]
|
|
652
|
-
// }
|
|
653
|
-
// },
|
|
654
|
-
// title: entry.title,
|
|
655
|
-
// key: entry.field,
|
|
656
|
-
// sorter: (a, b) => entry.sort(a, b),
|
|
657
|
-
// sortDirections: ['ascend', 'descend', 'ascend'],
|
|
658
|
-
// };
|
|
659
|
-
// } else {
|
|
660
|
-
return {
|
|
661
|
-
render: (record) => {
|
|
662
|
-
let textColor = 'inherit';
|
|
663
|
-
|
|
664
|
-
if (entry.color) {
|
|
665
|
-
textColor = entry.color;
|
|
666
|
-
}
|
|
667
|
-
/** We can have x types of components that is to be rendered here */
|
|
668
|
-
|
|
669
|
-
/**1. Column Data */
|
|
670
|
-
|
|
671
|
-
/**2. Action */
|
|
672
|
-
|
|
673
|
-
/**3. Custom Component - In future . */
|
|
674
|
-
|
|
675
|
-
if (entry.render) {
|
|
676
|
-
return entry.render(record);
|
|
677
|
-
|
|
678
|
-
// The type of component can be differentiated by type/ we can also reuse
|
|
679
|
-
// any field present in the columns to avoid any additional field
|
|
680
|
-
} else if (entry.type === 'link') {
|
|
681
|
-
//Cheacking type of action to be done ie,If it contains a type then it will match with the record
|
|
682
|
-
// for example initally we are implementing it for a type link
|
|
683
|
-
// ie, we will return a link in query with the field as link and type link in display_columns
|
|
684
|
-
//So here we match the field returned by query with the type in display_columns
|
|
685
|
-
|
|
686
|
-
if (record[entry.field]) {
|
|
687
|
-
return (
|
|
688
|
-
<a href={record[entry.field]} target="_blank" rel="noopener noreferrer">
|
|
689
|
-
View
|
|
690
|
-
</a>
|
|
691
|
-
);
|
|
692
|
-
}
|
|
693
|
-
} else if (entry.field === 'action') {
|
|
694
|
-
let redirectLink = entry.redirect_link;
|
|
695
|
-
|
|
696
|
-
// The variables to be replaced can be maintained
|
|
697
|
-
// as a configuration in the entry or the column configuration
|
|
698
|
-
|
|
699
|
-
// We iterate through all the variables that are present in the configuration
|
|
700
|
-
// and replace the link to generate the final link
|
|
701
|
-
|
|
702
|
-
entry.replace_variables.forEach((replacement) => {
|
|
703
|
-
redirectLink = redirectLink.replace(new RegExp('@' + replacement.field + ';', 'g'), record[replacement.field]);
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
return <Link to={`${redirectLink}`}>{entry.display_name_link ? entry.display_name_link : 'View'}</Link>;
|
|
707
|
-
} else if (entry.field === 'custom') {
|
|
708
|
-
// Make all the components in modules available for use in custom column of core script
|
|
709
|
-
// var genericComponents = require('./../../../../../../../nura-api-new/nura-desk/src/modules');
|
|
710
|
-
var genericComponents = CustomComponents;
|
|
711
|
-
|
|
712
|
-
// Arrive the component name
|
|
713
|
-
let componentName = entry.component;
|
|
714
|
-
|
|
715
|
-
let LoadedComponent = null;
|
|
716
|
-
// If there is custom components mensioned in the display_columns,
|
|
717
|
-
// then matching the custom components with the generic components
|
|
718
|
-
if (componentName) {
|
|
719
|
-
if (componentName && genericComponents[componentName]) {
|
|
720
|
-
LoadedComponent = genericComponents[componentName];
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
let propValue = {};
|
|
725
|
-
// If there is props value
|
|
726
|
-
if (entry.props) {
|
|
727
|
-
// Looping the props from the props mensioned in display_columns,
|
|
728
|
-
// Matching the field of record props field with the and return the
|
|
729
|
-
entry.props.forEach((values) => {
|
|
730
|
-
// Matching the fields of record with the props field
|
|
731
|
-
let valueCreation = record[values.field];
|
|
732
|
-
// Returning the values of field matched by fields
|
|
733
|
-
propValue[values.value] = valueCreation;
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
return (
|
|
738
|
-
<LoadedComponent
|
|
739
|
-
// Configuration will define
|
|
740
|
-
{...entry.config}
|
|
741
|
-
callback={() => {
|
|
742
|
-
refresh();
|
|
743
|
-
}}
|
|
744
|
-
// record={record}
|
|
745
|
-
|
|
746
|
-
{...record}
|
|
747
|
-
// assigning the props
|
|
748
|
-
{...propValue}
|
|
749
|
-
/>
|
|
750
|
-
);
|
|
751
|
-
} else {
|
|
752
|
-
// Check if both `color_code` exists in the record and `enableColor` is true
|
|
753
|
-
if (record.color_code && entry.enableColor) {
|
|
754
|
-
// If the column type is 'tag', render the field inside an Ant Design <Tag> with color
|
|
755
|
-
if (entry.columnType === 'tag') {
|
|
756
|
-
return <Tag color={record.color_code}>{record[entry.field]}</Tag>;
|
|
757
|
-
|
|
758
|
-
// If the column type is 'span', render the field inside a <span> with inline color style
|
|
759
|
-
} else if (entry.columnType === 'span') {
|
|
760
|
-
return <span style={{ color: record.color_code, overflowWrap: 'break-word', WebkitLineClamp: 3 }}>{record[entry.field]}</span>;
|
|
761
|
-
}
|
|
762
|
-
} else {
|
|
763
|
-
/**
|
|
764
|
-
* This code dynamically displays icons based on data and a configuration
|
|
765
|
-
* When a column's configuration includes a displayIcons JSON, the code will check the corresponding data field.
|
|
766
|
-
* If the field's value (e.g., "Pending") matches a key in that JSON,
|
|
767
|
-
* the system will display the specified icon with its defined color and size.
|
|
768
|
-
*/
|
|
769
|
-
if (entry.displayIcons) {
|
|
770
|
-
// Get the field value
|
|
771
|
-
const fieldValue = record[entry.field]?.toString();
|
|
772
|
-
|
|
773
|
-
// Get the configuration for the icon from the JSON.
|
|
774
|
-
const displayConfig = entry.displayIcons[fieldValue];
|
|
775
|
-
if (displayConfig) {
|
|
776
|
-
// Look up the actual component from our iconMap using the name from the JSON.
|
|
777
|
-
const DynamicIcon = Icons[displayConfig.icon];
|
|
778
|
-
// If a component is found, render it with the specified color and size.
|
|
779
|
-
if (DynamicIcon) {
|
|
780
|
-
return (
|
|
781
|
-
<DynamicIcon
|
|
782
|
-
style={{
|
|
783
|
-
color: displayConfig.color,
|
|
784
|
-
fontSize: displayConfig.size,
|
|
785
|
-
}}
|
|
786
|
-
/>
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
} else {
|
|
791
|
-
//If the value is neither 'Y' nor 'N', return the actual field value
|
|
792
|
-
return <span style={{ color: textColor, whiteSpace: 'pre-wrap', overflowWrap: 'break-word' }}>{record[entry.field]}</span>;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
},
|
|
797
|
-
field: entry.field,
|
|
798
|
-
// title: entry.title,
|
|
799
|
-
// title: (
|
|
800
|
-
// <Tooltip title={entry.title}>
|
|
801
|
-
// {entry.title}
|
|
802
|
-
// </Tooltip>
|
|
803
|
-
// ),
|
|
804
|
-
title: (
|
|
805
|
-
<Tooltip title={entry.tooltip || entry.title}>
|
|
806
|
-
<span>{entry.title}</span>
|
|
807
|
-
</Tooltip>
|
|
808
|
-
),
|
|
809
|
-
key: entry.field,
|
|
810
|
-
width: entry.width ? parseInt(entry.width) : 160,
|
|
811
|
-
fixed: entry.isFixedColumn ? entry.isFixedColumn : null, // Conditionally setting the 'fixed' key to 'left' if 'isColumnStatic' is true; otherwise, setting it to null.
|
|
812
|
-
// Check if filtering is enabled and patients is an array
|
|
813
|
-
filters:
|
|
814
|
-
entry.isFilterEnabled && Array.isArray(patients)
|
|
815
|
-
? [...new Set(patients.map((item) => item[entry.field]).filter(Boolean))].map((value) => ({ text: value, value }))
|
|
816
|
-
: null,
|
|
817
|
-
// Apply the filter only if it's enabled for the column
|
|
818
|
-
onFilter: entry.isFilterEnabled ? (value, record) => record[entry.field] === value : null,
|
|
819
|
-
//If sorting is enabled for this entry, provide a sorter function
|
|
820
|
-
sorter: entry.isSortingEnabled ? (a, b) => String(a[entry.field]).localeCompare(String(b[entry.field])) : null,
|
|
821
|
-
|
|
822
|
-
// Apply the filter search
|
|
823
|
-
filterSearch: entry.isFilterEnabled ? entry.isFilterEnabled : false,
|
|
824
|
-
exportDefinition: (record) => {
|
|
825
|
-
//Custom components should not be downloaded
|
|
826
|
-
// return entry.field === 'custom' ? null : record[entry.field];
|
|
827
|
-
if (entry.field === 'custom') {
|
|
828
|
-
// Find the field key in props that corresponds to 'description'
|
|
829
|
-
const description = entry.props?.find((p) => p.value === 'description')?.field;
|
|
830
|
-
|
|
831
|
-
// Return the value from record.props if it exists
|
|
832
|
-
return description && record[description] ? record[description] : null;
|
|
833
|
-
}
|
|
834
|
-
return record[entry.field];
|
|
835
|
-
},
|
|
836
|
-
// Add align property based on column type
|
|
837
|
-
align: entry.type === 'number' ? 'right' : 'left',
|
|
838
|
-
};
|
|
839
|
-
// }
|
|
840
|
-
}),
|
|
841
|
-
// ...[
|
|
842
|
-
// {
|
|
843
|
-
// title: '',
|
|
844
|
-
// key: 'action',
|
|
845
|
-
// render: (text, record) => {
|
|
846
|
-
// let detail = model.slice(0, model.length - 1);
|
|
847
|
-
|
|
848
|
-
// return (
|
|
849
|
-
// <Space size="middle">
|
|
850
|
-
// {!schema.hideView && !actions.length ? <Link to={`/${city}/${model}/${text.id}`}>View</Link> : null}
|
|
851
|
-
|
|
852
|
-
// {actions.map((action) => (
|
|
853
|
-
// <Link to={action.url(record)}>{action.caption}</Link>
|
|
854
|
-
// ))}
|
|
855
|
-
// </Space>
|
|
856
|
-
// );
|
|
857
|
-
// },
|
|
858
|
-
// },
|
|
859
|
-
// ],
|
|
860
|
-
|
|
861
|
-
// ...[
|
|
862
|
-
// {
|
|
863
|
-
// title: '',
|
|
864
|
-
// key: 'action',
|
|
865
|
-
// render: (text, record) => {
|
|
866
|
-
// let detail = model.slice(0, model.length - 1);
|
|
867
|
-
|
|
868
|
-
// return (
|
|
869
|
-
// <Link to={`${menu.path.replace(':id',visitid)}?op_no=${OpNo}`}>
|
|
870
|
-
// <Button size={'small'}>
|
|
871
|
-
// <PlayCircleOutlined />
|
|
872
|
-
// Go to Menu
|
|
873
|
-
// </Button>
|
|
874
|
-
// </Link>
|
|
875
|
-
// );
|
|
876
|
-
// },
|
|
877
|
-
// },
|
|
878
|
-
// ],
|
|
879
|
-
];
|
|
773
|
+
const cols = buildDisplayColumns({
|
|
774
|
+
columns,
|
|
775
|
+
patients,
|
|
776
|
+
isFixedIndex,
|
|
777
|
+
CustomComponents,
|
|
778
|
+
refresh,
|
|
779
|
+
});
|
|
880
780
|
|
|
881
781
|
/**
|
|
882
782
|
*
|
|
@@ -903,13 +803,13 @@ function GuestList({
|
|
|
903
803
|
useEffect(() => {
|
|
904
804
|
//Cheaking if there is patient data exists
|
|
905
805
|
if (patients) {
|
|
906
|
-
let data = patients?.map((entry) => {
|
|
907
|
-
|
|
806
|
+
// let data = patients?.map((entry) => {
|
|
807
|
+
// entry.rowIndex = entry.opb_id;
|
|
908
808
|
|
|
909
|
-
|
|
809
|
+
// entry.dispatch = dispatch;
|
|
910
810
|
|
|
911
|
-
|
|
912
|
-
});
|
|
811
|
+
// return entry;
|
|
812
|
+
// });
|
|
913
813
|
|
|
914
814
|
// setData(data);
|
|
915
815
|
|
|
@@ -974,7 +874,7 @@ function GuestList({
|
|
|
974
874
|
setRedirectPatient(matched);
|
|
975
875
|
message.success(`Match found for ${code}, redirecting...`);
|
|
976
876
|
|
|
977
|
-
const actionColumn = columns.find((col) => col.field === 'action');
|
|
877
|
+
const actionColumn = columns.find((col) => col.field === 'action') || columns.find((col) => col.type === 'action');
|
|
978
878
|
if (actionColumn) {
|
|
979
879
|
const redirectLink = getRedirectLink(actionColumn, patient);
|
|
980
880
|
// history.push(redirectLink);
|
|
@@ -1066,12 +966,6 @@ function GuestList({
|
|
|
1066
966
|
</div>
|
|
1067
967
|
|
|
1068
968
|
<div>
|
|
1069
|
-
{/* {view ? (
|
|
1070
|
-
<>
|
|
1071
|
-
<CardList dataSource={data} columns={columns} />
|
|
1072
|
-
</>
|
|
1073
|
-
) : (
|
|
1074
|
-
<> */}
|
|
1075
969
|
<Card>
|
|
1076
970
|
{loading ? (
|
|
1077
971
|
<>
|
|
@@ -1080,10 +974,12 @@ function GuestList({
|
|
|
1080
974
|
) : (
|
|
1081
975
|
<TableComponent
|
|
1082
976
|
size="small"
|
|
1083
|
-
scroll={{ x: 'max-content' }}
|
|
977
|
+
// scroll={{ x: 'max-content' }}
|
|
978
|
+
scroll={{ x: 'max-content', y: '60vh' }}
|
|
1084
979
|
rowKey={(record) => record.OpNo}
|
|
1085
980
|
dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
|
|
1086
981
|
columns={cols}
|
|
982
|
+
sticky
|
|
1087
983
|
pagination={false}
|
|
1088
984
|
// title={config.caption}
|
|
1089
985
|
summary={(pageData) => {
|
|
@@ -1152,53 +1048,3 @@ function GuestList({
|
|
|
1152
1048
|
</>
|
|
1153
1049
|
);
|
|
1154
1050
|
}
|
|
1155
|
-
|
|
1156
|
-
// //Mobile view card Section
|
|
1157
|
-
// function CardList({ dataSource, url }) {
|
|
1158
|
-
// const { user = {}, dispatch } = useContext(GlobalContext);
|
|
1159
|
-
|
|
1160
|
-
// function onClick(item) {
|
|
1161
|
-
// Location.navigate({
|
|
1162
|
-
// url: `/lab-detail/${item.BillID}`,
|
|
1163
|
-
// });
|
|
1164
|
-
|
|
1165
|
-
// dispatch({ type: 'index', payload: item.rowIndex });
|
|
1166
|
-
// }
|
|
1167
|
-
|
|
1168
|
-
// return dataSource.map((item, index) => {
|
|
1169
|
-
// // to={`/lab-detail/${item.BillID}`}
|
|
1170
|
-
// return (
|
|
1171
|
-
// <div
|
|
1172
|
-
// key={index}
|
|
1173
|
-
// className="report-item"
|
|
1174
|
-
// onClick={() => {
|
|
1175
|
-
// onClick(item);
|
|
1176
|
-
// }}
|
|
1177
|
-
// >
|
|
1178
|
-
// <GuestCard record={item} />
|
|
1179
|
-
// </div>
|
|
1180
|
-
// );
|
|
1181
|
-
// });
|
|
1182
|
-
// }
|
|
1183
|
-
|
|
1184
|
-
// function GuestCard({ record }) {
|
|
1185
|
-
// return (
|
|
1186
|
-
// <Card className="card vehicle-card">
|
|
1187
|
-
// <div className="card">
|
|
1188
|
-
// <h2 className="title">{record.PName}</h2>
|
|
1189
|
-
|
|
1190
|
-
// <h4 className="values">{record.OpNo}</h4>
|
|
1191
|
-
|
|
1192
|
-
// <h3 className="values">{record.Test}</h3>
|
|
1193
|
-
|
|
1194
|
-
// <h3 className="values">Primary Result : {record.PrimaryResult || 'Pending'}</h3>
|
|
1195
|
-
|
|
1196
|
-
// <Text type="secondary">{record.Mobile}</Text>
|
|
1197
|
-
|
|
1198
|
-
// <h4 className="values">{record.Date}</h4>
|
|
1199
|
-
// </div>
|
|
1200
|
-
// </Card>
|
|
1201
|
-
// );
|
|
1202
|
-
// }
|
|
1203
|
-
// );
|
|
1204
|
-
// }
|