ui-soxo-bootstrap-core 2.6.21 → 2.6.23
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/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,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Route, Switch } from 'react-router-dom';
|
|
4
4
|
|
|
5
5
|
import { Skeleton } from 'antd';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Card, ChangePassword, GlobalContext, GlobalHeader, ModuleRoutes, Profile, SettingsUtil, SpotlightSearch, useTranslation } from '../../lib';
|
|
8
8
|
|
|
9
9
|
import './landing-api.scss';
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ import ReportingDashboard from '../../modules/reporting/components/reporting-das
|
|
|
16
16
|
|
|
17
17
|
import PropTypes from 'prop-types';
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { CoreScripts, MenusAPI } from '../../models';
|
|
20
20
|
|
|
21
21
|
const motivatingMessages = [
|
|
22
22
|
'Setting things up for a great start...',
|
|
@@ -159,7 +159,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
159
159
|
* @param reports
|
|
160
160
|
*/
|
|
161
161
|
async function loadMenus(reports) {
|
|
162
|
-
|
|
163
162
|
setLoader(true);
|
|
164
163
|
|
|
165
164
|
// setReports(report)
|
|
@@ -169,16 +168,12 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
169
168
|
// console.log(result);
|
|
170
169
|
|
|
171
170
|
if (result && Array.isArray(result.result) && result.result.length) {
|
|
172
|
-
|
|
173
171
|
// setModules(result.result);
|
|
174
|
-
|
|
175
172
|
// result.result.map((ele) => {
|
|
176
173
|
// let languageString = JSON.parse(ele.attributes)
|
|
177
174
|
// console.log('language_string', languageString);
|
|
178
175
|
// if (languageString && languageString.languages) {
|
|
179
|
-
|
|
180
176
|
// const language = i18n.language;
|
|
181
|
-
|
|
182
177
|
// i18n.addResourceBundle(language, 'translation', languageString.languages[i18n.language]);
|
|
183
178
|
// }
|
|
184
179
|
// })
|
|
@@ -189,7 +184,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
189
184
|
dispatch({ type: 'settings', payload: result.result.settings });
|
|
190
185
|
}
|
|
191
186
|
|
|
192
|
-
|
|
193
187
|
// Reports length
|
|
194
188
|
if (reports.length) {
|
|
195
189
|
reportMenus = [
|
|
@@ -224,7 +218,6 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
224
218
|
//If there is no roles assigned to the user
|
|
225
219
|
setAllModules([...coreModules]);
|
|
226
220
|
}
|
|
227
|
-
|
|
228
221
|
} else {
|
|
229
222
|
// for nura
|
|
230
223
|
if (result && result.result.menus && reportMenus) {
|
|
@@ -233,14 +226,10 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
233
226
|
//If there is no roles assigned to the user
|
|
234
227
|
setAllModules([...coreModules]);
|
|
235
228
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
229
|
}
|
|
239
230
|
setLoader(false);
|
|
240
|
-
|
|
241
231
|
}
|
|
242
232
|
|
|
243
|
-
|
|
244
233
|
/**
|
|
245
234
|
* Load the scripts
|
|
246
235
|
*
|
|
@@ -505,16 +494,18 @@ function DefineRoute({ menus, CustomComponents, CustomModels, settings, callback
|
|
|
505
494
|
function printRoute(menu, index, CustomComponents, CustomModels, callback) {
|
|
506
495
|
// From the path we remove any query parameters that exist in the url
|
|
507
496
|
//
|
|
508
|
-
let paths;
|
|
497
|
+
let paths = [];
|
|
509
498
|
|
|
510
499
|
//There will be path for normal menus
|
|
511
|
-
if (menu
|
|
500
|
+
if (menu?.path) {
|
|
512
501
|
paths = menu.path.split('?');
|
|
513
|
-
} else {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
502
|
+
} else if (Array.isArray(menu?.sub_menus)) {
|
|
503
|
+
for (const subMenu of menu.sub_menus) {
|
|
504
|
+
if (subMenu?.path) {
|
|
505
|
+
paths = subMenu.path.split('?');
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
518
509
|
}
|
|
519
510
|
|
|
520
511
|
// Find the route path
|
|
@@ -35,6 +35,8 @@ import { FieldCustomizer, TabCustomizer } from "./../../../../";
|
|
|
35
35
|
|
|
36
36
|
import { prepareAndExecuteScript } from './../../../../utils/script.utils';
|
|
37
37
|
|
|
38
|
+
import AdvancedSearchSelect from "../../../../../modules/reporting/components/reporting-dashboard/adavance-search/advance-search";
|
|
39
|
+
|
|
38
40
|
const { TextArea } = Input;
|
|
39
41
|
|
|
40
42
|
const { Option } = Select;
|
|
@@ -59,8 +61,10 @@ function FormCreator({
|
|
|
59
61
|
formContent = {},
|
|
60
62
|
onSubmit,
|
|
61
63
|
fields = [],
|
|
64
|
+
reportId,
|
|
62
65
|
callback,
|
|
63
66
|
layout,
|
|
67
|
+
onFormValuesChange,
|
|
64
68
|
// Below are arguments for use in form display
|
|
65
69
|
onFieldUpdate,
|
|
66
70
|
// onListUpdate,
|
|
@@ -69,7 +73,6 @@ function FormCreator({
|
|
|
69
73
|
const [form] = Form.useForm();
|
|
70
74
|
|
|
71
75
|
let layoutValue;
|
|
72
|
-
|
|
73
76
|
if (layout) {
|
|
74
77
|
layoutValue = layout
|
|
75
78
|
} else {
|
|
@@ -194,11 +197,15 @@ function FormCreator({
|
|
|
194
197
|
values = await prepareAndExecuteScript(values, null, fieldConfiguration.on_change);
|
|
195
198
|
|
|
196
199
|
}
|
|
200
|
+
|
|
201
|
+
if (onFormValuesChange) {
|
|
202
|
+
onFormValuesChange(values);
|
|
203
|
+
}
|
|
197
204
|
}
|
|
198
205
|
|
|
199
206
|
return (
|
|
200
207
|
<section className="form-creator">
|
|
201
|
-
|
|
208
|
+
|
|
202
209
|
{/* <DragDropContext onDragEnd={onDragEnd}>
|
|
203
210
|
<Droppable droppableId="droppable">
|
|
204
211
|
|
|
@@ -251,6 +258,8 @@ function FormCreator({
|
|
|
251
258
|
});
|
|
252
259
|
|
|
253
260
|
}}
|
|
261
|
+
// layout="inline"
|
|
262
|
+
|
|
254
263
|
onFieldsChange={onFieldsChange}
|
|
255
264
|
|
|
256
265
|
onValuesChange={onValuesChange}
|
|
@@ -265,6 +274,7 @@ function FormCreator({
|
|
|
265
274
|
{/* Mapper maps each fields to build the form */}
|
|
266
275
|
<FieldMapper
|
|
267
276
|
fields={fields}
|
|
277
|
+
reportId={reportId}
|
|
268
278
|
onChange={onChange}
|
|
269
279
|
selectedInformation={selectedInformation}
|
|
270
280
|
onUpload={onUpload}
|
|
@@ -278,7 +288,7 @@ function FormCreator({
|
|
|
278
288
|
SUBMIT
|
|
279
289
|
</Button>
|
|
280
290
|
</Form>
|
|
281
|
-
|
|
291
|
+
|
|
282
292
|
{/* </div>)}
|
|
283
293
|
|
|
284
294
|
</Droppable>
|
|
@@ -299,6 +309,7 @@ export default FormCreator;
|
|
|
299
309
|
*/
|
|
300
310
|
function FieldMapper({
|
|
301
311
|
fields = [],
|
|
312
|
+
reportId,
|
|
302
313
|
onChange,
|
|
303
314
|
selectedInformation,
|
|
304
315
|
onUpload,
|
|
@@ -337,6 +348,7 @@ function FieldMapper({
|
|
|
337
348
|
{/* Mapper maps each fields to build the form */}
|
|
338
349
|
<FieldMapper
|
|
339
350
|
fields={tab.fields}
|
|
351
|
+
reportId={reportId}
|
|
340
352
|
onChange={onChange}
|
|
341
353
|
onUpload={onUpload}
|
|
342
354
|
onFieldUpdate={onFieldUpdate}
|
|
@@ -358,6 +370,7 @@ function FieldMapper({
|
|
|
358
370
|
?
|
|
359
371
|
<UserInput
|
|
360
372
|
onChange={onChange}
|
|
373
|
+
reportId={reportId}
|
|
361
374
|
index={index}
|
|
362
375
|
key={index}
|
|
363
376
|
onUpload={onUpload}
|
|
@@ -371,6 +384,7 @@ function FieldMapper({
|
|
|
371
384
|
} else {
|
|
372
385
|
return <UserInput
|
|
373
386
|
onChange={onChange}
|
|
387
|
+
reportId={reportId}
|
|
374
388
|
key={index}
|
|
375
389
|
selectedInformation={selectedInformation}
|
|
376
390
|
index={index}
|
|
@@ -397,7 +411,7 @@ function FieldMapper({
|
|
|
397
411
|
*
|
|
398
412
|
* @param {*} param0
|
|
399
413
|
*/
|
|
400
|
-
function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpdate, onFieldRemove, index }) {
|
|
414
|
+
function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpdate, onFieldRemove, index, reportId }) {
|
|
401
415
|
|
|
402
416
|
let props = {};
|
|
403
417
|
|
|
@@ -423,8 +437,11 @@ function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpda
|
|
|
423
437
|
|
|
424
438
|
switch (field.type) {
|
|
425
439
|
|
|
440
|
+
case 'search':
|
|
441
|
+
return <AdvancedSearchSelect {...field} reportId={reportId} style={{ width: '100%' }} />
|
|
442
|
+
|
|
426
443
|
case 'number':
|
|
427
|
-
return <InputNumber
|
|
444
|
+
return <InputNumber required={field.required} />
|
|
428
445
|
|
|
429
446
|
case 'input':
|
|
430
447
|
return <Input required={field.required} />
|
|
@@ -459,7 +476,7 @@ function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpda
|
|
|
459
476
|
return <TextArea rows={4} required={field.required} />
|
|
460
477
|
|
|
461
478
|
case 'boolean':
|
|
462
|
-
return <Select style={{ width: 120 }} required={field.required}>
|
|
479
|
+
return <Select style={{ width: '120' }} required={field.required}>
|
|
463
480
|
{[true, false].map((option, key) => <Option key={key} value={option}>{option ? 'Yes' : 'No'}</Option>)}
|
|
464
481
|
</Select>
|
|
465
482
|
|
|
@@ -495,7 +512,7 @@ function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpda
|
|
|
495
512
|
return (
|
|
496
513
|
<Select defaultValue={defaultValue}
|
|
497
514
|
required={field.required}
|
|
498
|
-
style={{ width: 120 }}
|
|
515
|
+
style={{ width: '120' }}
|
|
499
516
|
onChange={(value) => onChange(field, value)}
|
|
500
517
|
>
|
|
501
518
|
{field.options.map((option, key) => (
|
|
@@ -605,7 +622,13 @@ function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpda
|
|
|
605
622
|
<FieldCustomizer field={field} onFieldUpdate={onFieldUpdate} index={index} onFieldRemove={onFieldRemove} />
|
|
606
623
|
{/* Customizes the form Ends */}
|
|
607
624
|
|
|
608
|
-
<Form.Item
|
|
625
|
+
<Form.Item
|
|
626
|
+
{...props}
|
|
627
|
+
name={field.field}
|
|
628
|
+
label={field.caption}
|
|
629
|
+
rules={[{ required: field.required, message: field.placeholder || 'Please enter ' + field.caption }]}
|
|
630
|
+
style={{ marginBottom: '12px' }}
|
|
631
|
+
>
|
|
609
632
|
|
|
610
633
|
{inputElement(field, onChange)}
|
|
611
634
|
{/* <InputElement field={field} /> */}
|
|
@@ -1,31 +1,34 @@
|
|
|
1
1
|
.form-creator {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
.new-record {
|
|
7
|
-
gap: 10px !important;
|
|
8
|
-
}
|
|
2
|
+
.ant-input-number {
|
|
3
|
+
width: 100%;
|
|
4
|
+
}
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.field-customizer {
|
|
18
|
-
.actions {
|
|
19
|
-
// display: none;
|
|
20
|
-
float: right;
|
|
21
|
-
// position: absolute;
|
|
22
|
-
// right: 0px;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
6
|
+
.new-record {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-wrap: wrap;
|
|
9
|
+
align-items: flex-start;
|
|
10
|
+
gap: 10px !important;
|
|
11
|
+
}
|
|
25
12
|
|
|
13
|
+
.form-item-element {
|
|
14
|
+
position: relative;
|
|
15
|
+
padding: 4px;
|
|
16
|
+
// border-radius: 4px;
|
|
17
|
+
// border: 1px solid #e3e3e3;
|
|
18
|
+
// margin-bottom: 3px;
|
|
19
|
+
min-width: 140px;
|
|
20
|
+
// flex: 0 1 auto;
|
|
21
|
+
.field-customizer {
|
|
22
|
+
.actions {
|
|
23
|
+
// display: none;
|
|
24
|
+
float: right;
|
|
25
|
+
// position: absolute;
|
|
26
|
+
// right: 0px;
|
|
27
|
+
}
|
|
26
28
|
}
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
31
|
+
.submit-button {
|
|
32
|
+
margin-top: 34px;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -9,142 +9,150 @@ import React from 'react';
|
|
|
9
9
|
|
|
10
10
|
import Base from '../base/base';
|
|
11
11
|
|
|
12
|
-
import { ApiUtils } from './../../lib/'
|
|
13
|
-
|
|
12
|
+
import { ApiUtils } from './../../lib/';
|
|
14
13
|
|
|
15
14
|
class CoreScript extends Base {
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
field: 'name',
|
|
23
|
-
caption: 'Name'
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
field: 'amount',
|
|
27
|
-
caption: 'Amount'
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
// this.columns = ;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get id() {
|
|
35
|
-
return 'id';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get getEndpoint() {
|
|
39
|
-
return 'core-scripts';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
get modelName() {
|
|
44
|
-
return `core-scripts`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
get columns() {
|
|
48
|
-
return [
|
|
49
|
-
{
|
|
50
|
-
caption: 'Staff',
|
|
51
|
-
field: 'staff.name',
|
|
52
|
-
key: 'staff'
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
caption: 'Weight',
|
|
56
|
-
field: 'weight',
|
|
57
|
-
key: 'weight'
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
caption: 'Date',
|
|
61
|
-
field: 'created_at',
|
|
62
|
-
key: 'created_at'
|
|
63
|
-
}
|
|
64
|
-
];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
*Getting core_script data
|
|
70
|
-
* @returns
|
|
71
|
-
*/
|
|
72
|
-
getReportingLisitng = (id, formBody, dbPtr = null) => {
|
|
73
|
-
|
|
74
|
-
// Settings db pointer
|
|
75
|
-
if (!dbPtr) dbPtr = localStorage.db_ptr;
|
|
76
|
-
return ApiUtils.post({
|
|
77
|
-
url: `core-scripts/dashboardquery/${id}`,
|
|
78
|
-
formBody,
|
|
79
|
-
headers: {
|
|
80
|
-
'Content-Type': 'application/json',
|
|
81
|
-
Authorization: 'Bearer ' + localStorage.access_token,
|
|
82
|
-
db_ptr: dbPtr
|
|
18
|
+
this.fields = [
|
|
19
|
+
{
|
|
20
|
+
field: 'name',
|
|
21
|
+
caption: 'Name',
|
|
83
22
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
23
|
+
{
|
|
24
|
+
field: 'amount',
|
|
25
|
+
caption: 'Amount',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
87
28
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
* @returns
|
|
91
|
-
*/
|
|
92
|
-
getUserDetailsLisitng = (id, formBody) => {
|
|
29
|
+
// this.columns = ;
|
|
30
|
+
}
|
|
93
31
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
};
|
|
32
|
+
get id() {
|
|
33
|
+
return 'id';
|
|
34
|
+
}
|
|
99
35
|
|
|
36
|
+
get getEndpoint() {
|
|
37
|
+
return 'core-scripts';
|
|
38
|
+
}
|
|
100
39
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
getReportMenu = async ({ id }) => {
|
|
105
|
-
|
|
106
|
-
const result = await this.getRecord({ id });
|
|
107
|
-
|
|
108
|
-
const report = result.result
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
...report,
|
|
112
|
-
caption: report.caption,
|
|
113
|
-
path: '/reports/' + report.id,
|
|
114
|
-
is_visible: true
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* To get extra script data corresponding to mode
|
|
120
|
-
*
|
|
121
|
-
* @param {*} mode
|
|
122
|
-
* @returns
|
|
123
|
-
*/
|
|
124
|
-
getExtraInfo = (mode) => {
|
|
125
|
-
return ApiUtils.get({
|
|
126
|
-
url: `core-scripts/get-script-data?mode=${mode}`,
|
|
127
|
-
});
|
|
128
|
-
};
|
|
40
|
+
get modelName() {
|
|
41
|
+
return `core-scripts`;
|
|
42
|
+
}
|
|
129
43
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
44
|
+
get columns() {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
caption: 'Staff',
|
|
48
|
+
field: 'staff.name',
|
|
49
|
+
key: 'staff',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
caption: 'Weight',
|
|
53
|
+
field: 'weight',
|
|
54
|
+
key: 'weight',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
caption: 'Date',
|
|
58
|
+
field: 'created_at',
|
|
59
|
+
key: 'created_at',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
*Getting core_script data
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
getReportingLisitng = (id, formBody, dbPtr = null) => {
|
|
69
|
+
// Settings db pointer
|
|
70
|
+
if (!dbPtr) dbPtr = localStorage.db_ptr;
|
|
71
|
+
return ApiUtils.post({
|
|
72
|
+
// baseUrl: 'http://localhost:8002/dev/',
|
|
73
|
+
url: `core-scripts/dashboardquery/${id}`,
|
|
74
|
+
formBody,
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
Authorization: 'Bearer ' + localStorage.access_token,
|
|
78
|
+
db_ptr: dbPtr,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*Updating user details
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
87
|
+
getUserDetailsLisitng = (id, formBody) => {
|
|
88
|
+
return ApiUtils.post({
|
|
89
|
+
url: `core-scripts/update-user-deatils/${id}`,
|
|
90
|
+
formBody,
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
getQuery = (formBody) => {
|
|
95
|
+
return ApiUtils.post({
|
|
96
|
+
url: `core-scripts/execute-script-api`,
|
|
97
|
+
formBody,
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
103
|
+
getQuerySeacch = (formBody) => {
|
|
104
|
+
return ApiUtils.post({
|
|
105
|
+
// baseUrl: 'http://localhost:8002/dev/',
|
|
106
|
+
url: `core-scripts/execute-script-by-search`,
|
|
107
|
+
formBody,
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
getReportMenu = async ({ id }) => {
|
|
115
|
+
const result = await this.getRecord({ id });
|
|
116
|
+
|
|
117
|
+
const report = result.result;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
...report,
|
|
121
|
+
caption: report.caption,
|
|
122
|
+
path: '/reports/' + report.id,
|
|
123
|
+
is_visible: true,
|
|
147
124
|
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* To get extra script data corresponding to mode
|
|
129
|
+
*
|
|
130
|
+
* @param {*} mode
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
133
|
+
getExtraInfo = (mode) => {
|
|
134
|
+
return ApiUtils.get({
|
|
135
|
+
url: `core-scripts/get-script-data?mode=${mode}`,
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* To get executed data
|
|
141
|
+
*
|
|
142
|
+
* @param {*} mode
|
|
143
|
+
* @returns
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
getSelectedExtraInfo = (formBody) => {
|
|
147
|
+
return ApiUtils.post({
|
|
148
|
+
url: `core-scripts/get-selected-script-data`,
|
|
149
|
+
formBody,
|
|
150
|
+
headers: {
|
|
151
|
+
'Content-Type': 'application/json',
|
|
152
|
+
Authorization: 'Bearer ' + localStorage.access_token,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
};
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
export default CoreScript;
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Select, Checkbox, Input, Spin } from "antd";
|
|
4
|
+
import { CoreScripts } from "../../../../../models";
|
|
5
|
+
import "./advance-search.scss";
|
|
6
|
+
|
|
7
|
+
const { Search } = Input;
|
|
8
|
+
|
|
9
|
+
export default function AdvancedSearchSelect({ reportId, onReset, field, value, onChange, style, ...rest }) {
|
|
10
|
+
// Normalize the configuration.
|
|
11
|
+
// If 'field' is an object, we are in "legacy" mode. If it's a string, we are using the spread props from rest.
|
|
12
|
+
const isObjectMode = typeof field === "object";
|
|
13
|
+
const config = isObjectMode ? field : { ...rest, field };
|
|
14
|
+
|
|
15
|
+
const finalReportId = reportId || config.reportId;
|
|
16
|
+
const finalOnReset = onReset || config.onReset;
|
|
17
|
+
const isSearchQuery = !!config.search_query;
|
|
18
|
+
const fieldName = isObjectMode ? field.field : field;
|
|
19
|
+
const caption = config.caption;
|
|
20
|
+
|
|
21
|
+
const safeValue = Array.isArray(value) ? value : [];
|
|
22
|
+
|
|
23
|
+
const [searchResults, setSearchResults] = useState([]);
|
|
24
|
+
const [displayOptions, setDisplayOptions] = useState([]);
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
const debounceRef = useRef(null);
|
|
27
|
+
|
|
28
|
+
const loadOptions = async (searchText = "") => {
|
|
29
|
+
try {
|
|
30
|
+
setLoading(true);
|
|
31
|
+
|
|
32
|
+
const formBody = {
|
|
33
|
+
script_id: finalReportId,
|
|
34
|
+
search_field: fieldName,
|
|
35
|
+
search_condition: searchText,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const res = await CoreScripts.getQuerySeacch(formBody);
|
|
39
|
+
const data = Array.isArray(res.data) ? res.data : [];
|
|
40
|
+
const apiValues = data?.map((r) => r[fieldName]) || [];
|
|
41
|
+
|
|
42
|
+
setSearchResults(apiValues);
|
|
43
|
+
// Refresh display options only when new search results arrive to keep the list stable during interaction
|
|
44
|
+
setDisplayOptions([
|
|
45
|
+
...safeValue,
|
|
46
|
+
...apiValues.filter((item) => !safeValue.includes(item)),
|
|
47
|
+
]);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("Search API error", error);
|
|
50
|
+
} finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
// Clear search results when the field or report context changes,
|
|
57
|
+
// but do not load automatically on mount.
|
|
58
|
+
setSearchResults([]);
|
|
59
|
+
}, [fieldName, finalReportId]);
|
|
60
|
+
|
|
61
|
+
const handleSearch = (text) => {
|
|
62
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
63
|
+
debounceRef.current = setTimeout(() => loadOptions(text), 400);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const toggleValue = (checked, item) => {
|
|
67
|
+
let newValues;
|
|
68
|
+
|
|
69
|
+
if (checked) newValues = [...safeValue, item];
|
|
70
|
+
else newValues = safeValue.filter((v) => v !== item);
|
|
71
|
+
|
|
72
|
+
onChange(newValues);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleReset = () => {
|
|
76
|
+
onChange([]);
|
|
77
|
+
if (finalOnReset) {
|
|
78
|
+
finalOnReset(); // 🔥 trigger dashboard refresh
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const isActive = safeValue.length > 0;
|
|
83
|
+
|
|
84
|
+
// -------- INPUT MODE --------
|
|
85
|
+
if (!isSearchQuery) {
|
|
86
|
+
return (
|
|
87
|
+
<Input
|
|
88
|
+
allowClear
|
|
89
|
+
className={`advanced-search-input ${isActive ? "advanced-search-active" : ""}`}
|
|
90
|
+
placeholder={`Search ${caption}`}
|
|
91
|
+
style={style}
|
|
92
|
+
// The parent provides an array for `value`.
|
|
93
|
+
// We take the first element for the input's display value.
|
|
94
|
+
value={safeValue[0] || ""}
|
|
95
|
+
onChange={(e) => {
|
|
96
|
+
const text = e?.target?.value;
|
|
97
|
+
// Always pass an array back to the parent to be consistent with the Select mode.
|
|
98
|
+
onChange(text ? [text] : []);
|
|
99
|
+
if (!text && finalOnReset) {
|
|
100
|
+
finalOnReset();
|
|
101
|
+
}
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// -------- SELECT MODE --------
|
|
108
|
+
return (
|
|
109
|
+
<Select
|
|
110
|
+
className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
|
|
111
|
+
style={style}
|
|
112
|
+
mode="multiple"
|
|
113
|
+
value={safeValue}
|
|
114
|
+
placeholder={caption}
|
|
115
|
+
onDropdownVisibleChange={(open) => {
|
|
116
|
+
if (open) {
|
|
117
|
+
loadOptions("");
|
|
118
|
+
} else {
|
|
119
|
+
// When closing, re-sort selected items to the top so they are visible next time it opens
|
|
120
|
+
const currentResults = searchResults.length > 0 ? searchResults : displayOptions;
|
|
121
|
+
setDisplayOptions([
|
|
122
|
+
...safeValue,
|
|
123
|
+
...currentResults.filter((item) => !safeValue.includes(item)),
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
}}
|
|
127
|
+
allowClear
|
|
128
|
+
maxTagCount={1}
|
|
129
|
+
onChange={onChange}
|
|
130
|
+
maxTagPlaceholder={(omittedValues) => (
|
|
131
|
+
<span className="tag-placeholder-count">
|
|
132
|
+
+{omittedValues.length}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
dropdownRender={() => (
|
|
136
|
+
<div className="dropdown-content">
|
|
137
|
+
<Search
|
|
138
|
+
placeholder={`Search ${caption}`}
|
|
139
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
<div className="dropdown-options-list">
|
|
143
|
+
{loading ? (
|
|
144
|
+
<Spin />
|
|
145
|
+
) : (
|
|
146
|
+
displayOptions.map((item, idx) => (
|
|
147
|
+
<div
|
|
148
|
+
key={`${item}-${idx}`}
|
|
149
|
+
className="dropdown-option-item"
|
|
150
|
+
onClick={() => toggleValue(!safeValue.includes(item), item)}
|
|
151
|
+
style={{ cursor: 'pointer' }}
|
|
152
|
+
>
|
|
153
|
+
<Checkbox
|
|
154
|
+
checked={safeValue.includes(item)}
|
|
155
|
+
onClick={(e) => e.stopPropagation()} // Prevent double trigger when clicking the checkbox box specifically
|
|
156
|
+
onChange={(e) => toggleValue(e.target.checked, item)}
|
|
157
|
+
>
|
|
158
|
+
{item}
|
|
159
|
+
</Checkbox>
|
|
160
|
+
</div>
|
|
161
|
+
))
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="dropdown-footer">
|
|
166
|
+
<span className="dropdown-reset-button" onClick={handleReset}>
|
|
167
|
+
Reset
|
|
168
|
+
</span>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// .advanced-search-select,
|
|
2
|
+
/* Base select width */
|
|
3
|
+
.advanced-search-select {
|
|
4
|
+
width: 160px;
|
|
5
|
+
// width: 100%;
|
|
6
|
+
}
|
|
7
|
+
.advanced-search-input {
|
|
8
|
+
width: 160px;
|
|
9
|
+
}
|
|
10
|
+
/* Active state (when value selected) */
|
|
11
|
+
.advanced-search-select.advanced-search-active .ant-select-selector {
|
|
12
|
+
border-color: #1677ff !important;
|
|
13
|
+
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.2) !important;
|
|
14
|
+
border-radius: 6px !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Input active state */
|
|
18
|
+
.advanced-search-input.advanced-search-active {
|
|
19
|
+
border-color: #1677ff !important;
|
|
20
|
+
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.2);
|
|
21
|
+
border-radius: 6px;
|
|
22
|
+
}
|
|
23
|
+
// .advanced-search-input {
|
|
24
|
+
// width: 250px;
|
|
25
|
+
|
|
26
|
+
// &.advanced-search-active {
|
|
27
|
+
// // Antd Select and Input don't share the same border-radius property,
|
|
28
|
+
// // so we apply a wrapper style that works for both.
|
|
29
|
+
// // For more specific control, you might need to target internal antd classes.
|
|
30
|
+
// border: 1px solid #91caff !important;
|
|
31
|
+
// box-shadow: 0 0 0 1px #91caff;
|
|
32
|
+
// border-radius: 6px;
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
.tag-placeholder {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
|
|
40
|
+
.tag-placeholder-count {
|
|
41
|
+
background: #e6f0ff;
|
|
42
|
+
color: #1677ff;
|
|
43
|
+
border-radius: 10px;
|
|
44
|
+
padding: 0 8px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
font-size: 12px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dropdown-content {
|
|
51
|
+
padding: 10px;
|
|
52
|
+
|
|
53
|
+
.dropdown-options-list {
|
|
54
|
+
max-height: 200px;
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
margin-top: 10px;
|
|
57
|
+
|
|
58
|
+
.dropdown-option-item {
|
|
59
|
+
margin-bottom: 6px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.dropdown-footer {
|
|
64
|
+
display: flex;
|
|
65
|
+
justify-content: flex-end;
|
|
66
|
+
margin-top: 10px;
|
|
67
|
+
border-top: 1px solid #f0f0f0;
|
|
68
|
+
padding-top: 8px;
|
|
69
|
+
|
|
70
|
+
.dropdown-reset-button {
|
|
71
|
+
color: #1677ff;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -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 {
|