@nuskin/react-search-sdk 1.0.0-trying-again.3 → 1.0.0-trying-again.4
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/README.md +25 -2
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +147 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -7,7 +7,28 @@ A host renders one component, `CommerceSearch`, and gives it the market, who the
|
|
|
7
7
|
navigate, and its logo. The package works out the rest itself: the market's catalog lookup, the
|
|
8
8
|
minimum search length, which NUi app to use, the panel's text and the analytics events. The panel UI
|
|
9
9
|
comes from [`@nuskin/react-search-components`](../react-search-components) and the agent from
|
|
10
|
-
`@
|
|
10
|
+
`@nu-skin-enterprises/chat-widget`, the platform-owned chat widget published from
|
|
11
|
+
`sales-ai-platform` to AWS CodeArtifact (see [Installing](#installing)).
|
|
12
|
+
|
|
13
|
+
## Installing
|
|
14
|
+
|
|
15
|
+
`@nu-skin-enterprises/chat-widget` is not on npmjs. `.npmrc` routes only that scope to the
|
|
16
|
+
`npm-sdks` repository of the `ai-spr-sdk` CodeArtifact domain (account 105371274045, us-west-2) and
|
|
17
|
+
reads a token from `CODEARTIFACT_AUTH_TOKEN`. CI mints it in `.gitlab-ci.yml`; locally, with an AWS
|
|
18
|
+
identity that may read the domain (the `nse-dev` and `nse-nnp` developer permission sets can):
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
export CODEARTIFACT_AUTH_TOKEN="$(aws codeartifact get-authorization-token \
|
|
22
|
+
--domain ai-spr-sdk --domain-owner 105371274045 --region us-west-2 \
|
|
23
|
+
--query authorizationToken --output text)" # valid 12 hours
|
|
24
|
+
yarn install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The domain owner matters: the dev account has an unrelated domain of the same name.
|
|
28
|
+
|
|
29
|
+
The variable must exist for yarn to start at all, even with the package already installed: yarn
|
|
30
|
+
refuses an `.npmrc` that references an unset variable. An empty value is enough for commands that
|
|
31
|
+
do not fetch (`export CODEARTIFACT_AUTH_TOKEN=`).
|
|
11
32
|
|
|
12
33
|
## Requirements
|
|
13
34
|
|
|
@@ -52,7 +73,9 @@ import { CommerceSearch } from '@nuskin/react-search-sdk';
|
|
|
52
73
|
| `logo` | no | An element shown at the top of the panel. |
|
|
53
74
|
|
|
54
75
|
The NUi agent also needs the `enableNuiSearch` cookie, or `?enableNuiSearch=true` in the URL, while
|
|
55
|
-
that is the on/off switch.
|
|
76
|
+
that is the on/off switch. It is for signed-in visitors only, unless the configuration sets
|
|
77
|
+
`allowGuestAgent` (today only the test environment does, so the handoff can be tried where signing in
|
|
78
|
+
is not possible).
|
|
56
79
|
|
|
57
80
|
### Known gaps
|
|
58
81
|
|
package/dist/index.d.mts
CHANGED
|
@@ -22,7 +22,10 @@ type NavigationTarget = {
|
|
|
22
22
|
type CustomerAccountType = 'guest' | 'Brand Affiliate' | 'Retail Customer' | 'Preferred Customer/Member';
|
|
23
23
|
/** Who is searching. */
|
|
24
24
|
interface CommerceSearchCustomer {
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Whether the visitor is signed in. The NUi agent is for signed-in visitors only, unless the
|
|
27
|
+
* package's configuration allows guests.
|
|
28
|
+
*/
|
|
26
29
|
isAuthenticated: boolean;
|
|
27
30
|
/** Defaults to `guest`. Ignored for a visitor who is not signed in. */
|
|
28
31
|
accountType?: CustomerAccountType;
|
|
@@ -49,7 +52,7 @@ interface CommerceSearchProps {
|
|
|
49
52
|
/**
|
|
50
53
|
* The commerce website's single search entry point: typeahead and recent searches over the
|
|
51
54
|
* product catalog today, with the NUi agent taking over in the same panel when a signed-in
|
|
52
|
-
* shopper searches for something with no products. What it can answer is not limited to
|
|
55
|
+
* shopper (guests too, where the configuration allows it) searches for something with no products. What it can answer is not limited to
|
|
53
56
|
* products — it is the commerce site's search, not a product-only search box.
|
|
54
57
|
*
|
|
55
58
|
* The package works out the rest itself: the market's catalog lookup, the minimum search length,
|
package/dist/index.d.ts
CHANGED
|
@@ -22,7 +22,10 @@ type NavigationTarget = {
|
|
|
22
22
|
type CustomerAccountType = 'guest' | 'Brand Affiliate' | 'Retail Customer' | 'Preferred Customer/Member';
|
|
23
23
|
/** Who is searching. */
|
|
24
24
|
interface CommerceSearchCustomer {
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Whether the visitor is signed in. The NUi agent is for signed-in visitors only, unless the
|
|
27
|
+
* package's configuration allows guests.
|
|
28
|
+
*/
|
|
26
29
|
isAuthenticated: boolean;
|
|
27
30
|
/** Defaults to `guest`. Ignored for a visitor who is not signed in. */
|
|
28
31
|
accountType?: CustomerAccountType;
|
|
@@ -49,7 +52,7 @@ interface CommerceSearchProps {
|
|
|
49
52
|
/**
|
|
50
53
|
* The commerce website's single search entry point: typeahead and recent searches over the
|
|
51
54
|
* product catalog today, with the NUi agent taking over in the same panel when a signed-in
|
|
52
|
-
* shopper searches for something with no products. What it can answer is not limited to
|
|
55
|
+
* shopper (guests too, where the configuration allows it) searches for something with no products. What it can answer is not limited to
|
|
53
56
|
* products — it is the commerce site's search, not a product-only search box.
|
|
54
57
|
*
|
|
55
58
|
* The package works out the rest itself: the market's catalog lookup, the minimum search length,
|
package/dist/index.js
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var configurationSdk = require('@nuskin/configuration-sdk');
|
|
5
5
|
var reactSearchComponents = require('@nuskin/react-search-components');
|
|
6
|
+
var reactProductSdk = require('@nuskin/react-product-sdk');
|
|
7
|
+
var styled = require('@emotion/styled');
|
|
6
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
9
|
|
|
8
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
11
|
|
|
10
12
|
var configurationSdk__default = /*#__PURE__*/_interopDefault(configurationSdk);
|
|
13
|
+
var styled__default = /*#__PURE__*/_interopDefault(styled);
|
|
11
14
|
|
|
12
15
|
// src/components/CommerceSearch/CommerceSearch.tsx
|
|
13
16
|
var { getConfiguration, getEnvironmentCode } = configurationSdk__default.default;
|
|
@@ -34,7 +37,10 @@ var TEST_ENVIRONMENT_VALUES = {
|
|
|
34
37
|
agent: {
|
|
35
38
|
appKey: "pk_commerce_product_advisor_624aea4305b09240e8c8f41d0f6527b2",
|
|
36
39
|
apiBaseUrl: "https://nui.tst.nuskin.ai/"
|
|
37
|
-
}
|
|
40
|
+
},
|
|
41
|
+
// Open to visitors who are not signed in, so the handoff can be tried where signing in is not
|
|
42
|
+
// possible (for example local development). Sign-in stays required everywhere else.
|
|
43
|
+
allowGuestAgent: true
|
|
38
44
|
};
|
|
39
45
|
function nonEmptyString(value) {
|
|
40
46
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -51,7 +57,8 @@ function normalizeCommerceSearchConfig(raw, { language } = {}) {
|
|
|
51
57
|
apiBaseUrl,
|
|
52
58
|
...language ? { language } : {}
|
|
53
59
|
}
|
|
54
|
-
} : {}
|
|
60
|
+
} : {},
|
|
61
|
+
...raw?.allowGuestAgent === true ? { allowGuestAgent: true } : {}
|
|
55
62
|
};
|
|
56
63
|
}
|
|
57
64
|
async function fetchCommerceSearchConfig({
|
|
@@ -305,6 +312,16 @@ function resolveCustomerTypeHeaders(getCustomerType) {
|
|
|
305
312
|
const customerType = getCustomerType?.()?.trim();
|
|
306
313
|
return customerType ? { "X-Customer-Type": customerType } : void 0;
|
|
307
314
|
}
|
|
315
|
+
var NO_RESULTS_ERROR_CODE = "SFOSER002";
|
|
316
|
+
async function isNoResultsResponse(response) {
|
|
317
|
+
if (response.status !== 404) return false;
|
|
318
|
+
try {
|
|
319
|
+
const body = await response.json();
|
|
320
|
+
return body?.code === NO_RESULTS_ERROR_CODE;
|
|
321
|
+
} catch {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
308
325
|
async function fetchJsonFromCandidates(fetchImpl, requestUrls, requestHeaders = {}) {
|
|
309
326
|
let lastError;
|
|
310
327
|
for (const requestUrl of requestUrls) {
|
|
@@ -315,6 +332,7 @@ async function fetchJsonFromCandidates(fetchImpl, requestUrls, requestHeaders =
|
|
|
315
332
|
headers: { Accept: "application/json", ...requestHeaders }
|
|
316
333
|
});
|
|
317
334
|
if (!response.ok) {
|
|
335
|
+
if (await isNoResultsResponse(response)) return null;
|
|
318
336
|
lastError = new Error(
|
|
319
337
|
`Request failed with status ${response.status}`
|
|
320
338
|
);
|
|
@@ -481,6 +499,19 @@ function toNavigationTarget(path) {
|
|
|
481
499
|
return /^https?:\/\//i.test(path) ? { type: "external", url: path } : { type: "internal", path };
|
|
482
500
|
}
|
|
483
501
|
|
|
502
|
+
// src/utils/productEnvironment.ts
|
|
503
|
+
var PRODUCT_ENVIRONMENTS = ["dev", "test", "prod"];
|
|
504
|
+
function toProductEnvironment(environmentCode) {
|
|
505
|
+
return environmentCode && PRODUCT_ENVIRONMENTS.includes(environmentCode) ? environmentCode : void 0;
|
|
506
|
+
}
|
|
507
|
+
function getProductEnvironment() {
|
|
508
|
+
try {
|
|
509
|
+
return toProductEnvironment(getEnvironmentCode());
|
|
510
|
+
} catch {
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
484
515
|
// src/utils/catalogHelpers.ts
|
|
485
516
|
var CART_DISABLED_PRODUCT_STATUSES = /* @__PURE__ */ new Set([
|
|
486
517
|
"discontinued",
|
|
@@ -1010,9 +1041,10 @@ function resolveNuiSearchFlag(env = {}) {
|
|
|
1010
1041
|
}
|
|
1011
1042
|
function isNuiSearchEnabled({
|
|
1012
1043
|
isAuthenticated,
|
|
1013
|
-
flag
|
|
1044
|
+
flag,
|
|
1045
|
+
allowGuests = false
|
|
1014
1046
|
}) {
|
|
1015
|
-
return
|
|
1047
|
+
return flag === true && (isAuthenticated === true || allowGuests);
|
|
1016
1048
|
}
|
|
1017
1049
|
|
|
1018
1050
|
// src/utils/searchRouting.ts
|
|
@@ -1026,15 +1058,69 @@ function decideSearchRoute({
|
|
|
1026
1058
|
if (snapshot.suggestionFetchFailed) return "search";
|
|
1027
1059
|
return snapshot.suggestions.length > 0 ? "search" : "handoff";
|
|
1028
1060
|
}
|
|
1061
|
+
|
|
1062
|
+
// src/utils/cachedProductClient.ts
|
|
1063
|
+
function withProductCache(client) {
|
|
1064
|
+
const lookups = /* @__PURE__ */ new Map();
|
|
1065
|
+
return {
|
|
1066
|
+
getProductById(productId) {
|
|
1067
|
+
let lookup = lookups.get(productId);
|
|
1068
|
+
if (!lookup) {
|
|
1069
|
+
lookup = client.getProductById(productId);
|
|
1070
|
+
lookups.set(productId, lookup);
|
|
1071
|
+
}
|
|
1072
|
+
return lookup;
|
|
1073
|
+
},
|
|
1074
|
+
getProductsByIds: (productIds) => client.getProductsByIds(productIds)
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
var AgentProductSlot = styled__default.default.div`
|
|
1078
|
+
&& {
|
|
1079
|
+
flex: 0 0 240px;
|
|
1080
|
+
overflow: hidden;
|
|
1081
|
+
width: 240px;
|
|
1082
|
+
}
|
|
1083
|
+
`;
|
|
1084
|
+
var SalesAIChatWidget = react.lazy(
|
|
1085
|
+
() => Promise.all([
|
|
1086
|
+
import('@nu-skin-enterprises/chat-widget'),
|
|
1087
|
+
import('@nu-skin-enterprises/chat-widget/styles.css')
|
|
1088
|
+
]).then(([mod]) => ({ default: mod.SalesAIChatWidget }))
|
|
1089
|
+
);
|
|
1029
1090
|
var HANDLE_POLL_MS = 50;
|
|
1030
1091
|
var HANDLE_TIMEOUT_MS = 15e3;
|
|
1031
|
-
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
return {
|
|
1037
|
-
}
|
|
1092
|
+
function AgentProductCard({ sku }) {
|
|
1093
|
+
const { product, isLoading, error } = reactProductSdk.useProductById(sku);
|
|
1094
|
+
if (!isLoading && (error || !product?.seoInformation?.canonicalURL)) {
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AgentProductSlot, { children: /* @__PURE__ */ jsxRuntime.jsx(reactProductSdk.ProductCard, { productId: sku }) });
|
|
1098
|
+
}
|
|
1099
|
+
var renderAgentProduct = (product) => /* @__PURE__ */ jsxRuntime.jsx(AgentProductCard, { sku: product.sku });
|
|
1100
|
+
function AgentWidget({
|
|
1101
|
+
widgetProps,
|
|
1102
|
+
products
|
|
1103
|
+
}) {
|
|
1104
|
+
const environment = products?.environment;
|
|
1105
|
+
const market = products?.market;
|
|
1106
|
+
const language = products?.language;
|
|
1107
|
+
const client = react.useMemo(
|
|
1108
|
+
() => environment && market && language ? withProductCache(
|
|
1109
|
+
reactProductSdk.createProductClient({ environment, market, language })
|
|
1110
|
+
) : void 0,
|
|
1111
|
+
[environment, market, language]
|
|
1112
|
+
);
|
|
1113
|
+
if (!client) {
|
|
1114
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(SalesAIChatWidget, { ...widgetProps }) });
|
|
1115
|
+
}
|
|
1116
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactProductSdk.ProductProvider, { client, children: /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1117
|
+
SalesAIChatWidget,
|
|
1118
|
+
{
|
|
1119
|
+
renderProduct: renderAgentProduct,
|
|
1120
|
+
...widgetProps
|
|
1121
|
+
}
|
|
1122
|
+
) }) });
|
|
1123
|
+
}
|
|
1038
1124
|
function getSalesAIHandle() {
|
|
1039
1125
|
return window.SalesAI;
|
|
1040
1126
|
}
|
|
@@ -1090,6 +1176,7 @@ function NuiAgentPanel({
|
|
|
1090
1176
|
agent,
|
|
1091
1177
|
query,
|
|
1092
1178
|
handoffContext,
|
|
1179
|
+
products,
|
|
1093
1180
|
onUnavailable
|
|
1094
1181
|
}) {
|
|
1095
1182
|
useHandoff({
|
|
@@ -1098,19 +1185,22 @@ function NuiAgentPanel({
|
|
|
1098
1185
|
agentContext: agent.context,
|
|
1099
1186
|
onUnavailable
|
|
1100
1187
|
});
|
|
1101
|
-
|
|
1102
|
-
|
|
1188
|
+
const widgetProps = {
|
|
1189
|
+
appKey: agent.appKey,
|
|
1190
|
+
apiBaseUrl: agent.apiBaseUrl,
|
|
1191
|
+
theme: agent.theme ?? "nui-light",
|
|
1192
|
+
defaultOpen: true,
|
|
1193
|
+
hideFab: true,
|
|
1194
|
+
placement: "host",
|
|
1195
|
+
...agent.language === void 0 ? {} : { language: agent.language }
|
|
1196
|
+
};
|
|
1197
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AgentErrorBoundary, { onError: onUnavailable, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1198
|
+
AgentWidget,
|
|
1103
1199
|
{
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
theme: agent.theme ?? "nui-light",
|
|
1107
|
-
defaultOpen: true,
|
|
1108
|
-
hideFab: true,
|
|
1109
|
-
placement: "host",
|
|
1110
|
-
...agent.language === void 0 ? {} : { language: agent.language },
|
|
1111
|
-
...agent.renderProduct === void 0 ? {} : { renderProduct: agent.renderProduct }
|
|
1200
|
+
widgetProps,
|
|
1201
|
+
...products ? { products } : {}
|
|
1112
1202
|
}
|
|
1113
|
-
) })
|
|
1203
|
+
) });
|
|
1114
1204
|
}
|
|
1115
1205
|
var SUBMIT_WAIT_TIMEOUT_MS = 2e3;
|
|
1116
1206
|
function uniqueRecentSearches(queries) {
|
|
@@ -1129,6 +1219,8 @@ function CommerceSearchView({
|
|
|
1129
1219
|
navigate,
|
|
1130
1220
|
isAuthenticated,
|
|
1131
1221
|
agent,
|
|
1222
|
+
allowGuestAgent = false,
|
|
1223
|
+
agentProducts,
|
|
1132
1224
|
logo,
|
|
1133
1225
|
minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH,
|
|
1134
1226
|
gtmContext
|
|
@@ -1154,7 +1246,11 @@ function CommerceSearchView({
|
|
|
1154
1246
|
suggestions,
|
|
1155
1247
|
updateQuery
|
|
1156
1248
|
} = search;
|
|
1157
|
-
const enabled = agent !== void 0 && isNuiSearchEnabled({
|
|
1249
|
+
const enabled = agent !== void 0 && isNuiSearchEnabled({
|
|
1250
|
+
isAuthenticated,
|
|
1251
|
+
flag,
|
|
1252
|
+
allowGuests: allowGuestAgent
|
|
1253
|
+
});
|
|
1158
1254
|
const items = getVisibleSuggestions({
|
|
1159
1255
|
didYouMean,
|
|
1160
1256
|
suggestions
|
|
@@ -1324,6 +1420,22 @@ function CommerceSearchView({
|
|
|
1324
1420
|
if (agentQuery !== null)
|
|
1325
1421
|
navigateTo(service.submitProductSearch(agentQuery));
|
|
1326
1422
|
}, [agentQuery, navigateTo, service]);
|
|
1423
|
+
const agentProductsProps = agentProducts ? { products: agentProducts } : {};
|
|
1424
|
+
const agentPanel = handoff === null || agent === void 0 ? void 0 : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1425
|
+
NuiAgentPanel,
|
|
1426
|
+
{
|
|
1427
|
+
agent,
|
|
1428
|
+
query: handoff.query,
|
|
1429
|
+
handoffContext: {
|
|
1430
|
+
source: "storefront_search",
|
|
1431
|
+
trigger: "zero_results",
|
|
1432
|
+
query: handoff.query,
|
|
1433
|
+
did_you_mean: handoff.didYouMean
|
|
1434
|
+
},
|
|
1435
|
+
...agentProductsProps,
|
|
1436
|
+
onUnavailable: handleAgentUnavailable
|
|
1437
|
+
}
|
|
1438
|
+
);
|
|
1327
1439
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1328
1440
|
reactSearchComponents.SearchModal,
|
|
1329
1441
|
{
|
|
@@ -1351,20 +1463,7 @@ function CommerceSearchView({
|
|
|
1351
1463
|
onSelect: handleRecentSelect,
|
|
1352
1464
|
onClear: handleClearRecent
|
|
1353
1465
|
},
|
|
1354
|
-
agentPanel
|
|
1355
|
-
NuiAgentPanel,
|
|
1356
|
-
{
|
|
1357
|
-
agent,
|
|
1358
|
-
query: handoff.query,
|
|
1359
|
-
handoffContext: {
|
|
1360
|
-
source: "storefront_search",
|
|
1361
|
-
trigger: "zero_results",
|
|
1362
|
-
query: handoff.query,
|
|
1363
|
-
did_you_mean: handoff.didYouMean
|
|
1364
|
-
},
|
|
1365
|
-
onUnavailable: handleAgentUnavailable
|
|
1366
|
-
}
|
|
1367
|
-
)
|
|
1466
|
+
agentPanel
|
|
1368
1467
|
}
|
|
1369
1468
|
);
|
|
1370
1469
|
}
|
|
@@ -1382,7 +1481,7 @@ function CommerceSearch({
|
|
|
1382
1481
|
react.useEffect(() => {
|
|
1383
1482
|
if (isOpen) setHasOpened(true);
|
|
1384
1483
|
}, [isOpen]);
|
|
1385
|
-
const { minimumSearchLength, agent } = useCommerceSearchConfig({
|
|
1484
|
+
const { minimumSearchLength, agent, allowGuestAgent } = useCommerceSearchConfig({
|
|
1386
1485
|
country,
|
|
1387
1486
|
language,
|
|
1388
1487
|
enabled: hasOpened
|
|
@@ -1415,6 +1514,14 @@ function CommerceSearch({
|
|
|
1415
1514
|
}),
|
|
1416
1515
|
[country, language, isAuthenticated, accountType, userId]
|
|
1417
1516
|
);
|
|
1517
|
+
const agentProducts = react.useMemo(() => {
|
|
1518
|
+
const environment = getProductEnvironment();
|
|
1519
|
+
return environment ? {
|
|
1520
|
+
environment,
|
|
1521
|
+
market: country.toUpperCase(),
|
|
1522
|
+
language: language.toLowerCase()
|
|
1523
|
+
} : void 0;
|
|
1524
|
+
}, [country, language]);
|
|
1418
1525
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1419
1526
|
CommerceSearchView,
|
|
1420
1527
|
{
|
|
@@ -1424,6 +1531,8 @@ function CommerceSearch({
|
|
|
1424
1531
|
navigate: navigateToPath,
|
|
1425
1532
|
isAuthenticated,
|
|
1426
1533
|
...agent ? { agent } : {},
|
|
1534
|
+
...allowGuestAgent ? { allowGuestAgent } : {},
|
|
1535
|
+
...agentProducts ? { agentProducts } : {},
|
|
1427
1536
|
logo,
|
|
1428
1537
|
minimumSearchLength,
|
|
1429
1538
|
gtmContext
|