ui-soxo-bootstrap-core 2.6.20 → 2.6.22
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/landing-api/landing-api.js +12 -21
- package/core/lib/Store.js +5 -1
- package/core/lib/components/global-header/global-header.js +67 -37
- package/core/lib/models/forms/components/form-creator/form-creator.js +31 -8
- package/core/lib/models/forms/components/form-creator/form-creator.scss +29 -26
- package/core/models/core-scripts/core-scripts.js +134 -126
- 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/reporting-dashboard.js +177 -49
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +36 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Table, Skeleton, Input, Modal, message, Pagination } from 'antd';
|
|
3
|
+
import { Table, Skeleton, Input, Modal, message, Pagination, Tag } from 'antd';
|
|
4
4
|
|
|
5
5
|
import { QrcodeOutlined } from '@ant-design/icons';
|
|
6
6
|
|
|
@@ -17,10 +17,10 @@ import './reporting-dashboard.scss';
|
|
|
17
17
|
// import MenuDashBoard from '../../../../pages/homepage-api/menu-dashboard';
|
|
18
18
|
import MenuDashBoardComponent from '../../../../lib/elements/basic/menu-dashboard/menu-dashboard';
|
|
19
19
|
import { useHistory } from 'react-router-dom';
|
|
20
|
-
|
|
21
20
|
import * as ReportingDashboardComp from '../index';
|
|
22
21
|
import buildDisplayColumns from './display-columns/build-display-columns';
|
|
23
22
|
import { getRedirectLink } from './display-columns/display-cell-renderer';
|
|
23
|
+
import AdvancedSearchSelect from './adavance-search/advance-search';
|
|
24
24
|
|
|
25
25
|
// import { isPdfFile } from 'pdfjs-dist';
|
|
26
26
|
|
|
@@ -56,15 +56,20 @@ export default function ReportingDashboard({
|
|
|
56
56
|
const [config, setConfig] = useState({});
|
|
57
57
|
|
|
58
58
|
// State to manage the layout of the form
|
|
59
|
-
const [formLayout, setFormLayout] = useState('
|
|
59
|
+
const [formLayout, setFormLayout] = useState('vertical');
|
|
60
60
|
|
|
61
61
|
const [loading, setLoading] = useState(true);
|
|
62
62
|
|
|
63
63
|
const [cardLoading, setCardLoading] = useState(true);
|
|
64
64
|
|
|
65
|
+
const [searchParameters, setSearchParameters] = useState([]);
|
|
66
|
+
|
|
67
|
+
const [searchValues, setSearchValues] = useState({});
|
|
68
|
+
|
|
65
69
|
const [dashboardVisible, setDashBoardVisible] = useState(false);
|
|
66
70
|
|
|
67
71
|
const [formContents, setformContents] = useState({});
|
|
72
|
+
const [liveFormContents, setLiveFormContents] = useState({});
|
|
68
73
|
|
|
69
74
|
const scriptId = useRef(null);
|
|
70
75
|
|
|
@@ -112,7 +117,7 @@ export default function ReportingDashboard({
|
|
|
112
117
|
}
|
|
113
118
|
setColumns(parsedColumns);
|
|
114
119
|
|
|
115
|
-
await prepareInputParameters(result, parsedColumns);
|
|
120
|
+
await prepareInputParameters(result, parsedColumns, fetchId);
|
|
116
121
|
|
|
117
122
|
if (result.summary_columns) {
|
|
118
123
|
setSummaryColumns(JSON.parse(result.summary_columns));
|
|
@@ -157,7 +162,7 @@ export default function ReportingDashboard({
|
|
|
157
162
|
*/
|
|
158
163
|
|
|
159
164
|
//Prepare input parameters by mapping default values and binding models if needed
|
|
160
|
-
async function prepareInputParameters(record, parsedColumns) {
|
|
165
|
+
async function prepareInputParameters(record, parsedColumns, fetchId) {
|
|
161
166
|
setLoading(true);
|
|
162
167
|
let urlParams = Location.search();
|
|
163
168
|
|
|
@@ -166,9 +171,11 @@ export default function ReportingDashboard({
|
|
|
166
171
|
|
|
167
172
|
let otherDetails = record.other_details1 ? JSON.parse(record.other_details1) : null;
|
|
168
173
|
|
|
169
|
-
parameters = record.input_parameters ? JSON.parse(record.input_parameters) :
|
|
174
|
+
parameters = record.input_parameters ? JSON.parse(record.input_parameters) : [];
|
|
170
175
|
|
|
171
176
|
let formContent = {};
|
|
177
|
+
const searchFields = (parameters || []).filter((p) => p.type === 'search' && p.search_enabled === 'yes');
|
|
178
|
+
setSearchParameters([...searchFields]);
|
|
172
179
|
|
|
173
180
|
parameters = await parameters?.map((record) => {
|
|
174
181
|
// Only if the url params does have a matching value ,
|
|
@@ -212,7 +219,15 @@ export default function ReportingDashboard({
|
|
|
212
219
|
if (record.type === 'date' && !formContent[record.field]) {
|
|
213
220
|
formContent[record.field] = moment().tz(process.env.REACT_APP_TIMEZONE);
|
|
214
221
|
}
|
|
215
|
-
|
|
222
|
+
if (record.type === 'search') {
|
|
223
|
+
if (!formContent[record.field]) formContent[record.field] = [];
|
|
224
|
+
return {
|
|
225
|
+
...record,
|
|
226
|
+
reportId: fetchId,
|
|
227
|
+
onReset: () => getPatientDetails(fetchId),
|
|
228
|
+
required: record.required,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
216
231
|
if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
|
|
217
232
|
// let model = "";
|
|
218
233
|
let model = CustomModels[record.modelName];
|
|
@@ -231,6 +246,7 @@ export default function ReportingDashboard({
|
|
|
231
246
|
});
|
|
232
247
|
// Update form content state
|
|
233
248
|
setformContents(formContent);
|
|
249
|
+
setLiveFormContents(formContent);
|
|
234
250
|
|
|
235
251
|
// Trigger form submission
|
|
236
252
|
onFinish(formContent, null, record.input_parameters, parsedColumns);
|
|
@@ -242,12 +258,8 @@ export default function ReportingDashboard({
|
|
|
242
258
|
// If enabled, clear the details array
|
|
243
259
|
setDetails([]);
|
|
244
260
|
} else {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
let filter = parameters.filter((ele) => ele.type);
|
|
248
|
-
// Update the "details" state with the filtered results
|
|
249
|
-
|
|
250
|
-
setDetails([...filter]);
|
|
261
|
+
// Keep all parameters with a type (including search) to render in FormCreator
|
|
262
|
+
setDetails([...parameters.filter((ele) => ele.type)]);
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
265
|
|
|
@@ -261,6 +273,7 @@ export default function ReportingDashboard({
|
|
|
261
273
|
const { current, pageSize } = pagination || {};
|
|
262
274
|
// If card script id is exist load that id otherwise load id
|
|
263
275
|
const coreScriptId = scriptId.current ? scriptId.current : id;
|
|
276
|
+
const normalizedColumns = Array.isArray(parsedColumns) ? parsedColumns : Array.isArray(columns) ? columns : [];
|
|
264
277
|
|
|
265
278
|
setLoading(true);
|
|
266
279
|
try {
|
|
@@ -286,6 +299,7 @@ export default function ReportingDashboard({
|
|
|
286
299
|
if (scope) {
|
|
287
300
|
formBody = { body: { ...scope, ...paginationData } };
|
|
288
301
|
}
|
|
302
|
+
|
|
289
303
|
// Fetch result
|
|
290
304
|
const result = await CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr);
|
|
291
305
|
|
|
@@ -299,8 +313,8 @@ export default function ReportingDashboard({
|
|
|
299
313
|
// Update patients
|
|
300
314
|
setPatients(resultDetails || []);
|
|
301
315
|
|
|
302
|
-
//
|
|
303
|
-
if (
|
|
316
|
+
// When display_columns is missing, build columns from the response keys.
|
|
317
|
+
if (normalizedColumns.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;
|
|
@@ -330,31 +344,127 @@ export default function ReportingDashboard({
|
|
|
330
344
|
}
|
|
331
345
|
};
|
|
332
346
|
|
|
333
|
-
// Handle Submit
|
|
334
347
|
const handleSubmit = (values) => {
|
|
348
|
+
// Extract search fields from the form values
|
|
349
|
+
const searchKeys = searchParameters.map((p) => p.field);
|
|
350
|
+
const currentSearchValues = {};
|
|
351
|
+
const formValues = {};
|
|
352
|
+
|
|
353
|
+
Object.keys(values).forEach((key) => {
|
|
354
|
+
if (searchKeys.includes(key)) {
|
|
355
|
+
currentSearchValues[key] = values[key];
|
|
356
|
+
} else {
|
|
357
|
+
formValues[key] = values[key];
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const hasSearchValues = Object.values(currentSearchValues).some((v) => Array.isArray(v) && v.length > 0);
|
|
362
|
+
|
|
363
|
+
if (!hasSearchValues) {
|
|
364
|
+
runSubmit(formValues);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Check if main form values (non-search) changed
|
|
369
|
+
const formChanged = Object.keys(formValues).some((key) => {
|
|
370
|
+
const newVal = formValues[key];
|
|
371
|
+
const oldVal = formContents[key];
|
|
372
|
+
|
|
373
|
+
if (moment.isMoment(newVal) && moment.isMoment(oldVal)) {
|
|
374
|
+
return !newVal.isSame(oldVal, 'day');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return newVal !== oldVal;
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (formChanged) {
|
|
381
|
+
Modal.confirm({
|
|
382
|
+
title: 'Filters changed',
|
|
383
|
+
content: 'You changed some filters. Do you want to search using these filters also?',
|
|
384
|
+
okText: 'Yes',
|
|
385
|
+
cancelText: 'No',
|
|
386
|
+
|
|
387
|
+
onOk() {
|
|
388
|
+
// YES → send form values + search condition
|
|
389
|
+
const finalValues = {
|
|
390
|
+
...formValues,
|
|
391
|
+
search_values: currentSearchValues,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
runSubmit(finalValues);
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
onCancel() {
|
|
398
|
+
// NO → reset form values
|
|
399
|
+
const resetValues = {};
|
|
400
|
+
Object.keys(formValues).forEach((key) => {
|
|
401
|
+
resetValues[key] = null;
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const finalValues = {
|
|
405
|
+
...resetValues,
|
|
406
|
+
search_values: currentSearchValues,
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
runSubmit(finalValues);
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
} else {
|
|
413
|
+
// no form change
|
|
414
|
+
const resetValues = {};
|
|
415
|
+
Object.keys(formValues).forEach((key) => {
|
|
416
|
+
resetValues[key] = null;
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const finalValues = {
|
|
420
|
+
...resetValues,
|
|
421
|
+
search_values: currentSearchValues,
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
runSubmit(finalValues);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const runSubmit = (finalValues) => {
|
|
335
429
|
const { pageSize } = pagination;
|
|
336
430
|
const resetPage = 1;
|
|
337
431
|
|
|
338
|
-
// Reset script id on Submit
|
|
339
432
|
scriptId.current = null;
|
|
340
433
|
|
|
341
|
-
|
|
342
|
-
const currentUrlParams = Location.search();
|
|
343
|
-
const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
434
|
+
const hasSearchValues = finalValues.search_values && Object.values(finalValues.search_values).some((v) => Array.isArray(v) && v.length > 0);
|
|
344
435
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
...cleanParams
|
|
348
|
-
current: resetPage,
|
|
349
|
-
pageSize,
|
|
350
|
-
});
|
|
436
|
+
if (!hasSearchValues) {
|
|
437
|
+
const currentUrlParams = Location.search();
|
|
438
|
+
const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
351
439
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
440
|
+
const newParams = new URLSearchParams({
|
|
441
|
+
...cleanParams,
|
|
442
|
+
current: resetPage,
|
|
443
|
+
pageSize,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const newUrl = `${window.location.pathname}?${newParams.toString()}`;
|
|
447
|
+
window.history.replaceState({}, '', newUrl);
|
|
448
|
+
}
|
|
355
449
|
|
|
356
|
-
|
|
357
|
-
|
|
450
|
+
onFinish(finalValues, resetPage);
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const selectedSearchFields = details.filter((field) => {
|
|
454
|
+
if (field.type !== 'search') return false;
|
|
455
|
+
|
|
456
|
+
const value = liveFormContents[field.field];
|
|
457
|
+
return Array.isArray(value) ? value.length > 0 : !!value;
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const handleRemoveSearchField = (fieldName) => {
|
|
461
|
+
const updatedValues = {
|
|
462
|
+
...liveFormContents,
|
|
463
|
+
[fieldName]: [],
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
setLiveFormContents(updatedValues);
|
|
467
|
+
handleSubmit(updatedValues);
|
|
358
468
|
};
|
|
359
469
|
|
|
360
470
|
/**
|
|
@@ -389,6 +499,12 @@ export default function ReportingDashboard({
|
|
|
389
499
|
//Getting url friendly value by matching the url values and input_parameter values
|
|
390
500
|
let value = values[parameter.field];
|
|
391
501
|
|
|
502
|
+
// If the value is missing at the root level (which happens when search fields are moved
|
|
503
|
+
// into search_values during submission), try to retrieve it from the nested object.
|
|
504
|
+
if (value === undefined && values.search_values && values.search_values[parameter.field] !== undefined) {
|
|
505
|
+
value = values.search_values[parameter.field];
|
|
506
|
+
}
|
|
507
|
+
|
|
392
508
|
// Keep Moment object in state for picker
|
|
393
509
|
if (parameter.type === 'date' && value) {
|
|
394
510
|
formContent[parameter.field] = value.isValid ? value : moment(value); // ensure Moment
|
|
@@ -410,6 +526,7 @@ export default function ReportingDashboard({
|
|
|
410
526
|
);
|
|
411
527
|
|
|
412
528
|
setformContents(formContent);
|
|
529
|
+
setLiveFormContents(formContent);
|
|
413
530
|
Location.search({ ...Location.search(), ...filteredParams });
|
|
414
531
|
}
|
|
415
532
|
|
|
@@ -505,17 +622,18 @@ export default function ReportingDashboard({
|
|
|
505
622
|
}}
|
|
506
623
|
styles={{ paddingRight: '15px', alignItems: 'center' }}
|
|
507
624
|
fields={details}
|
|
625
|
+
reportId={id}
|
|
508
626
|
formContent={formContents}
|
|
509
627
|
// formContent={{ [model]: {} }}
|
|
510
628
|
modelIndex="requestId"
|
|
511
629
|
model={model}
|
|
512
630
|
onSubmit={handleSubmit}
|
|
631
|
+
onFormValuesChange={setLiveFormContents}
|
|
513
632
|
callback={() => {
|
|
514
633
|
// history.goBack();
|
|
515
634
|
}}
|
|
516
635
|
/>
|
|
517
636
|
) : null}
|
|
518
|
-
{/* </Card> */}
|
|
519
637
|
</div>
|
|
520
638
|
|
|
521
639
|
{/** GuestList component start*/}
|
|
@@ -533,6 +651,8 @@ export default function ReportingDashboard({
|
|
|
533
651
|
pagination={pagination}
|
|
534
652
|
handlePagination={handlePagination}
|
|
535
653
|
attributes={attributes}
|
|
654
|
+
selectedSearchFields={selectedSearchFields}
|
|
655
|
+
handleRemoveSearchField={handleRemoveSearchField}
|
|
536
656
|
fetchReportData={(paginationUpdate) => fetchReportData(id, formContents, dbPtr, paginationUpdate || pagination)}
|
|
537
657
|
/>
|
|
538
658
|
{/** GuestList component end*/}
|
|
@@ -566,6 +686,8 @@ function GuestList({
|
|
|
566
686
|
pagination,
|
|
567
687
|
handlePagination,
|
|
568
688
|
attributes,
|
|
689
|
+
selectedSearchFields,
|
|
690
|
+
handleRemoveSearchField,
|
|
569
691
|
fetchReportData,
|
|
570
692
|
}) {
|
|
571
693
|
/**
|
|
@@ -824,13 +946,31 @@ function GuestList({
|
|
|
824
946
|
<>
|
|
825
947
|
<div className="table-header">
|
|
826
948
|
<div className="table-left">
|
|
827
|
-
{/*
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
949
|
+
{/* {selectedSearchFields?.length > 0 ? (
|
|
950
|
+
<div className="search-tags-container">
|
|
951
|
+
{selectedSearchFields.map((field) => (
|
|
952
|
+
<Tag key={field.field} closable color="blue" onClose={() => handleRemoveSearchField(field.field)}>
|
|
953
|
+
{field.caption}
|
|
954
|
+
</Tag>
|
|
955
|
+
))}
|
|
956
|
+
</div>
|
|
957
|
+
) : null} */}
|
|
831
958
|
</div>
|
|
832
959
|
|
|
833
960
|
<div className="table-right">
|
|
961
|
+
{/* shwoing caption is not correct so this commented */}
|
|
962
|
+
{/* <span className="menu-caption">{config.caption}</span> */}
|
|
963
|
+
<Search className="table-search-input" placeholder="Enter Search Value" allowClear onChange={onSearch} />
|
|
964
|
+
<div className="table-export-button">
|
|
965
|
+
{exportData.exportDatas && (
|
|
966
|
+
<ExportReactCSV
|
|
967
|
+
title={config.caption}
|
|
968
|
+
headers={exportData.exportDatas.exportDataHeaders}
|
|
969
|
+
csvData={exportData.exportDatas.exportDataColumns}
|
|
970
|
+
/>
|
|
971
|
+
)}
|
|
972
|
+
</div>
|
|
973
|
+
|
|
834
974
|
{/* QR Scan start */}
|
|
835
975
|
{showScanner ? (
|
|
836
976
|
<Button size="small" type="primary" icon={<QrcodeOutlined />} onClick={() => setScannerVisible(true)}>
|
|
@@ -863,19 +1003,7 @@ function GuestList({
|
|
|
863
1003
|
<Modal open={isScannerVisible} title="Scan QR Code" footer={null} onCancel={() => setScannerVisible(false)} destroyOnClose>
|
|
864
1004
|
<QrScanner onScanSuccess={handleScanSuccess} onClose={() => setScannerVisible(false)} />
|
|
865
1005
|
</Modal>
|
|
866
|
-
|
|
867
|
-
<div>
|
|
868
|
-
{/* QR Scan End */}
|
|
869
|
-
{/*table data export to csc component*/}
|
|
870
|
-
{exportData.exportDatas && (
|
|
871
|
-
<ExportReactCSV
|
|
872
|
-
title={config.caption}
|
|
873
|
-
fileName={`${(config.caption || 'Report').trim().replace(/\s+/g, '_')}_${moment().format('YYYY-MM-DD-HH-mm-ss-SSS')}.xlsx`}
|
|
874
|
-
headers={exportData.exportDatas.exportDataHeaders}
|
|
875
|
-
csvData={exportData.exportDatas.exportDataColumns}
|
|
876
|
-
/>
|
|
877
|
-
)}
|
|
878
|
-
</div>
|
|
1006
|
+
{/* QR Scan End */}
|
|
879
1007
|
</div>
|
|
880
1008
|
</div>
|
|
881
1009
|
|
|
@@ -16,12 +16,17 @@
|
|
|
16
16
|
display: flex;
|
|
17
17
|
justify-content: space-between;
|
|
18
18
|
align-items: center;
|
|
19
|
+
gap: 12px;
|
|
19
20
|
padding-bottom: 10px;
|
|
20
21
|
padding-top: 10px;
|
|
21
22
|
|
|
22
23
|
.table-left {
|
|
23
24
|
display: flex;
|
|
24
25
|
align-items: center;
|
|
26
|
+
gap: 8px;
|
|
27
|
+
flex: 1;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
margin-left: 6px;
|
|
25
30
|
|
|
26
31
|
.menu-caption {
|
|
27
32
|
min-width: 100px;
|
|
@@ -31,9 +36,23 @@
|
|
|
31
36
|
|
|
32
37
|
.table-right{
|
|
33
38
|
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: flex-end;
|
|
41
|
+
flex-wrap: nowrap;
|
|
34
42
|
gap: 4px;
|
|
35
43
|
}
|
|
36
44
|
}
|
|
45
|
+
|
|
46
|
+
.table-search-input {
|
|
47
|
+
width: 220px;
|
|
48
|
+
min-width: 220px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.table-export-button {
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
flex-shrink: 0;
|
|
55
|
+
}
|
|
37
56
|
|
|
38
57
|
|
|
39
58
|
.form-card {
|
|
@@ -48,6 +67,23 @@
|
|
|
48
67
|
padding: 0px;
|
|
49
68
|
}
|
|
50
69
|
|
|
70
|
+
.search-tags-container {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
flex-wrap: nowrap;
|
|
74
|
+
gap: 8px;
|
|
75
|
+
min-width: 0;
|
|
76
|
+
max-width: 100%;
|
|
77
|
+
overflow-x: auto;
|
|
78
|
+
overflow-y: hidden;
|
|
79
|
+
white-space: nowrap;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.search-tags-container .ant-tag {
|
|
83
|
+
flex-shrink: 0;
|
|
84
|
+
margin-inline-end: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
51
87
|
// margin: 0px 10px;
|
|
52
88
|
|
|
53
89
|
.ant-skeleton {
|