ui-soxo-bootstrap-core 2.6.24 → 2.6.26

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.
@@ -3,116 +3,72 @@
3
3
  * Global header component
4
4
  */
5
5
 
6
- import React, { useState, useEffect, useContext, useRef } from "react";
6
+ import { useContext, useEffect, useRef, useState } from 'react';
7
7
 
8
- import { animationControls, motion, useAnimation } from "framer-motion";
8
+ import { motion, useAnimation } from 'framer-motion';
9
9
 
10
- import {
11
- boxVariants,
12
- } from "./animations"
10
+ import { boxVariants } from './animations';
13
11
 
14
- import { GlobalContext, GlobalProvider } from "./../../Store";
12
+ import { GlobalContext, GlobalProvider } from './../../Store';
15
13
 
16
- import { Link, useLocation } from "react-router-dom";
14
+ import { Link, useLocation } from 'react-router-dom';
17
15
 
18
- import { Typography, Avatar, Input, Tooltip } from "antd";
16
+ import { Avatar, Input, Tooltip, Typography } from 'antd';
19
17
 
18
+ import ProgressBar from '../progress-bar/progress-bar'; // Adjust the path as needed
20
19
 
20
+ import { Button } from '../../elements';
21
21
 
22
- import ProgressBar from "../progress-bar/progress-bar"; // Adjust the path as needed
22
+ import GenericHeader from '../header/generic-header';
23
23
 
24
- import { Button } from "../../elements";
24
+ import * as AntIcons from '@ant-design/icons';
25
25
 
26
+ import { CustomerServiceOutlined, MenuOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
26
27
 
28
+ import { Drawer } from 'antd';
27
29
 
28
- import GenericHeader from "../header/generic-header";
30
+ import { ReloadOutlined, SearchOutlined } from '@ant-design/icons';
29
31
 
30
- import {
31
- MenuOutlined,
32
- UserOutlined,
33
- SettingOutlined,
34
- QuestionOutlined,
35
- } from "@ant-design/icons";
32
+ import SideMenu from './../sidemenu/sidemenu';
36
33
 
37
- import { Drawer } from "antd";
34
+ import './global-header.scss';
38
35
 
39
- import { ReloadOutlined, SearchOutlined } from "@ant-design/icons";
40
-
41
- import SideMenu from "./../sidemenu/sidemenu";
42
-
43
- import "./global-header.scss";
44
-
45
- import LanguageSwitcher from "../language-switcher/language-switcher";
46
-
47
- import { useTranslation } from "react-i18next";
48
-
49
-
50
- import BorderStyle from "pdf-lib/cjs/core/annotation/BorderStyle";
51
-
52
- import SettingsUtil from "../../../utils/settings.utils";
53
- import SpotlightSearch from "../spotlight-search/spotlight-search.component";
36
+ import LanguageSwitcher from '../language-switcher/language-switcher';
54
37
 
38
+ import { useTranslation } from 'react-i18next';
55
39
 
40
+ import SettingsUtil from '../../../utils/settings.utils';
41
+ import SpotlightSearch from '../spotlight-search/spotlight-search.component';
56
42
 
57
43
  const { Title } = Typography;
58
44
 
59
-
60
-
61
-
62
- function GlobalHeaderContent({
63
- loading,
64
- appSettings,
65
- children,
66
- isConnected,
67
- history,
68
- modules = [],
69
- sidemenu = [],
70
- reload,
71
- meta = {},
72
- ...props
73
- }) {
74
-
45
+ function GlobalHeaderContent({ loading, appSettings, children, isConnected, history, modules = [], sidemenu = [], reload, meta = {}, ...props }) {
75
46
  let location = useLocation();
76
47
  // let location = {};
77
48
 
78
-
79
-
80
49
  const { isMobile, user = { locations: [] }, kiosk, state, settings } = useContext(GlobalContext);
81
50
 
82
-
83
-
84
51
  const [visible, setVisible] = useState(false);
85
- const helpDeskSetting = settings?.HELPATR || {}
52
+ const helpDeskSetting = settings?.HELPATR || {};
86
53
 
87
54
  // Variable to handle toggling of menu
88
55
  const [collapsed, setCollapsed] = useState(false);
89
56
  // varibale handle branch switcher
90
57
 
91
-
92
-
93
58
  // const [searchModalVisible, setSearchModalVisible] = useState(false);
94
59
 
95
-
96
- const { globalCustomerHeader = () => { } } = appSettings;
60
+ const { globalCustomerHeader = () => {} } = appSettings;
97
61
 
98
62
  const { t, i18n } = useTranslation();
99
63
 
100
64
  const spotlightRef = useRef();
101
65
 
102
66
  useEffect(() => {
103
-
104
67
  setTimeout(() => {
105
-
106
68
  i18n.changeLanguage(localStorage.selectedLanguage);
107
-
108
- }, 0)
109
-
110
-
69
+ }, 0);
111
70
  }, []);
112
71
 
113
-
114
-
115
-
116
72
  /**
117
73
  * Function to handle toggling of menu
118
74
  */
@@ -128,9 +84,6 @@ function GlobalHeaderContent({
128
84
  SettingsUtil.openSpotlightModal();
129
85
  };
130
86
 
131
-
132
-
133
-
134
87
  /**
135
88
  * Function to remove toggling on mobile view
136
89
  */
@@ -158,58 +111,42 @@ function GlobalHeaderContent({
158
111
  const boxControls = useAnimation();
159
112
 
160
113
  async function animate() {
161
- await boxControls.start("entered");
114
+ await boxControls.start('entered');
162
115
 
163
116
  //await boxControls.start("show");
164
117
  }
165
118
 
166
119
  useEffect(() => {
167
-
168
120
  animate();
169
- }, [])
170
-
171
- useEffect(() => {
172
-
173
- }, [state.theme]);
174
-
175
-
176
-
177
-
178
-
121
+ }, []);
179
122
 
123
+ useEffect(() => {}, [state.theme]);
180
124
 
181
125
  return (
182
126
  <div
183
- className={`global-header ${process.env.REACT_APP_THEME} ${isConnected && !kiosk ? "connected" : ""
184
- }`}
127
+ className={`global-header ${process.env.REACT_APP_THEME} ${isConnected && !kiosk ? 'connected' : ''}`}
185
128
  style={{
186
-
187
129
  // background: state.theme.colors.bodyBackground,
188
130
 
189
131
  height: 10,
190
-
191
132
  }}
192
133
  >
193
134
  {/* <MenuOutlined style={{left:'1%',top:'1%', fontSize:18, position:'absolute', zIndex:999}} onClick={showSidebar}/> */}
194
135
 
195
136
  <div className="layout-content">
196
137
  <div
197
-
198
138
  //whileHover="hover"
199
139
  //whileTap="tap"
200
- className={`left-bar ${!isConnected && !isMobile ? "" : "hide"}${!collapsed ? "open" : "close"}${kiosk && !isConnected ? "kioskon" : ""}`}
201
- // style={{ background: state.theme.colors.leftSectionBackground }}
140
+ className={`left-bar ${!isConnected && !isMobile ? '' : 'hide'}${!collapsed ? 'open' : 'close'}${kiosk && !isConnected ? 'kioskon' : ''}`}
141
+ // style={{ background: state.theme.colors.leftSectionBackground }}
202
142
  >
203
-
204
143
  {!isMobile && !isConnected ? (
205
144
  <motion.div
206
145
  variants={boxVariants}
207
- initial={["entering"]}
146
+ initial={['entering']}
208
147
  animate={boxControls}
209
- className={`sidebar-container ${!collapsed ? "open" : "close"}${kiosk ? "" : "kioskon"}`}
210
-
148
+ className={`sidebar-container ${!collapsed ? 'open' : 'close'}${kiosk ? '' : 'kioskon'}`}
211
149
  >
212
-
213
150
  <SideMenu
214
151
  // isOpen={isOpen}
215
152
  collapsed={collapsed}
@@ -220,18 +157,10 @@ function GlobalHeaderContent({
220
157
  modules={modules}
221
158
  history={history}
222
159
  />
223
-
224
160
  </motion.div>
225
161
  ) : (
226
- <Drawer
227
- placement="left"
228
- onClose={onClose}
229
- visible={visible}
230
- className="side-drawer-content"
231
- >
232
- <div
233
- className={`sidebar-container ${!collapsed ? "open" : "close"}`}
234
- >
162
+ <Drawer placement="left" onClose={onClose} visible={visible} className="side-drawer-content">
163
+ <div className={`sidebar-container ${!collapsed ? 'open' : 'close'}`}>
235
164
  <SideMenu
236
165
  // isOpen={isOpen}
237
166
  collapsed={collapsed}
@@ -248,28 +177,26 @@ function GlobalHeaderContent({
248
177
  </div>
249
178
 
250
179
  {/* Right Section of the Component Loader */}
251
- <div className={`right-section ${!collapsed ? "open" : "close"} ${kiosk ? "kioskon" : ""}`} style={{ background: state.theme.colors.bodyBackground }}>
180
+ <div
181
+ className={`right-section ${!collapsed ? 'open' : 'close'} ${kiosk ? 'kioskon' : ''}`}
182
+ style={{ background: state.theme.colors.bodyBackground }}
183
+ >
252
184
  {/* For not connected section */}
253
185
  {!isConnected && !kiosk ? (
254
-
255
- <div className={`page-wrapper ${!collapsed ? "open" : "close"}`}
256
- // style={{ background: state.theme.colors.headerBg }}
186
+ <div
187
+ className={`page-wrapper ${!collapsed ? 'open' : 'close'}`}
188
+ // style={{ background: state.theme.colors.headerBg }}
257
189
  >
258
190
  {/* */}
259
191
  <div className="page-header-name">
260
-
261
192
  <ProgressBar isLoading={loading} />
262
193
 
263
- <span type
264
- onClick={!isMobile ? toggleCollapsed : hideToggle}
265
- className="toggle-box toggle-menu"
266
- >
194
+ <span type onClick={!isMobile ? toggleCollapsed : hideToggle} className="toggle-box toggle-menu">
267
195
  <MenuOutlined />
268
-
269
196
  </span>
270
197
 
271
198
  {/* Back Button */}
272
- {location.pathname !== "/" ? (
199
+ {location.pathname !== '/' ? (
273
200
  <span
274
201
  className="toggle-box"
275
202
  onClick={() => {
@@ -281,8 +208,7 @@ function GlobalHeaderContent({
281
208
  ) : null}
282
209
  {/* Back Button Ends */}
283
210
 
284
-
285
- {location.pathname !== "/" ? (
211
+ {location.pathname !== '/' ? (
286
212
  <h4 className="menu-caption header-caption" style={{ color: state.theme.colors.headerColor }}>
287
213
  {menu.caption}
288
214
  </h4>
@@ -295,32 +221,19 @@ function GlobalHeaderContent({
295
221
  <div className="page-menu">
296
222
  {/* Search Input in header start */}
297
223
  {!isMobile && (
298
-
299
224
  <div>
300
- <Input
301
- placeholder="Search (Shift + F)"
302
- prefix={<SearchOutlined />}
303
- onClick={openSearchModal}
304
-
305
- readOnly
306
- style={{ width: 250 }}
307
- />
308
-
225
+ <Input placeholder="Search (Shift + F)" prefix={<SearchOutlined />} onClick={openSearchModal} readOnly style={{ width: 250 }} />
309
226
 
310
227
  <SpotlightSearch ref={(elem) => SettingsUtil.registerModal(elem)} props={props} />
311
-
312
228
  </div>
313
229
  )}
314
230
  {/* Search Input in header start */}
315
231
 
316
232
  {/** branchswitcher Option */}
317
233
  {/* branch switcher controlled with env for matria and nura */}
318
- {!process.env.REACT_APP_SHOW_BRANCH_SWITCHER ? (
319
- <div className="branch-switcher">{globalCustomerHeader()}</div>
320
- ) : null}
234
+ {!process.env.REACT_APP_SHOW_BRANCH_SWITCHER ? <div className="branch-switcher">{globalCustomerHeader()}</div> : null}
321
235
  {/* <div className="branch-switcher">{globalCustomerHeader()}</div> */}
322
236
 
323
-
324
237
  {/* Search Option */}
325
238
 
326
239
  {/* <ModalSearch /> */}
@@ -333,10 +246,7 @@ function GlobalHeaderContent({
333
246
  {/* Models */}
334
247
  {menu && menu.id ? (
335
248
  <Link to={`/menus/${menu.id}`}>
336
-
337
- <Button type="default" size={"small"} icon={<SettingOutlined />}>
338
-
339
- </Button>
249
+ <Button type="default" size={'small'} icon={<SettingOutlined />}></Button>
340
250
  </Link>
341
251
  ) : null}
342
252
  {/* Models Ends */}
@@ -344,37 +254,37 @@ function GlobalHeaderContent({
344
254
  ) : null}
345
255
  {/* Configurator Actions Ends */}
346
256
 
347
-
348
-
349
-
350
-
351
-
352
257
  {/* Reload Button */}
353
258
 
354
- <Button onClick={reload} icon={<ReloadOutlined />} type="default" size={"small"} >
355
-
356
- </Button>
259
+ <Button onClick={reload} icon={<ReloadOutlined />} type="default" size={'small'}></Button>
357
260
 
358
261
  {/* Reload Button Ends */}
359
262
 
360
263
  {/* Help-desk-btn */}
361
- {helpDeskSetting?.showSupportBtn ? <Tooltip title={helpDeskSetting?.toolTipText}>
362
- <span>
363
- <Button
364
- onClick={() => window.open(helpDeskSetting?.helpDeskLink ?? '', '_blank')}
365
- icon={<QuestionOutlined />}
366
- type="default"
367
- size="small"
368
- />
369
- </span>
370
- </Tooltip> : null}
264
+ {helpDeskSetting?.showSupportBtn
265
+ ? (() => {
266
+ const HelpIcon = AntIcons[helpDeskSetting?.icon] ?? CustomerServiceOutlined;
267
+ return (
268
+ <Tooltip title={helpDeskSetting?.toolTipText} overlayClassName="modern-tooltip">
269
+ <span>
270
+ <Button
271
+ onClick={() => window.open(helpDeskSetting?.helpDeskLink ?? '', '_blank')}
272
+ icon={<HelpIcon />}
273
+ type="default"
274
+ size="small"
275
+ />
276
+ </span>
277
+ </Tooltip>
278
+ );
279
+ })()
280
+ : null}
371
281
 
372
282
  {/** Switch Languages starts */}
373
283
  {process.env.REACT_APP_ENABLE_LANGUAGE_SWITCHER ? <LanguageSwitcher /> : null}
374
284
  {/** Switch Languages ends */}
375
285
 
376
286
  {/* User Profile */}
377
- <div style={{ padding: "10px" }}>
287
+ <div style={{ padding: '10px' }}>
378
288
  <ProfileAvatar />
379
289
  <span style={{ color: state.theme.colors.colorText }}> {user.name} </span>
380
290
  </div>
@@ -396,7 +306,6 @@ function GlobalHeaderContent({
396
306
  );
397
307
  }
398
308
 
399
-
400
309
  export default function GlobalHeader(props) {
401
310
  const context = useContext(GlobalContext);
402
311
 
@@ -411,27 +320,20 @@ export default function GlobalHeader(props) {
411
320
  );
412
321
  }
413
322
 
414
-
415
-
416
323
  /**
417
- *
418
- * @returns
324
+ *
325
+ * @returns
419
326
  */
420
327
  function ProfileAvatar() {
421
-
422
328
  const { user = { locations: [] } } = useContext(GlobalContext);
423
329
 
424
- useEffect(() => { }, []);
330
+ useEffect(() => {}, []);
425
331
 
426
332
  return (
427
333
  <Link className="profile-avatar" to="/profile">
428
334
  {user.photograph ? (
429
335
  <>
430
- <img
431
- className="profile-picture"
432
- src={user.photograph}
433
- alt={"user photograph"}
434
- />
336
+ <img className="profile-picture" src={user.photograph} alt={'user photograph'} />
435
337
  </>
436
338
  ) : (
437
339
  <Avatar shape="square" size="small" icon={<UserOutlined />} />
@@ -155,12 +155,10 @@
155
155
  }
156
156
  }
157
157
 
158
- @media only screen and (max-width: 768px) {
158
+ @media only screen and (max-width: 768px) {
159
159
  margin-left: 0 !important; // remove left margin on mobile
160
160
  padding: 10px !important;
161
161
  }
162
-
163
-
164
162
  }
165
163
 
166
164
  .profile-avatar {
@@ -220,17 +218,16 @@
220
218
  left: 212px;
221
219
  }
222
220
 
223
- @media only screen and (max-width: 768px) {
224
- padding: 8px 10px 8px 14px !important;
225
- left: 0 !important;
226
- width: 100% !important;
227
-
228
- &.open {
229
- width: 100% !important;
221
+ @media only screen and (max-width: 768px) {
222
+ padding: 8px 10px 8px 14px !important;
230
223
  left: 0 !important;
231
- }
232
- }
224
+ width: 100% !important;
233
225
 
226
+ &.open {
227
+ width: 100% !important;
228
+ left: 0 !important;
229
+ }
230
+ }
234
231
 
235
232
  .page-header-name {
236
233
  display: flex;
@@ -289,7 +286,7 @@
289
286
  .branch-selection {
290
287
  margin: 0px 10px;
291
288
  }
292
- .branch-switcher {
289
+ .branch-switcher {
293
290
  .branches {
294
291
  .ant-select {
295
292
  min-width: 150px;
@@ -317,13 +314,44 @@
317
314
  }
318
315
  }
319
316
 
317
+ // Tooltip — must be top-level because antd Tooltip renders in a portal on document.body
318
+ .modern-tooltip {
319
+ .ant-tooltip-inner {
320
+ background: #ffffff;
321
+ color: #1f1f1f;
322
+ font-size: 13px;
323
+ padding: 8px 14px;
324
+ border-radius: 10px;
325
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
326
+ line-height: 1.4;
327
+ letter-spacing: 0.2px;
328
+ min-height: auto;
329
+ }
330
+
331
+ .ant-tooltip-arrow {
332
+ --antd-arrow-background-color: #ffffff;
333
+
334
+ &::before {
335
+ background: #ffffff !important;
336
+ }
337
+ }
338
+
339
+ .ant-tooltip-arrow-content {
340
+ background: #ffffff !important;
341
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
342
+
343
+ &::before {
344
+ background: #ffffff !important;
345
+ }
346
+ }
347
+ }
348
+
320
349
  .side-drawer-content-wrapper {
321
350
  .drawer-content {
322
351
  .ant-drawer-body {
323
352
  padding: 0px;
324
353
  }
325
354
  }
326
-
327
355
  }
328
356
 
329
357
  .sidebar-container {
@@ -361,7 +389,7 @@
361
389
  .ant-drawer-body {
362
390
  padding: 0px;
363
391
  }
364
- .ant-drawer-header{
392
+ .ant-drawer-header {
365
393
  padding: 2px 20px;
366
394
  }
367
395
  }
@@ -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.26",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"