ui-soxo-bootstrap-core 2.6.23 → 2.6.25
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.
|
@@ -600,7 +600,7 @@ function UserInput({ field, onUpload, selectedInformation, onChange, onFieldUpda
|
|
|
600
600
|
|
|
601
601
|
> */}
|
|
602
602
|
{isVisible === true ? (
|
|
603
|
-
<div className=
|
|
603
|
+
<div className={`form-item-element ${field.type === 'search' ? 'form-item-element-search' : ''}`}>
|
|
604
604
|
|
|
605
605
|
{/* <Button
|
|
606
606
|
size="small"
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js
CHANGED
|
@@ -5,6 +5,7 @@ import { CoreScripts } from "../../../../../models";
|
|
|
5
5
|
import "./advance-search.scss";
|
|
6
6
|
|
|
7
7
|
const { Search } = Input;
|
|
8
|
+
const advancedSearchCache = {};
|
|
8
9
|
|
|
9
10
|
export default function AdvancedSearchSelect({ reportId, onReset, field, value, onChange, style, ...rest }) {
|
|
10
11
|
// Normalize the configuration.
|
|
@@ -17,13 +18,31 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
17
18
|
const isSearchQuery = !!config.search_query;
|
|
18
19
|
const fieldName = isObjectMode ? field.field : field;
|
|
19
20
|
const caption = config.caption;
|
|
21
|
+
const cacheKey = `${finalReportId || "no-report"}::${fieldName || "no-field"}`;
|
|
20
22
|
|
|
21
23
|
const safeValue = Array.isArray(value) ? value : [];
|
|
24
|
+
const cachedState = advancedSearchCache[cacheKey] || { optionLabels: {}, displayOptions: [] };
|
|
22
25
|
|
|
23
26
|
const [searchResults, setSearchResults] = useState([]);
|
|
24
|
-
const [displayOptions, setDisplayOptions] = useState(
|
|
27
|
+
const [displayOptions, setDisplayOptions] = useState(cachedState.displayOptions);
|
|
28
|
+
const [optionLabels, setOptionLabels] = useState(cachedState.optionLabels);
|
|
25
29
|
const [loading, setLoading] = useState(false);
|
|
26
30
|
const debounceRef = useRef(null);
|
|
31
|
+
const missingLabelsFetchRef = useRef("");
|
|
32
|
+
|
|
33
|
+
const mergeDisplayOptions = (apiOptions = [], selectedValues = safeValue, labels = optionLabels) => {
|
|
34
|
+
const selectedOptions = selectedValues
|
|
35
|
+
.filter((item) => labels[item])
|
|
36
|
+
.map((item) => ({
|
|
37
|
+
search_value: item,
|
|
38
|
+
display_value: labels[item],
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
return [...selectedOptions, ...apiOptions].filter(
|
|
42
|
+
(item, index, array) =>
|
|
43
|
+
array.findIndex((entry) => entry.search_value === item.search_value) === index
|
|
44
|
+
);
|
|
45
|
+
};
|
|
27
46
|
|
|
28
47
|
const loadOptions = async (searchText = "") => {
|
|
29
48
|
try {
|
|
@@ -37,14 +56,21 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
37
56
|
|
|
38
57
|
const res = await CoreScripts.getQuerySeacch(formBody);
|
|
39
58
|
const data = Array.isArray(res.data) ? res.data : [];
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
59
|
+
const nextLabels = {
|
|
60
|
+
...optionLabels,
|
|
61
|
+
...data.reduce((acc, item) => {
|
|
62
|
+
acc[item.search_value] = item.display_value;
|
|
63
|
+
return acc;
|
|
64
|
+
}, {}),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
setSearchResults(data.map((item) => item.search_value));
|
|
68
|
+
setOptionLabels(nextLabels);
|
|
69
|
+
setDisplayOptions(mergeDisplayOptions(data, safeValue, nextLabels));
|
|
70
|
+
advancedSearchCache[cacheKey] = {
|
|
71
|
+
optionLabels: nextLabels,
|
|
72
|
+
displayOptions: mergeDisplayOptions(data, safeValue, nextLabels),
|
|
73
|
+
};
|
|
48
74
|
} catch (error) {
|
|
49
75
|
console.error("Search API error", error);
|
|
50
76
|
} finally {
|
|
@@ -53,16 +79,58 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
53
79
|
};
|
|
54
80
|
|
|
55
81
|
useEffect(() => {
|
|
56
|
-
//
|
|
57
|
-
// but do not load automatically on mount.
|
|
82
|
+
// Restore cached labels/options for the current field after remounts like submit refreshes.
|
|
58
83
|
setSearchResults([]);
|
|
59
|
-
|
|
84
|
+
setOptionLabels(cachedState.optionLabels || {});
|
|
85
|
+
setDisplayOptions(cachedState.displayOptions || []);
|
|
86
|
+
missingLabelsFetchRef.current = "";
|
|
87
|
+
}, [cacheKey]);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
// FormCreator can remount this field after submit with only search_value(s).
|
|
91
|
+
// If any selected value is missing a label, fetch the option list once to recover display_value.
|
|
92
|
+
const missingValues = safeValue.filter((item) => !optionLabels[item]);
|
|
93
|
+
const missingKey = missingValues.join("||");
|
|
94
|
+
|
|
95
|
+
if (!isSearchQuery || safeValue.length === 0 || missingValues.length === 0) {
|
|
96
|
+
missingLabelsFetchRef.current = "";
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (missingLabelsFetchRef.current === missingKey) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
missingLabelsFetchRef.current = missingKey;
|
|
105
|
+
if (isSearchQuery && safeValue.length > 0) {
|
|
106
|
+
loadOptions("");
|
|
107
|
+
}
|
|
108
|
+
}, [isSearchQuery, safeValue, optionLabels]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!isSearchQuery || safeValue.length === 0) return;
|
|
112
|
+
|
|
113
|
+
setDisplayOptions((prev) => {
|
|
114
|
+
const nextDisplayOptions = mergeDisplayOptions(prev, safeValue, optionLabels);
|
|
115
|
+
advancedSearchCache[cacheKey] = {
|
|
116
|
+
optionLabels,
|
|
117
|
+
displayOptions: nextDisplayOptions,
|
|
118
|
+
};
|
|
119
|
+
return nextDisplayOptions;
|
|
120
|
+
});
|
|
121
|
+
}, [cacheKey, isSearchQuery, safeValue, optionLabels]);
|
|
60
122
|
|
|
61
123
|
const handleSearch = (text) => {
|
|
62
124
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
63
125
|
debounceRef.current = setTimeout(() => loadOptions(text), 400);
|
|
64
126
|
};
|
|
65
127
|
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
return () => {
|
|
130
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
131
|
+
};
|
|
132
|
+
}, []);
|
|
133
|
+
|
|
66
134
|
const toggleValue = (checked, item) => {
|
|
67
135
|
let newValues;
|
|
68
136
|
|
|
@@ -80,6 +148,18 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
80
148
|
};
|
|
81
149
|
|
|
82
150
|
const isActive = safeValue.length > 0;
|
|
151
|
+
const selectOptions = [
|
|
152
|
+
...safeValue
|
|
153
|
+
.filter((item) => optionLabels[item])
|
|
154
|
+
.map((item) => ({
|
|
155
|
+
value: item,
|
|
156
|
+
label: optionLabels[item],
|
|
157
|
+
})),
|
|
158
|
+
...displayOptions.map((item) => ({
|
|
159
|
+
value: item.search_value,
|
|
160
|
+
label: item.display_value,
|
|
161
|
+
})),
|
|
162
|
+
].filter((item, index, array) => array.findIndex((entry) => entry.value === item.value) === index);
|
|
83
163
|
|
|
84
164
|
// -------- INPUT MODE --------
|
|
85
165
|
if (!isSearchQuery) {
|
|
@@ -110,18 +190,17 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
110
190
|
className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
|
|
111
191
|
style={style}
|
|
112
192
|
mode="multiple"
|
|
193
|
+
showSearch={false}
|
|
113
194
|
value={safeValue}
|
|
195
|
+
options={selectOptions}
|
|
114
196
|
placeholder={caption}
|
|
115
197
|
onDropdownVisibleChange={(open) => {
|
|
116
198
|
if (open) {
|
|
117
199
|
loadOptions("");
|
|
118
200
|
} else {
|
|
119
201
|
// When closing, re-sort selected items to the top so they are visible next time it opens
|
|
120
|
-
const currentResults =
|
|
121
|
-
setDisplayOptions(
|
|
122
|
-
...safeValue,
|
|
123
|
-
...currentResults.filter((item) => !safeValue.includes(item)),
|
|
124
|
-
]);
|
|
202
|
+
const currentResults = displayOptions.length > 0 ? displayOptions : [];
|
|
203
|
+
setDisplayOptions(mergeDisplayOptions(currentResults, safeValue, optionLabels));
|
|
125
204
|
}
|
|
126
205
|
}}
|
|
127
206
|
allowClear
|
|
@@ -137,6 +216,10 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
137
216
|
<Search
|
|
138
217
|
placeholder={`Search ${caption}`}
|
|
139
218
|
onChange={(e) => handleSearch(e.target.value)}
|
|
219
|
+
onKeyDown={(e) => {
|
|
220
|
+
// Prevent the parent Select from treating backspace as "remove last selected tag".
|
|
221
|
+
e.stopPropagation();
|
|
222
|
+
}}
|
|
140
223
|
/>
|
|
141
224
|
|
|
142
225
|
<div className="dropdown-options-list">
|
|
@@ -145,17 +228,17 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
|
|
|
145
228
|
) : (
|
|
146
229
|
displayOptions.map((item, idx) => (
|
|
147
230
|
<div
|
|
148
|
-
key={`${item}-${idx}`}
|
|
231
|
+
key={`${item.search_value}-${idx}`}
|
|
149
232
|
className="dropdown-option-item"
|
|
150
|
-
onClick={() => toggleValue(!safeValue.includes(item), item)}
|
|
233
|
+
onClick={() => toggleValue(!safeValue.includes(item.search_value), item.search_value)}
|
|
151
234
|
style={{ cursor: 'pointer' }}
|
|
152
235
|
>
|
|
153
236
|
<Checkbox
|
|
154
|
-
checked={safeValue.includes(item)}
|
|
237
|
+
checked={safeValue.includes(item.search_value)}
|
|
155
238
|
onClick={(e) => e.stopPropagation()} // Prevent double trigger when clicking the checkbox box specifically
|
|
156
|
-
onChange={(e) => toggleValue(e.target.checked, item)}
|
|
239
|
+
onChange={(e) => toggleValue(e.target.checked, item.search_value)}
|
|
157
240
|
>
|
|
158
|
-
{item}
|
|
241
|
+
{item.display_value}
|
|
159
242
|
</Checkbox>
|
|
160
243
|
</div>
|
|
161
244
|
))
|