ui-soxo-bootstrap-core 2.6.24 → 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="form-item-element">
603
+ <div className={`form-item-element ${field.type === 'search' ? 'form-item-element-search' : ''}`}>
604
604
 
605
605
  {/* <Button
606
606
  size="small"
@@ -28,6 +28,11 @@
28
28
  }
29
29
  }
30
30
 
31
+ .form-item-element-search {
32
+ min-width: 170px;
33
+ flex: 0 1 200px;
34
+ }
35
+
31
36
  .submit-button {
32
37
  margin-top: 34px;
33
38
  }
@@ -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,14 +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([]);
25
- const [optionLabels, setOptionLabels] = useState({});
27
+ const [displayOptions, setDisplayOptions] = useState(cachedState.displayOptions);
28
+ const [optionLabels, setOptionLabels] = useState(cachedState.optionLabels);
26
29
  const [loading, setLoading] = useState(false);
27
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
+ };
28
46
 
29
47
  const loadOptions = async (searchText = "") => {
30
48
  try {
@@ -38,24 +56,21 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
38
56
 
39
57
  const res = await CoreScripts.getQuerySeacch(formBody);
40
58
  const data = Array.isArray(res.data) ? res.data : [];
41
- const apiValues = data.map((item) => item.search_value);
42
-
43
- setSearchResults(apiValues);
44
- setDisplayOptions(data);
45
- setOptionLabels((prev) => ({
46
- ...prev,
59
+ const nextLabels = {
60
+ ...optionLabels,
47
61
  ...data.reduce((acc, item) => {
48
62
  acc[item.search_value] = item.display_value;
49
63
  return acc;
50
64
  }, {}),
51
- }));
65
+ };
52
66
 
53
- // Refresh display options only when new search results arrive to keep the list stable during interaction
54
- // setDisplayOptions([
55
- // ...safeValue,
56
- // ...apiValues
57
- // // ...apiValues.filter((item) => !safeValue.includes(item)),
58
- // ]);
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
+ };
59
74
  } catch (error) {
60
75
  console.error("Search API error", error);
61
76
  } finally {
@@ -64,26 +79,58 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
64
79
  };
65
80
 
66
81
  useEffect(() => {
67
- // Clear search results when the field or report context changes,
68
- // but do not load automatically on mount.
82
+ // Restore cached labels/options for the current field after remounts like submit refreshes.
69
83
  setSearchResults([]);
70
- }, [fieldName, finalReportId]);
84
+ setOptionLabels(cachedState.optionLabels || {});
85
+ setDisplayOptions(cachedState.displayOptions || []);
86
+ missingLabelsFetchRef.current = "";
87
+ }, [cacheKey]);
71
88
 
72
89
  useEffect(() => {
73
90
  // FormCreator can remount this field after submit with only search_value(s).
74
91
  // If any selected value is missing a label, fetch the option list once to recover display_value.
75
- const hasMissingLabels = safeValue.some((item) => !optionLabels[item]);
92
+ const missingValues = safeValue.filter((item) => !optionLabels[item]);
93
+ const missingKey = missingValues.join("||");
76
94
 
77
- if (isSearchQuery && safeValue.length > 0 && hasMissingLabels) {
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) {
78
106
  loadOptions("");
79
107
  }
80
108
  }, [isSearchQuery, safeValue, optionLabels]);
81
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]);
122
+
82
123
  const handleSearch = (text) => {
83
124
  if (debounceRef.current) clearTimeout(debounceRef.current);
84
125
  debounceRef.current = setTimeout(() => loadOptions(text), 400);
85
126
  };
86
127
 
128
+ useEffect(() => {
129
+ return () => {
130
+ if (debounceRef.current) clearTimeout(debounceRef.current);
131
+ };
132
+ }, []);
133
+
87
134
  const toggleValue = (checked, item) => {
88
135
  let newValues;
89
136
 
@@ -143,6 +190,7 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
143
190
  className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
144
191
  style={style}
145
192
  mode="multiple"
193
+ showSearch={false}
146
194
  value={safeValue}
147
195
  options={selectOptions}
148
196
  placeholder={caption}
@@ -152,10 +200,7 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
152
200
  } else {
153
201
  // When closing, re-sort selected items to the top so they are visible next time it opens
154
202
  const currentResults = displayOptions.length > 0 ? displayOptions : [];
155
- setDisplayOptions([
156
- ...currentResults.filter((item) => safeValue.includes(item.search_value)),
157
- ...currentResults.filter((item) => !safeValue.includes(item.search_value)),
158
- ]);
203
+ setDisplayOptions(mergeDisplayOptions(currentResults, safeValue, optionLabels));
159
204
  }
160
205
  }}
161
206
  allowClear
@@ -171,6 +216,10 @@ export default function AdvancedSearchSelect({ reportId, onReset, field, value,
171
216
  <Search
172
217
  placeholder={`Search ${caption}`}
173
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
+ }}
174
223
  />
175
224
 
176
225
  <div className="dropdown-options-list">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.6.24",
3
+ "version": "2.6.25",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"