ui-soxo-bootstrap-core 2.6.1-dev.26 → 2.6.1-dev.27
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/lib/models/forms/components/form-creator/form-creator.js +525 -468
- package/core/lib/models/forms/components/form-creator/form-creator.scss +29 -26
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +28 -2
- package/core/lib/utils/generic/generic.utils.js +2 -1
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +70 -43
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +2 -2
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +134 -250
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +36 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import React from 'react';
|
|
11
11
|
// Import XLSX library to handle Excel file creation
|
|
12
12
|
import * as XLSX from 'xlsx';
|
|
13
|
+
// import * as XLSX from 'xlsx-js-style';
|
|
14
|
+
// import * as XLSX from 'xlsx-js-style';
|
|
13
15
|
// Import saveAs from file-saver to trigger file download in browser
|
|
14
16
|
import { saveAs } from 'file-saver';
|
|
15
17
|
import { Button } from 'antd';
|
|
@@ -30,7 +32,27 @@ export const ExportReactCSV = ({ csvData, headers, fileName,title }) => {
|
|
|
30
32
|
// Extract label names for the header row
|
|
31
33
|
const headerLabels = headers.map((h) => h.label);
|
|
32
34
|
// Map data rows according to the headers' key order
|
|
33
|
-
const rows = csvData.map((row) =>
|
|
35
|
+
const rows = csvData.map((row) =>
|
|
36
|
+
headers.map((h, hIndex) => {
|
|
37
|
+
let value = row[h.key];
|
|
38
|
+
// If it's a summary row, return a cell object with bold styling
|
|
39
|
+
if (row.isSummaryRow) {
|
|
40
|
+
// Remove the index value (typically the first column) for the summary row
|
|
41
|
+
if (hIndex === 0) value = "";
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
v: value,
|
|
45
|
+
s: {
|
|
46
|
+
font: { bold: true },
|
|
47
|
+
// "Thick" visual indicator via border
|
|
48
|
+
// Visual indicator via border
|
|
49
|
+
border: { bottom: { style: "thick", color: { rgb: "000000" } } }
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
})
|
|
55
|
+
);
|
|
34
56
|
// Create a worksheet from an array of arrays (header + rows)
|
|
35
57
|
const data = [[title || "Report"], [], headerLabels, ...rows];
|
|
36
58
|
// worksheet = XLSX.utils.aoa_to_sheet([headerLabels, ...rows]);
|
|
@@ -44,7 +66,11 @@ export const ExportReactCSV = ({ csvData, headers, fileName,title }) => {
|
|
|
44
66
|
// Append the worksheet to the workbook with the name 'Sheet1'
|
|
45
67
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
46
68
|
// Write workbook to a buffer as an array
|
|
47
|
-
const excelBuffer = XLSX.write(workbook, {
|
|
69
|
+
const excelBuffer = XLSX.write(workbook, {
|
|
70
|
+
bookType: 'xlsx',
|
|
71
|
+
type: 'array',
|
|
72
|
+
cellStyles: true, // Required to export styles
|
|
73
|
+
});
|
|
48
74
|
// Create a Blob object from the buffer with correct MIME type
|
|
49
75
|
const blob = new Blob([excelBuffer], {
|
|
50
76
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
@@ -31,7 +31,8 @@ export function getExportData(records, exportDataColumns) {
|
|
|
31
31
|
const response = records.map((row, columnIndex) => {
|
|
32
32
|
|
|
33
33
|
let entry = {
|
|
34
|
-
'Sl No': columnIndex + 1
|
|
34
|
+
'Sl No': row.isSummaryRow ? '' : columnIndex + 1,
|
|
35
|
+
isSummaryRow: row.isSummaryRow
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
filteredColumns.forEach((column, indexValue) => {
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js
CHANGED
|
@@ -6,34 +6,45 @@ import "./advance-search.scss";
|
|
|
6
6
|
|
|
7
7
|
const { Search } = Input;
|
|
8
8
|
|
|
9
|
-
export default function AdvancedSearchSelect({ reportId, field, value
|
|
10
|
-
|
|
11
|
-
|
|
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 : [];
|
|
12
22
|
|
|
13
23
|
const [searchResults, setSearchResults] = useState([]);
|
|
14
|
-
const [
|
|
24
|
+
const [displayOptions, setDisplayOptions] = useState([]);
|
|
15
25
|
const [loading, setLoading] = useState(false);
|
|
16
26
|
const debounceRef = useRef(null);
|
|
17
27
|
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
setSelectedItems(value);
|
|
20
|
-
}, [value]);
|
|
21
|
-
|
|
22
28
|
const loadOptions = async (searchText = "") => {
|
|
23
29
|
try {
|
|
24
30
|
setLoading(true);
|
|
25
31
|
|
|
26
32
|
const formBody = {
|
|
27
|
-
script_id:
|
|
28
|
-
search_field:
|
|
33
|
+
script_id: finalReportId,
|
|
34
|
+
search_field: fieldName,
|
|
29
35
|
search_condition: searchText,
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
const res = await CoreScripts.getQuerySeacch(formBody);
|
|
33
39
|
const data = Array.isArray(res.data) ? res.data : [];
|
|
34
|
-
const apiValues = data?.map((r) => r[
|
|
40
|
+
const apiValues = data?.map((r) => r[fieldName]) || [];
|
|
35
41
|
|
|
36
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
|
+
]);
|
|
37
48
|
} catch (error) {
|
|
38
49
|
console.error("Search API error", error);
|
|
39
50
|
} finally {
|
|
@@ -42,8 +53,10 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
|
|
|
42
53
|
};
|
|
43
54
|
|
|
44
55
|
useEffect(() => {
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
// Clear search results when the field or report context changes,
|
|
57
|
+
// but do not load automatically on mount.
|
|
58
|
+
setSearchResults([]);
|
|
59
|
+
}, [fieldName, finalReportId]);
|
|
47
60
|
|
|
48
61
|
const handleSearch = (text) => {
|
|
49
62
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
@@ -53,41 +66,39 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
|
|
|
53
66
|
const toggleValue = (checked, item) => {
|
|
54
67
|
let newValues;
|
|
55
68
|
|
|
56
|
-
if (checked) newValues = [...
|
|
57
|
-
else newValues =
|
|
69
|
+
if (checked) newValues = [...safeValue, item];
|
|
70
|
+
else newValues = safeValue.filter((v) => v !== item);
|
|
58
71
|
|
|
59
|
-
setSelectedItems(newValues);
|
|
60
72
|
onChange(newValues);
|
|
61
73
|
};
|
|
62
74
|
|
|
63
75
|
const handleReset = () => {
|
|
64
|
-
setSelectedItems([]);
|
|
65
76
|
onChange([]);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
if (finalOnReset) {
|
|
78
|
+
finalOnReset(); // 🔥 trigger dashboard refresh
|
|
79
|
+
}
|
|
69
80
|
};
|
|
70
81
|
|
|
71
|
-
const
|
|
72
|
-
...selectedItems,
|
|
73
|
-
...searchResults.filter((item) => !selectedItems.includes(item)),
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
const isActive = value && value.length > 0;
|
|
82
|
+
const isActive = safeValue.length > 0;
|
|
77
83
|
|
|
78
84
|
// -------- INPUT MODE --------
|
|
79
85
|
if (!isSearchQuery) {
|
|
80
86
|
return (
|
|
81
87
|
<Input
|
|
88
|
+
allowClear
|
|
82
89
|
className={`advanced-search-input ${isActive ? "advanced-search-active" : ""}`}
|
|
83
|
-
placeholder={`Search ${
|
|
90
|
+
placeholder={`Search ${caption}`}
|
|
91
|
+
style={style}
|
|
84
92
|
// The parent provides an array for `value`.
|
|
85
93
|
// We take the first element for the input's display value.
|
|
86
|
-
value={
|
|
94
|
+
value={safeValue[0] || ""}
|
|
87
95
|
onChange={(e) => {
|
|
88
|
-
const text = e
|
|
96
|
+
const text = e?.target?.value;
|
|
89
97
|
// Always pass an array back to the parent to be consistent with the Select mode.
|
|
90
98
|
onChange(text ? [text] : []);
|
|
99
|
+
if (!text && finalOnReset) {
|
|
100
|
+
finalOnReset();
|
|
101
|
+
}
|
|
91
102
|
}}
|
|
92
103
|
/>
|
|
93
104
|
);
|
|
@@ -97,24 +108,34 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
|
|
|
97
108
|
return (
|
|
98
109
|
<Select
|
|
99
110
|
className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
|
|
111
|
+
style={style}
|
|
100
112
|
mode="multiple"
|
|
101
|
-
value={
|
|
102
|
-
placeholder={
|
|
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
|
+
}}
|
|
103
127
|
allowClear
|
|
104
|
-
maxTagCount={
|
|
128
|
+
maxTagCount={1}
|
|
105
129
|
onChange={onChange}
|
|
106
|
-
maxTagPlaceholder={() => (
|
|
107
|
-
<span className="tag-placeholder">
|
|
108
|
-
{
|
|
109
|
-
<span className="tag-placeholder-count">
|
|
110
|
-
{value?.length || 0}
|
|
111
|
-
</span>
|
|
130
|
+
maxTagPlaceholder={(omittedValues) => (
|
|
131
|
+
<span className="tag-placeholder-count">
|
|
132
|
+
+{omittedValues.length}
|
|
112
133
|
</span>
|
|
113
134
|
)}
|
|
114
135
|
dropdownRender={() => (
|
|
115
136
|
<div className="dropdown-content">
|
|
116
137
|
<Search
|
|
117
|
-
placeholder={`Search ${
|
|
138
|
+
placeholder={`Search ${caption}`}
|
|
118
139
|
onChange={(e) => handleSearch(e.target.value)}
|
|
119
140
|
/>
|
|
120
141
|
|
|
@@ -122,10 +143,16 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
|
|
|
122
143
|
{loading ? (
|
|
123
144
|
<Spin />
|
|
124
145
|
) : (
|
|
125
|
-
displayOptions.map((item) => (
|
|
126
|
-
<div
|
|
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
|
+
>
|
|
127
153
|
<Checkbox
|
|
128
|
-
checked={
|
|
154
|
+
checked={safeValue.includes(item)}
|
|
155
|
+
onClick={(e) => e.stopPropagation()} // Prevent double trigger when clicking the checkbox box specifically
|
|
129
156
|
onChange={(e) => toggleValue(e.target.checked, item)}
|
|
130
157
|
>
|
|
131
158
|
{item}
|
|
@@ -144,4 +171,4 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
|
|
|
144
171
|
)}
|
|
145
172
|
/>
|
|
146
173
|
);
|
|
147
|
-
}
|
|
174
|
+
}
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/* Base select width */
|
|
3
3
|
.advanced-search-select {
|
|
4
4
|
width: 160px;
|
|
5
|
+
// width: 100%;
|
|
5
6
|
}
|
|
6
7
|
.advanced-search-input {
|
|
7
8
|
width: 160px;
|
|
@@ -30,7 +31,6 @@
|
|
|
30
31
|
// box-shadow: 0 0 0 1px #91caff;
|
|
31
32
|
// border-radius: 6px;
|
|
32
33
|
// }
|
|
33
|
-
// }
|
|
34
34
|
|
|
35
35
|
.tag-placeholder {
|
|
36
36
|
display: flex;
|
|
@@ -73,4 +73,4 @@
|
|
|
73
73
|
font-weight: 500;
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|