ui-soxo-bootstrap-core 2.6.1-dev.10 → 2.6.1-dev.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/components/landing-api/landing-api.js +66 -2
- package/core/components/landing-api/landing-api.scss +22 -0
- package/core/lib/models/forms/components/form-creator/form-creator.scss +1 -1
- package/core/models/core-scripts/core-scripts.js +134 -135
- package/core/models/menus/components/menu-lists/menu-lists.js +49 -46
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +59 -32
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +101 -147
- package/core/modules/steps/action-buttons.js +6 -0
- package/core/modules/steps/action-buttons.scss +9 -3
- package/core/modules/steps/steps.js +46 -5
- package/core/modules/steps/steps.scss +82 -13
- package/package.json +1 -1
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
2
|
import React, { useState, useEffect, useRef } from "react";
|
|
4
3
|
import { Select, Checkbox, Input, Spin } from "antd";
|
|
5
4
|
import { CoreScripts } from "../../../../../models";
|
|
5
|
+
import "./advance-search.scss";
|
|
6
6
|
|
|
7
7
|
const { Search } = Input;
|
|
8
8
|
|
|
9
|
-
export default function AdvancedSearchSelect({ field, value = [], onChange }) {
|
|
10
|
-
|
|
11
|
-
const
|
|
9
|
+
export default function AdvancedSearchSelect({ reportId, field, value = [], onChange ,onReset}) {
|
|
10
|
+
|
|
11
|
+
const isSearchQuery = !!field.search_query;
|
|
12
|
+
|
|
13
|
+
const [searchResults, setSearchResults] = useState([]);
|
|
14
|
+
const [selectedItems, setSelectedItems] = useState(value);
|
|
12
15
|
const [loading, setLoading] = useState(false);
|
|
13
16
|
const debounceRef = useRef(null);
|
|
14
17
|
|
|
15
|
-
// Sync selectedItems if parent value changes externally
|
|
16
18
|
useEffect(() => {
|
|
17
19
|
setSelectedItems(value);
|
|
18
20
|
}, [value]);
|
|
@@ -21,11 +23,14 @@ export default function AdvancedSearchSelect({ field, value = [], onChange }) {
|
|
|
21
23
|
try {
|
|
22
24
|
setLoading(true);
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const formBody = {
|
|
27
|
+
script_id: reportId,
|
|
28
|
+
search_field: field.field,
|
|
29
|
+
search_condition: searchText,
|
|
30
|
+
};
|
|
26
31
|
|
|
27
|
-
const res = await CoreScripts.
|
|
28
|
-
const data = Array.isArray(res) ? res
|
|
32
|
+
const res = await CoreScripts.getQuerySeacch(formBody);
|
|
33
|
+
const data = Array.isArray(res.data) ? res.data : [];
|
|
29
34
|
const apiValues = data?.map((r) => r[field.field]) || [];
|
|
30
35
|
|
|
31
36
|
setSearchResults(apiValues);
|
|
@@ -37,7 +42,7 @@ export default function AdvancedSearchSelect({ field, value = [], onChange }) {
|
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
useEffect(() => {
|
|
40
|
-
loadOptions("");
|
|
45
|
+
if (isSearchQuery) loadOptions("");
|
|
41
46
|
}, [field]);
|
|
42
47
|
|
|
43
48
|
const handleSearch = (text) => {
|
|
@@ -48,61 +53,77 @@ export default function AdvancedSearchSelect({ field, value = [], onChange }) {
|
|
|
48
53
|
const toggleValue = (checked, item) => {
|
|
49
54
|
let newValues;
|
|
50
55
|
|
|
51
|
-
if (checked)
|
|
52
|
-
|
|
53
|
-
} else {
|
|
54
|
-
newValues = selectedItems.filter((v) => v !== item);
|
|
55
|
-
}
|
|
56
|
+
if (checked) newValues = [...selectedItems, item];
|
|
57
|
+
else newValues = selectedItems.filter((v) => v !== item);
|
|
56
58
|
|
|
57
|
-
setSelectedItems(newValues);
|
|
59
|
+
setSelectedItems(newValues);
|
|
58
60
|
onChange(newValues);
|
|
59
61
|
};
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
const handleReset = () => {
|
|
64
|
+
setSelectedItems([]);
|
|
65
|
+
onChange([]);
|
|
66
|
+
// if (onReset) {
|
|
67
|
+
onReset(); // 🔥 trigger dashboard refresh
|
|
68
|
+
// }
|
|
69
|
+
};
|
|
70
|
+
|
|
62
71
|
const displayOptions = [
|
|
63
72
|
...selectedItems,
|
|
64
73
|
...searchResults.filter((item) => !selectedItems.includes(item)),
|
|
65
74
|
];
|
|
66
75
|
|
|
76
|
+
const isActive = value && value.length > 0;
|
|
77
|
+
|
|
78
|
+
// -------- INPUT MODE --------
|
|
79
|
+
if (!isSearchQuery) {
|
|
80
|
+
return (
|
|
81
|
+
<Input
|
|
82
|
+
className={`advanced-search-input ${isActive ? "advanced-search-active" : ""}`}
|
|
83
|
+
placeholder={`Search ${field.caption}`}
|
|
84
|
+
// The parent provides an array for `value`.
|
|
85
|
+
// We take the first element for the input's display value.
|
|
86
|
+
value={value[0] || ""}
|
|
87
|
+
onChange={(e) => {
|
|
88
|
+
const text = e.target.value;
|
|
89
|
+
// Always pass an array back to the parent to be consistent with the Select mode.
|
|
90
|
+
onChange(text ? [text] : []);
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// -------- SELECT MODE --------
|
|
67
97
|
return (
|
|
68
98
|
<Select
|
|
99
|
+
className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
|
|
69
100
|
mode="multiple"
|
|
70
101
|
value={value}
|
|
71
|
-
style={{ width: 250 }}
|
|
72
102
|
placeholder={field.caption}
|
|
73
103
|
allowClear
|
|
74
104
|
maxTagCount={0}
|
|
75
105
|
onChange={onChange}
|
|
76
106
|
maxTagPlaceholder={() => (
|
|
77
|
-
<span
|
|
107
|
+
<span className="tag-placeholder">
|
|
78
108
|
{field.caption}
|
|
79
|
-
<span
|
|
80
|
-
style={{
|
|
81
|
-
background: "#e6f0ff",
|
|
82
|
-
color: "#1677ff",
|
|
83
|
-
borderRadius: "10px",
|
|
84
|
-
padding: "0 8px",
|
|
85
|
-
fontWeight: 600,
|
|
86
|
-
fontSize: 12,
|
|
87
|
-
}}
|
|
88
|
-
>
|
|
109
|
+
<span className="tag-placeholder-count">
|
|
89
110
|
{value?.length || 0}
|
|
90
111
|
</span>
|
|
91
112
|
</span>
|
|
92
113
|
)}
|
|
93
114
|
dropdownRender={() => (
|
|
94
|
-
<div
|
|
115
|
+
<div className="dropdown-content">
|
|
95
116
|
<Search
|
|
96
117
|
placeholder={`Search ${field.caption}`}
|
|
97
118
|
onChange={(e) => handleSearch(e.target.value)}
|
|
98
119
|
/>
|
|
99
120
|
|
|
100
|
-
<div
|
|
121
|
+
<div className="dropdown-options-list">
|
|
101
122
|
{loading ? (
|
|
102
123
|
<Spin />
|
|
103
124
|
) : (
|
|
104
125
|
displayOptions.map((item) => (
|
|
105
|
-
<div key={item}
|
|
126
|
+
<div key={item} className="dropdown-option-item">
|
|
106
127
|
<Checkbox
|
|
107
128
|
checked={selectedItems.includes(item)}
|
|
108
129
|
onChange={(e) => toggleValue(e.target.checked, item)}
|
|
@@ -113,6 +134,12 @@ export default function AdvancedSearchSelect({ field, value = [], onChange }) {
|
|
|
113
134
|
))
|
|
114
135
|
)}
|
|
115
136
|
</div>
|
|
137
|
+
|
|
138
|
+
<div className="dropdown-footer">
|
|
139
|
+
<span className="dropdown-reset-button" onClick={handleReset}>
|
|
140
|
+
Reset
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
116
143
|
</div>
|
|
117
144
|
)}
|
|
118
145
|
/>
|
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
|
+
}
|
|
6
|
+
.advanced-search-input {
|
|
7
|
+
width: 160px;
|
|
8
|
+
}
|
|
9
|
+
/* Active state (when value selected) */
|
|
10
|
+
.advanced-search-select.advanced-search-active .ant-select-selector {
|
|
11
|
+
border-color: #1677ff !important;
|
|
12
|
+
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.2) !important;
|
|
13
|
+
border-radius: 6px !important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Input active state */
|
|
17
|
+
.advanced-search-input.advanced-search-active {
|
|
18
|
+
border-color: #1677ff !important;
|
|
19
|
+
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.2);
|
|
20
|
+
border-radius: 6px;
|
|
21
|
+
}
|
|
22
|
+
// .advanced-search-input {
|
|
23
|
+
// width: 250px;
|
|
24
|
+
|
|
25
|
+
// &.advanced-search-active {
|
|
26
|
+
// // Antd Select and Input don't share the same border-radius property,
|
|
27
|
+
// // so we apply a wrapper style that works for both.
|
|
28
|
+
// // For more specific control, you might need to target internal antd classes.
|
|
29
|
+
// border: 1px solid #91caff !important;
|
|
30
|
+
// box-shadow: 0 0 0 1px #91caff;
|
|
31
|
+
// border-radius: 6px;
|
|
32
|
+
// }
|
|
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
|
+
}
|
|
@@ -173,7 +173,7 @@ 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');
|
|
176
|
+
const searchFields = parameters.filter((p) => p.type === 'search' && p.search_enabled === 'yes');
|
|
177
177
|
setSearchParameters([...searchFields]);
|
|
178
178
|
|
|
179
179
|
parameters = await parameters?.map((record) => {
|
|
@@ -344,171 +344,98 @@ export default function ReportingDashboard({
|
|
|
344
344
|
}
|
|
345
345
|
};
|
|
346
346
|
|
|
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
|
-
}
|
|
347
|
+
const handleSubmit = (values) => {
|
|
348
|
+
const hasSearchValues = Object.values(searchValues).some((v) => Array.isArray(v) && v.length > 0);
|
|
391
349
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
.map((v) => `${column} ${operator} '${v}'`)
|
|
396
|
-
.join(" OR ");
|
|
397
|
-
searchCondition += ` AND (${conditions})`;
|
|
350
|
+
if (!hasSearchValues) {
|
|
351
|
+
runSubmit(values);
|
|
352
|
+
return;
|
|
398
353
|
}
|
|
399
|
-
});
|
|
400
354
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const
|
|
355
|
+
// Check if form values changed (date or others)
|
|
356
|
+
const formChanged = Object.keys(values).some((key) => {
|
|
357
|
+
const newVal = values[key];
|
|
358
|
+
const oldVal = formContents[key];
|
|
404
359
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
let finalValues = values;
|
|
410
|
-
|
|
411
|
-
if (hasSearchValues) {
|
|
412
|
-
|
|
413
|
-
const searchCondition = buildSearchCondition(
|
|
414
|
-
searchValues,
|
|
415
|
-
searchParameters
|
|
416
|
-
);
|
|
360
|
+
if (moment.isMoment(newVal) && moment.isMoment(oldVal)) {
|
|
361
|
+
return !newVal.isSame(oldVal, 'day');
|
|
362
|
+
}
|
|
417
363
|
|
|
418
|
-
|
|
419
|
-
Object.keys(values).forEach((key) => {
|
|
420
|
-
resetValues[key] = null;
|
|
364
|
+
return newVal !== oldVal;
|
|
421
365
|
});
|
|
422
366
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
367
|
+
if (formChanged) {
|
|
368
|
+
Modal.confirm({
|
|
369
|
+
title: 'Filters changed',
|
|
370
|
+
content: 'You changed some filters. Do you want to search using these filters also?',
|
|
371
|
+
okText: 'Yes',
|
|
372
|
+
cancelText: 'No',
|
|
373
|
+
|
|
374
|
+
onOk() {
|
|
375
|
+
// YES → send form values + search condition
|
|
376
|
+
const finalValues = {
|
|
377
|
+
...values,
|
|
378
|
+
search_values: searchValues,
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
runSubmit(finalValues);
|
|
382
|
+
},
|
|
428
383
|
|
|
429
|
-
|
|
430
|
-
|
|
384
|
+
onCancel() {
|
|
385
|
+
// NO → reset form values
|
|
386
|
+
const resetValues = {};
|
|
387
|
+
Object.keys(values).forEach((key) => {
|
|
388
|
+
resetValues[key] = null;
|
|
389
|
+
});
|
|
431
390
|
|
|
432
|
-
|
|
391
|
+
const finalValues = {
|
|
392
|
+
...resetValues,
|
|
393
|
+
search_values: searchValues,
|
|
394
|
+
};
|
|
433
395
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
396
|
+
runSubmit(finalValues);
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
} else {
|
|
400
|
+
// no form change
|
|
401
|
+
const resetValues = {};
|
|
402
|
+
Object.keys(values).forEach((key) => {
|
|
403
|
+
resetValues[key] = null;
|
|
404
|
+
});
|
|
437
405
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
});
|
|
406
|
+
const finalValues = {
|
|
407
|
+
...resetValues,
|
|
408
|
+
search_values: searchValues,
|
|
409
|
+
};
|
|
443
410
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
411
|
+
runSubmit(finalValues);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
447
414
|
|
|
448
|
-
|
|
449
|
-
};
|
|
450
|
-
|
|
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
|
-
// }
|
|
415
|
+
const runSubmit = (finalValues) => {
|
|
416
|
+
const { pageSize } = pagination;
|
|
417
|
+
const resetPage = 1;
|
|
488
418
|
|
|
489
|
-
|
|
490
|
-
// const resetPage = 1;
|
|
419
|
+
scriptId.current = null;
|
|
491
420
|
|
|
492
|
-
|
|
421
|
+
const hasSearchValues = finalValues.search_values && Object.values(finalValues.search_values).some((v) => Array.isArray(v) && v.length > 0);
|
|
493
422
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
// const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
423
|
+
if (!hasSearchValues) {
|
|
424
|
+
const currentUrlParams = Location.search();
|
|
425
|
+
const { script_id, selected_card, ...cleanParams } = currentUrlParams;
|
|
498
426
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
427
|
+
const newParams = new URLSearchParams({
|
|
428
|
+
...cleanParams,
|
|
429
|
+
current: resetPage,
|
|
430
|
+
pageSize,
|
|
431
|
+
});
|
|
504
432
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
433
|
+
const newUrl = `${window.location.pathname}?${newParams.toString()}`;
|
|
434
|
+
window.history.replaceState({}, '', newUrl);
|
|
435
|
+
}
|
|
508
436
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
// };
|
|
437
|
+
onFinish(finalValues, resetPage);
|
|
438
|
+
};
|
|
512
439
|
/**
|
|
513
440
|
*
|
|
514
441
|
* @param {*} values
|
|
@@ -675,6 +602,7 @@ const handleSubmit = (values) => {
|
|
|
675
602
|
key={param.field}
|
|
676
603
|
parameter={param}
|
|
677
604
|
field={param}
|
|
605
|
+
reportId={reportId}
|
|
678
606
|
value={searchValues[param.field] || []}
|
|
679
607
|
onChange={(value) => {
|
|
680
608
|
setSearchValues((prev) => ({
|
|
@@ -682,6 +610,32 @@ const handleSubmit = (values) => {
|
|
|
682
610
|
[param.field]: value,
|
|
683
611
|
}));
|
|
684
612
|
}}
|
|
613
|
+
onReset={() => {
|
|
614
|
+
setSearchValues((prev) => {
|
|
615
|
+
const updated = {
|
|
616
|
+
...prev,
|
|
617
|
+
[param.field]: [],
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// const finalValues = {
|
|
621
|
+
// ...formContents,
|
|
622
|
+
// search_values: updated,
|
|
623
|
+
// };
|
|
624
|
+
// console.log(finalValues);
|
|
625
|
+
// runSubmit(finalValues); // 🔥 refresh report correctly
|
|
626
|
+
getPatientDetails();
|
|
627
|
+
|
|
628
|
+
return updated;
|
|
629
|
+
});
|
|
630
|
+
}}
|
|
631
|
+
// onReset={() => {
|
|
632
|
+
// setSearchValues((prev) => ({
|
|
633
|
+
// ...prev,
|
|
634
|
+
// [param.field]: [],
|
|
635
|
+
// }));
|
|
636
|
+
|
|
637
|
+
// handleSubmit(formContents); // 🔥 refresh report
|
|
638
|
+
// }}
|
|
685
639
|
/>
|
|
686
640
|
))
|
|
687
641
|
: null}
|
|
@@ -13,6 +13,8 @@ export default function ActionButtons({
|
|
|
13
13
|
steps,
|
|
14
14
|
activeStep,
|
|
15
15
|
isStepCompleted,
|
|
16
|
+
isFullscreen,
|
|
17
|
+
onToggleFullscreen,
|
|
16
18
|
renderDynamicComponent,
|
|
17
19
|
handlePrevious,
|
|
18
20
|
handleNext,
|
|
@@ -37,6 +39,10 @@ export default function ActionButtons({
|
|
|
37
39
|
Back
|
|
38
40
|
</Button>
|
|
39
41
|
|
|
42
|
+
<Button type="default" onClick={onToggleFullscreen}>
|
|
43
|
+
{isFullscreen ? 'Exit Full Screen' : 'Switch to Full Screen'}
|
|
44
|
+
</Button>
|
|
45
|
+
|
|
40
46
|
{/* Skip button */}
|
|
41
47
|
{steps.length > 0 && steps[activeStep]?.allow_skip === 'Y' && (
|
|
42
48
|
<Button type="default" onClick={handleSkip} disabled={activeStep === steps.length - 1}>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
overflow-y: auto;
|
|
12
12
|
overflow-x: hidden;
|
|
13
13
|
overscroll-behavior: contain;
|
|
14
|
+
|
|
14
15
|
padding-bottom: 8px;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -27,7 +28,6 @@
|
|
|
27
28
|
border-top: 1px solid #f0f0f0;
|
|
28
29
|
box-shadow: 0 -2px 10px rgba(15, 23, 42, 0.04);
|
|
29
30
|
|
|
30
|
-
|
|
31
31
|
width: 61%;
|
|
32
32
|
padding: 10px;
|
|
33
33
|
position: fixed;
|
|
@@ -37,9 +37,15 @@
|
|
|
37
37
|
display: flex;
|
|
38
38
|
border-radius: 4px;
|
|
39
39
|
box-shadow: -1px -4px 10px 2px #f7f7f76e;
|
|
40
|
-
|
|
40
|
+
flex-wrap: wrap;
|
|
41
41
|
|
|
42
42
|
.ant-btn {
|
|
43
43
|
border-radius: 4px;
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.process-steps-page.is-fullscreen .action-buttons-container {
|
|
48
|
+
width: calc(100% - 24px);
|
|
49
|
+
left: 12px;
|
|
50
|
+
right: 12px;
|
|
51
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Handles process submission and optional chaining to the next process.
|
|
9
9
|
* - Provides a collapsible timeline view and action controls.
|
|
10
10
|
*/
|
|
11
|
-
import React, { useEffect, useState } from 'react';
|
|
11
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
12
12
|
import { Row, Col, Empty } from 'antd';
|
|
13
13
|
import { Card } from './../../lib';
|
|
14
14
|
import * as genericComponents from './../../lib';
|
|
@@ -36,8 +36,11 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
36
36
|
const [timelineCollapsed, setTimelineCollapsed] = useState(false);
|
|
37
37
|
const [showExternalWindow, setShowExternalWindow] = useState(false);
|
|
38
38
|
const [externalWin, setExternalWin] = useState(null);
|
|
39
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
40
|
+
const processStepsRef = useRef(null);
|
|
39
41
|
|
|
40
42
|
const urlParams = Location.search();
|
|
43
|
+
const isConsultationMode = String(urlParams?.consultation).toLowerCase() === 'true';
|
|
41
44
|
let processId = urlParams.processId;
|
|
42
45
|
const [currentProcessId, setCurrentProcessId] = useState(processId);
|
|
43
46
|
// Load process details based on the current process ID
|
|
@@ -271,13 +274,49 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
271
274
|
};
|
|
272
275
|
}, [activeStep, steps, externalWin]);
|
|
273
276
|
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
const syncFullscreenState = () => {
|
|
279
|
+
setIsFullscreen(document.fullscreenElement === processStepsRef.current);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
document.addEventListener('fullscreenchange', syncFullscreenState);
|
|
283
|
+
syncFullscreenState();
|
|
284
|
+
|
|
285
|
+
return () => {
|
|
286
|
+
document.removeEventListener('fullscreenchange', syncFullscreenState);
|
|
287
|
+
};
|
|
288
|
+
}, []);
|
|
289
|
+
|
|
290
|
+
const handleToggleFullscreen = async () => {
|
|
291
|
+
const element = processStepsRef.current;
|
|
292
|
+
if (!element) return;
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
if (document.fullscreenElement === element) {
|
|
296
|
+
if (document.exitFullscreen) {
|
|
297
|
+
await document.exitFullscreen();
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (element.requestFullscreen) {
|
|
303
|
+
await element.requestFullscreen();
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error('Unable to toggle full screen for steps page:', error);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
274
310
|
/**
|
|
275
311
|
* Renders the main process UI including timeline, step details,
|
|
276
312
|
* and action buttons. This content is reused in both normal view
|
|
277
313
|
* and external window view.
|
|
278
314
|
*/
|
|
279
|
-
const renderContent = () => (
|
|
280
|
-
<div
|
|
315
|
+
const renderContent = (attachRef = true) => (
|
|
316
|
+
<div
|
|
317
|
+
ref={attachRef ? processStepsRef : null}
|
|
318
|
+
className={`process-steps-page ${isConsultationMode ? 'consultation-mode' : ''} ${isFullscreen ? 'is-fullscreen' : ''}`}
|
|
319
|
+
>
|
|
281
320
|
<Card className="process-steps-card">
|
|
282
321
|
<Row gutter={20} className="process-steps-row" align="stretch">
|
|
283
322
|
<Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6} className="process-steps-timeline-col">
|
|
@@ -302,6 +341,8 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
302
341
|
steps={steps}
|
|
303
342
|
activeStep={activeStep}
|
|
304
343
|
isStepCompleted={isStepCompleted}
|
|
344
|
+
isFullscreen={isFullscreen}
|
|
345
|
+
onToggleFullscreen={handleToggleFullscreen}
|
|
305
346
|
renderDynamicComponent={DynamicComponent}
|
|
306
347
|
handlePrevious={handlePrevious}
|
|
307
348
|
handleNext={handleNext}
|
|
@@ -335,9 +376,9 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
335
376
|
width={props.ExternalWindowWidth || 1000}
|
|
336
377
|
height={props.ExternalWindowHeight || 1000}
|
|
337
378
|
>
|
|
338
|
-
{renderContent()}
|
|
379
|
+
{renderContent(false)}
|
|
339
380
|
</ExternalWindow>
|
|
340
|
-
{renderContent()}
|
|
381
|
+
{renderContent(true)}
|
|
341
382
|
</>
|
|
342
383
|
);
|
|
343
384
|
}
|