@nuskin/react-search-sdk 1.0.0-comx-2233.4 → 1.0.0-trying-again.2
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 +59 -8
- package/dist/index.d.mts +45 -632
- package/dist/index.d.ts +45 -632
- package/dist/index.js +605 -1149
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +589 -1094
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -3
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { lazy, useState, useEffect, useRef, useMemo, useCallback, Suspense, Component } from 'react';
|
|
2
|
+
import configurationSdk from '@nuskin/configuration-sdk';
|
|
3
|
+
import { SearchModal } from '@nuskin/react-search-components';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/components/CommerceSearch/CommerceSearch.tsx
|
|
7
|
+
var { getConfiguration, getEnvironmentCode } = configurationSdk;
|
|
2
8
|
|
|
3
9
|
// src/constants.ts
|
|
4
10
|
var STOREFRONT_SHOPPING_CONTEXT = "storefront";
|
|
@@ -9,16 +15,77 @@ var DEFAULT_SUGGESTION_PAGE = 1;
|
|
|
9
15
|
var DEFAULT_SUGGESTION_SIZE = 6;
|
|
10
16
|
var DEFAULT_SEARCH_PATH = "/search";
|
|
11
17
|
var DEBOUNCE_MS = 220;
|
|
18
|
+
var MINIMUM_SEARCH_CHAR_LENGTH = 2;
|
|
12
19
|
var MAX_VISIBLE_SUGGESTIONS = 6;
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
var
|
|
20
|
+
var EQUINOX_MARKETS_MAP = "Equinox_Markets";
|
|
21
|
+
var DEFAULT_EQUINOX_CLIENT_ID = "63056848d7532f9923f3d08cb48c423b";
|
|
22
|
+
|
|
23
|
+
// src/config/commerceSearchConfig.ts
|
|
24
|
+
var COMMERCE_SEARCH_CONFIG_DEFAULTS = Object.freeze({
|
|
25
|
+
minimumSearchLength: MINIMUM_SEARCH_CHAR_LENGTH
|
|
26
|
+
});
|
|
27
|
+
var TEST_ENVIRONMENT_VALUES = {
|
|
28
|
+
agent: {
|
|
29
|
+
appKey: "pk_commerce_product_advisor_624aea4305b09240e8c8f41d0f6527b2",
|
|
30
|
+
apiBaseUrl: "https://nui.tst.nuskin.ai/"
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function nonEmptyString(value) {
|
|
34
|
+
return typeof value === "string" ? value.trim() : "";
|
|
35
|
+
}
|
|
36
|
+
function normalizeCommerceSearchConfig(raw, { language } = {}) {
|
|
37
|
+
const minimumSearchLength = Number(raw?.minimumSearchLength);
|
|
38
|
+
const appKey = nonEmptyString(raw?.agent?.appKey);
|
|
39
|
+
const apiBaseUrl = nonEmptyString(raw?.agent?.apiBaseUrl);
|
|
40
|
+
return {
|
|
41
|
+
minimumSearchLength: Number.isInteger(minimumSearchLength) && minimumSearchLength >= 1 ? minimumSearchLength : COMMERCE_SEARCH_CONFIG_DEFAULTS.minimumSearchLength,
|
|
42
|
+
...appKey && apiBaseUrl ? {
|
|
43
|
+
agent: {
|
|
44
|
+
appKey,
|
|
45
|
+
apiBaseUrl,
|
|
46
|
+
...language ? { language } : {}
|
|
47
|
+
}
|
|
48
|
+
} : {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function fetchCommerceSearchConfig({
|
|
52
|
+
language
|
|
53
|
+
} = {}) {
|
|
54
|
+
const raw = getEnvironmentCode() === "test" ? TEST_ENVIRONMENT_VALUES : {};
|
|
55
|
+
return normalizeCommerceSearchConfig(raw, language ? { language } : {});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/hooks/useCommerceSearchConfig.ts
|
|
59
|
+
function useCommerceSearchConfig({
|
|
60
|
+
country,
|
|
61
|
+
language,
|
|
62
|
+
enabled
|
|
63
|
+
}) {
|
|
64
|
+
const [config, setConfig] = useState(
|
|
65
|
+
COMMERCE_SEARCH_CONFIG_DEFAULTS
|
|
66
|
+
);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!enabled) return void 0;
|
|
69
|
+
let isStale = false;
|
|
70
|
+
fetchCommerceSearchConfig({
|
|
71
|
+
...country ? { country } : {},
|
|
72
|
+
...language ? { language } : {}
|
|
73
|
+
}).then((next) => {
|
|
74
|
+
if (!isStale) setConfig(next);
|
|
75
|
+
}).catch((error) => {
|
|
76
|
+
console.warn(
|
|
77
|
+
"[search] Could not load the product search configuration:",
|
|
78
|
+
error
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
return () => {
|
|
82
|
+
isStale = true;
|
|
83
|
+
};
|
|
84
|
+
}, [enabled, country, language]);
|
|
85
|
+
return config;
|
|
86
|
+
}
|
|
18
87
|
|
|
19
88
|
// src/services/SearchApiService.ts
|
|
20
|
-
var DEFAULT_SEARCH_PLACEHOLDER = TRIGGER_PLACEHOLDER;
|
|
21
|
-
var DEFAULT_INPUT_ARIA_LABEL = SEARCH_PLACEHOLDER;
|
|
22
89
|
function canUseStorage(storage) {
|
|
23
90
|
return Boolean(storage) && typeof storage.getItem === "function";
|
|
24
91
|
}
|
|
@@ -37,7 +104,7 @@ function normalizeLocaleParts({
|
|
|
37
104
|
country,
|
|
38
105
|
language,
|
|
39
106
|
locale
|
|
40
|
-
}
|
|
107
|
+
}) {
|
|
41
108
|
const normalizedCountry = String(country ?? "").trim().toUpperCase();
|
|
42
109
|
const normalizedLanguage = String(language ?? "").trim().toLowerCase();
|
|
43
110
|
const normalizedLocale = String(locale ?? "").trim() || "UNKNOWN";
|
|
@@ -228,14 +295,18 @@ function resolveOptionHref(option, localeParts) {
|
|
|
228
295
|
}
|
|
229
296
|
return "";
|
|
230
297
|
}
|
|
231
|
-
|
|
298
|
+
function resolveCustomerTypeHeaders(getCustomerType) {
|
|
299
|
+
const customerType = getCustomerType?.()?.trim();
|
|
300
|
+
return customerType ? { "X-Customer-Type": customerType } : void 0;
|
|
301
|
+
}
|
|
302
|
+
async function fetchJsonFromCandidates(fetchImpl, requestUrls, requestHeaders = {}) {
|
|
232
303
|
let lastError;
|
|
233
304
|
for (const requestUrl of requestUrls) {
|
|
234
305
|
try {
|
|
235
306
|
const response = await fetchImpl(requestUrl, {
|
|
236
307
|
method: "GET",
|
|
237
308
|
credentials: "include",
|
|
238
|
-
headers: { Accept: "application/json" }
|
|
309
|
+
headers: { Accept: "application/json", ...requestHeaders }
|
|
239
310
|
});
|
|
240
311
|
if (!response.ok) {
|
|
241
312
|
lastError = new Error(
|
|
@@ -260,9 +331,9 @@ function createSearchService(options = {}) {
|
|
|
260
331
|
country,
|
|
261
332
|
language,
|
|
262
333
|
locale,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
334
|
+
getMarketConfig,
|
|
335
|
+
getCustomerType,
|
|
336
|
+
minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH
|
|
266
337
|
} = options;
|
|
267
338
|
const fetchImpl = fetchImplOption !== void 0 ? fetchImplOption : typeof fetch === "function" ? fetch.bind(globalThis) : null;
|
|
268
339
|
const locationLike = getBrowserLocation(providedLocation);
|
|
@@ -282,15 +353,9 @@ function createSearchService(options = {}) {
|
|
|
282
353
|
return catalogRequestConfigPromise;
|
|
283
354
|
}
|
|
284
355
|
const service = {
|
|
285
|
-
async getSearchChromeCopy() {
|
|
286
|
-
return {
|
|
287
|
-
placeholder: searchPlaceholder || DEFAULT_SEARCH_PLACEHOLDER,
|
|
288
|
-
inputAriaLabel: inputAriaLabel || DEFAULT_INPUT_ARIA_LABEL
|
|
289
|
-
};
|
|
290
|
-
},
|
|
291
356
|
async fetchSuggestions(searchTerm) {
|
|
292
357
|
const trimmedSearchTerm = String(searchTerm ?? "").trim();
|
|
293
|
-
if (!trimmedSearchTerm || !fetchImpl) {
|
|
358
|
+
if (!trimmedSearchTerm || trimmedSearchTerm.length < minimumSearchLength || !fetchImpl) {
|
|
294
359
|
return {
|
|
295
360
|
suggestions: [],
|
|
296
361
|
didYouMean: [],
|
|
@@ -315,7 +380,8 @@ function createSearchService(options = {}) {
|
|
|
315
380
|
const requestUrls = [`${suggestionsUrl}?${queryParams.toString()}`];
|
|
316
381
|
const responsePayload = await fetchJsonFromCandidates(
|
|
317
382
|
fetchImpl,
|
|
318
|
-
requestUrls
|
|
383
|
+
requestUrls,
|
|
384
|
+
resolveCustomerTypeHeaders(getCustomerType)
|
|
319
385
|
);
|
|
320
386
|
const suggestionsField = Array.isArray(responsePayload?.suggestions) ? responsePayload?.suggestions : [];
|
|
321
387
|
const productField = Array.isArray(responsePayload?.product) ? responsePayload?.product : null;
|
|
@@ -347,33 +413,6 @@ function createSearchService(options = {}) {
|
|
|
347
413
|
return stripLocalePrefix(targetPath, localeParts);
|
|
348
414
|
},
|
|
349
415
|
activateSuggestionAction(option) {
|
|
350
|
-
if (options.onSuggestionAction) {
|
|
351
|
-
const result = options.onSuggestionAction(option);
|
|
352
|
-
if (result instanceof Promise) {
|
|
353
|
-
result.then((handled) => {
|
|
354
|
-
if (!handled) {
|
|
355
|
-
const optionHref2 = resolveOptionHref(
|
|
356
|
-
option,
|
|
357
|
-
localeParts
|
|
358
|
-
);
|
|
359
|
-
if (!optionHref2) {
|
|
360
|
-
const optionQuery2 = option?.value ?? option?.label ?? option?.identifier ?? "";
|
|
361
|
-
service.submitProductSearch(
|
|
362
|
-
String(optionQuery2)
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}).catch((error) => {
|
|
367
|
-
console.error(
|
|
368
|
-
"Error in onSuggestionAction:",
|
|
369
|
-
error
|
|
370
|
-
);
|
|
371
|
-
});
|
|
372
|
-
return "action-pending";
|
|
373
|
-
} else if (result === true) {
|
|
374
|
-
return "action-handled";
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
416
|
const optionHref = resolveOptionHref(option, localeParts);
|
|
378
417
|
if (optionHref) {
|
|
379
418
|
return stripLocalePrefix(optionHref, localeParts);
|
|
@@ -400,547 +439,33 @@ function createSearchService(options = {}) {
|
|
|
400
439
|
return service;
|
|
401
440
|
}
|
|
402
441
|
|
|
403
|
-
//
|
|
404
|
-
var
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
442
|
+
// src/services/marketConfig.ts
|
|
443
|
+
var fetchMarketConfig = async ({ country }) => {
|
|
444
|
+
const countryCode = String(country ?? "").trim().toUpperCase() || "US";
|
|
445
|
+
const configuration = await getConfiguration([EQUINOX_MARKETS_MAP], {
|
|
446
|
+
country: countryCode,
|
|
447
|
+
...getEnvironmentCode() ? { environment: getEnvironmentCode() } : {},
|
|
448
|
+
clientId: DEFAULT_EQUINOX_CLIENT_ID,
|
|
449
|
+
shoppingContext: STOREFRONT_SHOPPING_CONTEXT
|
|
450
|
+
});
|
|
451
|
+
return configuration?.[EQUINOX_MARKETS_MAP] ?? null;
|
|
413
452
|
};
|
|
414
453
|
|
|
415
|
-
//
|
|
416
|
-
var
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
var
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config;
|
|
427
|
-
const pendingFragments = [];
|
|
428
|
-
let pendingFragmentsLength = 0;
|
|
429
|
-
let isFirstChunk = true;
|
|
430
|
-
let id;
|
|
431
|
-
let data = "";
|
|
432
|
-
let dataLines = 0;
|
|
433
|
-
let eventType;
|
|
434
|
-
let terminated = false;
|
|
435
|
-
let skippingLine = false;
|
|
436
|
-
let skipNextLineFeed = false;
|
|
437
|
-
function feed(chunk) {
|
|
438
|
-
if (terminated) {
|
|
439
|
-
throw new Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");
|
|
440
|
-
}
|
|
441
|
-
if (isFirstChunk) {
|
|
442
|
-
isFirstChunk = false;
|
|
443
|
-
if (chunk.charCodeAt(0) === 65279) {
|
|
444
|
-
chunk = chunk.slice(1);
|
|
445
|
-
} else if (chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191) {
|
|
446
|
-
chunk = chunk.slice(3);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
if (skippingLine || skipNextLineFeed) {
|
|
450
|
-
chunk = resumeAfterSkip(chunk);
|
|
451
|
-
if (!chunk) {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
if (!pendingFragments.length) {
|
|
456
|
-
const trailing = processLines(chunk);
|
|
457
|
-
if (trailing !== "") {
|
|
458
|
-
storeTrailing(trailing);
|
|
459
|
-
}
|
|
460
|
-
checkBufferSize();
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
if (chunk.indexOf("\n") === -1 && chunk.indexOf("\r") === -1) {
|
|
464
|
-
if (pendingFragmentsLength < MAX_FIELD_PREFIX_LENGTH) {
|
|
465
|
-
const head = pendingFragments.join("") + chunk.slice(0, MAX_FIELD_PREFIX_LENGTH - pendingFragmentsLength);
|
|
466
|
-
if (!shouldBufferTrailing(head)) {
|
|
467
|
-
pendingFragments.length = 0;
|
|
468
|
-
pendingFragmentsLength = 0;
|
|
469
|
-
skippingLine = true;
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
pendingFragments.push(chunk);
|
|
474
|
-
pendingFragmentsLength += chunk.length;
|
|
475
|
-
checkBufferSize();
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
pendingFragments.push(chunk);
|
|
479
|
-
const input = pendingFragments.join("");
|
|
480
|
-
pendingFragments.length = 0;
|
|
481
|
-
pendingFragmentsLength = 0;
|
|
482
|
-
storeTrailing(processLines(input));
|
|
483
|
-
checkBufferSize();
|
|
484
|
-
}
|
|
485
|
-
function resumeAfterSkip(chunk) {
|
|
486
|
-
if (chunk.length === 0) {
|
|
487
|
-
return chunk;
|
|
488
|
-
}
|
|
489
|
-
if (skipNextLineFeed) {
|
|
490
|
-
skipNextLineFeed = false;
|
|
491
|
-
return chunk.charCodeAt(0) === LF ? chunk.slice(1) : chunk;
|
|
492
|
-
}
|
|
493
|
-
const crIndex = chunk.indexOf("\r");
|
|
494
|
-
const lfIndex = chunk.indexOf("\n");
|
|
495
|
-
const lineEnd = crIndex === -1 ? lfIndex : lfIndex === -1 ? crIndex : crIndex < lfIndex ? crIndex : lfIndex;
|
|
496
|
-
if (lineEnd === -1) {
|
|
497
|
-
return "";
|
|
498
|
-
}
|
|
499
|
-
if (lineEnd === chunk.length - 1 && chunk.charCodeAt(lineEnd) === CR) {
|
|
500
|
-
skippingLine = false;
|
|
501
|
-
skipNextLineFeed = true;
|
|
502
|
-
return "";
|
|
503
|
-
}
|
|
504
|
-
skippingLine = false;
|
|
505
|
-
return chunk.slice(lineEnd + (chunk.charCodeAt(lineEnd) === CR && chunk.charCodeAt(lineEnd + 1) === LF ? 2 : 1));
|
|
506
|
-
}
|
|
507
|
-
function storeTrailing(trailing) {
|
|
508
|
-
if (!trailing)
|
|
509
|
-
return;
|
|
510
|
-
if (trailing.charCodeAt(trailing.length - 1) === CR) {
|
|
511
|
-
parseLine(trailing, 0, trailing.length - 1);
|
|
512
|
-
skipNextLineFeed = true;
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
if (shouldBufferTrailing(trailing)) {
|
|
516
|
-
pendingFragments.push(trailing);
|
|
517
|
-
pendingFragmentsLength = trailing.length;
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
skippingLine = true;
|
|
521
|
-
}
|
|
522
|
-
function shouldBufferTrailing(trailing) {
|
|
523
|
-
const firstCharCode = trailing.charCodeAt(0);
|
|
524
|
-
return firstCharCode === 58 && !!onComment || firstCharCode === 100 && isPotentialField(trailing, "data") || firstCharCode === 101 && isPotentialField(trailing, "event") || firstCharCode === 105 && isPotentialField(trailing, "id") || firstCharCode === 114 && isPotentialField(trailing, "retry");
|
|
525
|
-
}
|
|
526
|
-
function checkBufferSize() {
|
|
527
|
-
if (maxBufferSize === void 0)
|
|
528
|
-
return;
|
|
529
|
-
if (pendingFragmentsLength + data.length <= maxBufferSize)
|
|
530
|
-
return;
|
|
531
|
-
terminated = true;
|
|
532
|
-
pendingFragments.length = 0;
|
|
533
|
-
pendingFragmentsLength = 0;
|
|
534
|
-
id = void 0;
|
|
535
|
-
data = "";
|
|
536
|
-
dataLines = 0;
|
|
537
|
-
eventType = void 0;
|
|
538
|
-
skippingLine = false;
|
|
539
|
-
skipNextLineFeed = false;
|
|
540
|
-
onError(new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
|
|
541
|
-
type: "max-buffer-size-exceeded"
|
|
542
|
-
}));
|
|
543
|
-
}
|
|
544
|
-
function processLines(chunk) {
|
|
545
|
-
let searchIndex = 0;
|
|
546
|
-
if (chunk.indexOf("\r") === -1) {
|
|
547
|
-
let lfIndex = chunk.indexOf("\n", searchIndex);
|
|
548
|
-
while (lfIndex !== -1) {
|
|
549
|
-
if (searchIndex === lfIndex) {
|
|
550
|
-
if (dataLines > 0) {
|
|
551
|
-
onEvent({ id, event: eventType, data });
|
|
552
|
-
}
|
|
553
|
-
id = void 0;
|
|
554
|
-
data = "";
|
|
555
|
-
dataLines = 0;
|
|
556
|
-
eventType = void 0;
|
|
557
|
-
searchIndex = lfIndex + 1;
|
|
558
|
-
lfIndex = chunk.indexOf("\n", searchIndex);
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
const firstCharCode = chunk.charCodeAt(searchIndex);
|
|
562
|
-
if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
|
|
563
|
-
const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5;
|
|
564
|
-
const value = chunk.slice(valueStart, lfIndex);
|
|
565
|
-
if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
|
|
566
|
-
onEvent({ id, event: eventType, data: value });
|
|
567
|
-
id = void 0;
|
|
568
|
-
data = "";
|
|
569
|
-
eventType = void 0;
|
|
570
|
-
searchIndex = lfIndex + 2;
|
|
571
|
-
lfIndex = chunk.indexOf("\n", searchIndex);
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
data = dataLines === 0 ? value : `${data}
|
|
575
|
-
${value}`;
|
|
576
|
-
dataLines++;
|
|
577
|
-
} else if (isEventPrefix(chunk, searchIndex, firstCharCode)) {
|
|
578
|
-
eventType = chunk.slice(chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6, lfIndex) || void 0;
|
|
579
|
-
} else {
|
|
580
|
-
parseLine(chunk, searchIndex, lfIndex);
|
|
581
|
-
}
|
|
582
|
-
searchIndex = lfIndex + 1;
|
|
583
|
-
lfIndex = chunk.indexOf("\n", searchIndex);
|
|
584
|
-
}
|
|
585
|
-
return chunk.slice(searchIndex);
|
|
586
|
-
}
|
|
587
|
-
while (searchIndex < chunk.length) {
|
|
588
|
-
const crIndex = chunk.indexOf("\r", searchIndex);
|
|
589
|
-
const lfIndex = chunk.indexOf("\n", searchIndex);
|
|
590
|
-
let lineEnd = -1;
|
|
591
|
-
if (crIndex !== -1 && lfIndex !== -1) {
|
|
592
|
-
lineEnd = crIndex < lfIndex ? crIndex : lfIndex;
|
|
593
|
-
} else if (crIndex !== -1) {
|
|
594
|
-
if (crIndex === chunk.length - 1) {
|
|
595
|
-
lineEnd = -1;
|
|
596
|
-
} else {
|
|
597
|
-
lineEnd = crIndex;
|
|
598
|
-
}
|
|
599
|
-
} else if (lfIndex !== -1) {
|
|
600
|
-
lineEnd = lfIndex;
|
|
601
|
-
}
|
|
602
|
-
if (lineEnd === -1) {
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
605
|
-
parseLine(chunk, searchIndex, lineEnd);
|
|
606
|
-
searchIndex = lineEnd + 1;
|
|
607
|
-
if (chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF) {
|
|
608
|
-
searchIndex++;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
return chunk.slice(searchIndex);
|
|
612
|
-
}
|
|
613
|
-
function parseLine(chunk, start, end) {
|
|
614
|
-
if (start === end) {
|
|
615
|
-
dispatchEvent();
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
const firstCharCode = chunk.charCodeAt(start);
|
|
619
|
-
if (isDataPrefix(chunk, start, firstCharCode)) {
|
|
620
|
-
const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5;
|
|
621
|
-
const value2 = chunk.slice(valueStart, end);
|
|
622
|
-
data = dataLines === 0 ? value2 : `${data}
|
|
623
|
-
${value2}`;
|
|
624
|
-
dataLines++;
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
if (isEventPrefix(chunk, start, firstCharCode)) {
|
|
628
|
-
eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
|
|
632
|
-
const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
|
|
633
|
-
if (!value2.includes("\0"))
|
|
634
|
-
id = value2;
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
if (firstCharCode === 58) {
|
|
638
|
-
if (onComment) {
|
|
639
|
-
const line2 = chunk.slice(start, end);
|
|
640
|
-
onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
|
|
641
|
-
}
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
const line = chunk.slice(start, end);
|
|
645
|
-
const fieldSeparatorIndex = line.indexOf(":");
|
|
646
|
-
if (fieldSeparatorIndex === -1) {
|
|
647
|
-
processField(line, "", line);
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
const field = line.slice(0, fieldSeparatorIndex);
|
|
651
|
-
const offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1;
|
|
652
|
-
const value = line.slice(fieldSeparatorIndex + offset);
|
|
653
|
-
processField(field, value, line);
|
|
654
|
-
}
|
|
655
|
-
function processField(field, value, line) {
|
|
656
|
-
switch (field) {
|
|
657
|
-
case "event":
|
|
658
|
-
eventType = value || void 0;
|
|
659
|
-
break;
|
|
660
|
-
case "data":
|
|
661
|
-
data = dataLines === 0 ? value : `${data}
|
|
662
|
-
${value}`;
|
|
663
|
-
dataLines++;
|
|
664
|
-
break;
|
|
665
|
-
case "id":
|
|
666
|
-
if (!value.includes("\0"))
|
|
667
|
-
id = value;
|
|
668
|
-
break;
|
|
669
|
-
case "retry":
|
|
670
|
-
if (/^\d+$/.test(value)) {
|
|
671
|
-
onRetry(parseInt(value, 10));
|
|
672
|
-
} else {
|
|
673
|
-
onError(new ParseError(`Invalid \`retry\` value: "${value}"`, {
|
|
674
|
-
type: "invalid-retry",
|
|
675
|
-
value,
|
|
676
|
-
line
|
|
677
|
-
}));
|
|
678
|
-
}
|
|
679
|
-
break;
|
|
680
|
-
default:
|
|
681
|
-
onError(new ParseError(`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`, { type: "unknown-field", field, value, line }));
|
|
682
|
-
break;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
function dispatchEvent() {
|
|
686
|
-
if (dataLines > 0) {
|
|
687
|
-
onEvent({
|
|
688
|
-
id,
|
|
689
|
-
event: eventType,
|
|
690
|
-
data
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
id = void 0;
|
|
694
|
-
data = "";
|
|
695
|
-
dataLines = 0;
|
|
696
|
-
eventType = void 0;
|
|
697
|
-
}
|
|
698
|
-
function reset(options = {}) {
|
|
699
|
-
if (options.consume && pendingFragments.length > 0) {
|
|
700
|
-
const incompleteLine = pendingFragments.join("");
|
|
701
|
-
parseLine(incompleteLine, 0, incompleteLine.length);
|
|
702
|
-
}
|
|
703
|
-
isFirstChunk = true;
|
|
704
|
-
id = void 0;
|
|
705
|
-
data = "";
|
|
706
|
-
dataLines = 0;
|
|
707
|
-
eventType = void 0;
|
|
708
|
-
pendingFragments.length = 0;
|
|
709
|
-
pendingFragmentsLength = 0;
|
|
710
|
-
terminated = false;
|
|
711
|
-
skippingLine = false;
|
|
712
|
-
skipNextLineFeed = false;
|
|
713
|
-
}
|
|
714
|
-
return { feed, reset };
|
|
715
|
-
}
|
|
716
|
-
function isDataPrefix(chunk, i, firstCharCode) {
|
|
717
|
-
return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
|
|
718
|
-
}
|
|
719
|
-
function isEventPrefix(chunk, i, firstCharCode) {
|
|
720
|
-
return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
|
|
721
|
-
}
|
|
722
|
-
function isPotentialField(line, field) {
|
|
723
|
-
let i = 1;
|
|
724
|
-
while (i < line.length && i < field.length) {
|
|
725
|
-
if (line.charCodeAt(i) !== field.charCodeAt(i)) {
|
|
726
|
-
return false;
|
|
727
|
-
}
|
|
728
|
-
i++;
|
|
729
|
-
}
|
|
730
|
-
return line.length <= field.length || line.charCodeAt(field.length) === 58;
|
|
731
|
-
}
|
|
454
|
+
// src/utils/gtmConstants.ts
|
|
455
|
+
var SEARCH_GTM_EVENTS = {
|
|
456
|
+
CHAT_ENTRY_POINT: "Begin_chat",
|
|
457
|
+
PRODUCT_SEARCH: "ProductSearch"};
|
|
458
|
+
var SEARCH_GTM_SEARCH_TYPES = {
|
|
459
|
+
SEMANTIC: "semantic search"};
|
|
460
|
+
var SEARCH_GTM_USER_STATUS = {
|
|
461
|
+
GUEST: "guest",
|
|
462
|
+
LOGGED_IN: "logged_in"
|
|
463
|
+
};
|
|
464
|
+
var GTM_FALLBACK_NA = "NA";
|
|
732
465
|
|
|
733
|
-
// src/
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
function findProductsBlockStart(content, openingFenceIndex) {
|
|
737
|
-
let blockStart = openingFenceIndex + PRODUCTS_OPENING_FENCE.length;
|
|
738
|
-
while (blockStart < content.length && (content[blockStart] === " " || content[blockStart] === " " || content[blockStart] === "\r")) {
|
|
739
|
-
blockStart += 1;
|
|
740
|
-
}
|
|
741
|
-
return content[blockStart] === "\n" ? blockStart + 1 : null;
|
|
742
|
-
}
|
|
743
|
-
function isStandaloneClosingFence(content, fenceEnd) {
|
|
744
|
-
const lineEnd = content.indexOf("\n", fenceEnd);
|
|
745
|
-
const suffixEnd = lineEnd === -1 ? content.length : lineEnd;
|
|
746
|
-
return content.slice(fenceEnd, suffixEnd).trim() === "";
|
|
747
|
-
}
|
|
748
|
-
function findClosingFence(content, fromIndex) {
|
|
749
|
-
let candidate = content.indexOf(MARKDOWN_CLOSING_FENCE, fromIndex);
|
|
750
|
-
while (candidate !== -1) {
|
|
751
|
-
const fenceEnd = candidate + MARKDOWN_CLOSING_FENCE.length;
|
|
752
|
-
if (isStandaloneClosingFence(content, fenceEnd)) {
|
|
753
|
-
return candidate;
|
|
754
|
-
}
|
|
755
|
-
candidate = content.indexOf(MARKDOWN_CLOSING_FENCE, fenceEnd);
|
|
756
|
-
}
|
|
757
|
-
return -1;
|
|
758
|
-
}
|
|
759
|
-
function isRecord(value) {
|
|
760
|
-
return value !== null && typeof value === "object";
|
|
761
|
-
}
|
|
762
|
-
function getRenderProductListItems(event) {
|
|
763
|
-
if (event["type"] !== "render" || event["pattern"] !== "product_list") {
|
|
764
|
-
return [];
|
|
765
|
-
}
|
|
766
|
-
const data = event["data"];
|
|
767
|
-
if (!isRecord(data) || !Array.isArray(data["items"])) {
|
|
768
|
-
return [];
|
|
769
|
-
}
|
|
770
|
-
return data["items"].filter(isRecord);
|
|
771
|
-
}
|
|
772
|
-
function extractProductsFromMarkdown(content) {
|
|
773
|
-
const products = [];
|
|
774
|
-
const summaryParts = [];
|
|
775
|
-
let summaryCursor = 0;
|
|
776
|
-
let searchCursor = 0;
|
|
777
|
-
while (searchCursor < content.length) {
|
|
778
|
-
const start = content.indexOf(PRODUCTS_OPENING_FENCE, searchCursor);
|
|
779
|
-
if (start === -1) break;
|
|
780
|
-
const blockStart = findProductsBlockStart(content, start);
|
|
781
|
-
if (blockStart === null) {
|
|
782
|
-
searchCursor = start + PRODUCTS_OPENING_FENCE.length;
|
|
783
|
-
continue;
|
|
784
|
-
}
|
|
785
|
-
const nextOpeningFence = content.indexOf(
|
|
786
|
-
PRODUCTS_OPENING_FENCE,
|
|
787
|
-
blockStart
|
|
788
|
-
);
|
|
789
|
-
const end = findClosingFence(content, blockStart);
|
|
790
|
-
if (end === -1) break;
|
|
791
|
-
if (nextOpeningFence !== -1 && nextOpeningFence < end) {
|
|
792
|
-
searchCursor = nextOpeningFence;
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
try {
|
|
796
|
-
const parsed = JSON.parse(content.slice(blockStart, end));
|
|
797
|
-
if (Array.isArray(parsed)) {
|
|
798
|
-
products.push(...parsed);
|
|
799
|
-
}
|
|
800
|
-
} catch (e) {
|
|
801
|
-
console.error("Failed to parse products block:", e);
|
|
802
|
-
}
|
|
803
|
-
summaryParts.push(content.slice(summaryCursor, start));
|
|
804
|
-
summaryCursor = end + MARKDOWN_CLOSING_FENCE.length;
|
|
805
|
-
searchCursor = summaryCursor;
|
|
806
|
-
}
|
|
807
|
-
summaryParts.push(content.slice(summaryCursor));
|
|
808
|
-
return { summary: summaryParts.join("").trim(), products };
|
|
809
|
-
}
|
|
810
|
-
function createChatService(options) {
|
|
811
|
-
const {
|
|
812
|
-
conversationsUrl = DEFAULT_AI_CHAT_CONVERSATIONS_URL,
|
|
813
|
-
messagesUrl = DEFAULT_AI_CHAT_MESSAGES_URL,
|
|
814
|
-
salesAiAppKey,
|
|
815
|
-
salesAiParentOrigin,
|
|
816
|
-
market,
|
|
817
|
-
language,
|
|
818
|
-
fetchImpl: fetchImplOption
|
|
819
|
-
} = options;
|
|
820
|
-
let fetchImpl;
|
|
821
|
-
if (fetchImplOption !== void 0) {
|
|
822
|
-
fetchImpl = fetchImplOption;
|
|
823
|
-
} else if (typeof fetch === "function") {
|
|
824
|
-
fetchImpl = fetch.bind(globalThis);
|
|
825
|
-
} else {
|
|
826
|
-
fetchImpl = () => {
|
|
827
|
-
throw new Error("ChatService: no fetch implementation available");
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
function buildHeaders(extra, includeJsonContentType = true) {
|
|
831
|
-
const headers = {
|
|
832
|
-
...extra
|
|
833
|
-
};
|
|
834
|
-
if (includeJsonContentType) {
|
|
835
|
-
headers["Content-Type"] = "application/json";
|
|
836
|
-
}
|
|
837
|
-
const accessToken = typeof window !== "undefined" ? window.localStorage?.getItem("accessToken") : null;
|
|
838
|
-
if (accessToken) {
|
|
839
|
-
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
840
|
-
}
|
|
841
|
-
if (salesAiAppKey) {
|
|
842
|
-
headers["X-Sales-AI-App-Key"] = salesAiAppKey;
|
|
843
|
-
}
|
|
844
|
-
if (salesAiParentOrigin) {
|
|
845
|
-
headers["X-Sales-AI-Parent-Origin"] = salesAiParentOrigin;
|
|
846
|
-
}
|
|
847
|
-
return headers;
|
|
848
|
-
}
|
|
849
|
-
return {
|
|
850
|
-
async createConversation() {
|
|
851
|
-
const res = await fetchImpl(conversationsUrl, {
|
|
852
|
-
method: "POST",
|
|
853
|
-
headers: buildHeaders(void 0, false)
|
|
854
|
-
});
|
|
855
|
-
if (!res.ok) {
|
|
856
|
-
throw new Error(
|
|
857
|
-
`ChatService.createConversation: ${res.status} ${res.statusText}`
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
const data = await res.json();
|
|
861
|
-
const id = data["id"] ?? data["conversation_id"];
|
|
862
|
-
if (typeof id !== "number") {
|
|
863
|
-
throw new TypeError(
|
|
864
|
-
"ChatService.createConversation: response missing numeric id"
|
|
865
|
-
);
|
|
866
|
-
}
|
|
867
|
-
return id;
|
|
868
|
-
},
|
|
869
|
-
async sendMessage(conversationId, content, onChunk) {
|
|
870
|
-
const requestBody = {
|
|
871
|
-
conversation_id: conversationId,
|
|
872
|
-
content
|
|
873
|
-
};
|
|
874
|
-
if (market || language) {
|
|
875
|
-
requestBody.scope_context = {
|
|
876
|
-
market: market ?? "US",
|
|
877
|
-
language: language ?? "en"
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
requestBody.client_capabilities = {
|
|
881
|
-
patterns: {
|
|
882
|
-
product_list: [1],
|
|
883
|
-
follow_up: [1]
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
const res = await fetchImpl(messagesUrl, {
|
|
887
|
-
method: "POST",
|
|
888
|
-
headers: buildHeaders({ Accept: "text/event-stream" }),
|
|
889
|
-
body: JSON.stringify(requestBody)
|
|
890
|
-
});
|
|
891
|
-
if (!res.ok) {
|
|
892
|
-
throw new Error(
|
|
893
|
-
`ChatService.sendMessage: ${res.status} ${res.statusText}`
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
|
-
let accumulated = "";
|
|
897
|
-
const renderedProducts = [];
|
|
898
|
-
const parser = createParser({
|
|
899
|
-
onEvent(event) {
|
|
900
|
-
const data = event.data;
|
|
901
|
-
if (!data || data === "[DONE]") return;
|
|
902
|
-
try {
|
|
903
|
-
const parsed = JSON.parse(data);
|
|
904
|
-
const parsedType = parsed["type"];
|
|
905
|
-
const type = typeof parsedType === "string" ? parsedType : "";
|
|
906
|
-
renderedProducts.push(
|
|
907
|
-
...getRenderProductListItems(parsed)
|
|
908
|
-
);
|
|
909
|
-
if (type === "content" && typeof parsed["text"] === "string") {
|
|
910
|
-
const text = parsed["text"];
|
|
911
|
-
accumulated += text;
|
|
912
|
-
onChunk?.(type, text);
|
|
913
|
-
} else if (type === "status" && typeof parsed["message"] === "string") {
|
|
914
|
-
const message = parsed["message"];
|
|
915
|
-
onChunk?.(type, message);
|
|
916
|
-
}
|
|
917
|
-
} catch {
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
const body = res.body;
|
|
922
|
-
if (body && typeof body.getReader === "function") {
|
|
923
|
-
const reader = body.getReader();
|
|
924
|
-
const decoder = new TextDecoder();
|
|
925
|
-
while (true) {
|
|
926
|
-
const { value, done } = await reader.read();
|
|
927
|
-
if (done) break;
|
|
928
|
-
parser.feed(decoder.decode(value, { stream: true }));
|
|
929
|
-
}
|
|
930
|
-
parser.feed(decoder.decode());
|
|
931
|
-
} else {
|
|
932
|
-
const rawBody = await res.text();
|
|
933
|
-
parser.feed(rawBody);
|
|
934
|
-
}
|
|
935
|
-
const { summary, products } = extractProductsFromMarkdown(accumulated);
|
|
936
|
-
const finalProducts = renderedProducts.length > 0 ? renderedProducts : products;
|
|
937
|
-
return {
|
|
938
|
-
summary,
|
|
939
|
-
products: finalProducts,
|
|
940
|
-
rawContent: accumulated
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
};
|
|
466
|
+
// src/utils/navigationTarget.ts
|
|
467
|
+
function toNavigationTarget(path) {
|
|
468
|
+
return /^https?:\/\//i.test(path) ? { type: "external", url: path } : { type: "internal", path };
|
|
944
469
|
}
|
|
945
470
|
|
|
946
471
|
// src/utils/catalogHelpers.ts
|
|
@@ -958,9 +483,6 @@ var CART_DISABLED_PRODUCT_STATUSES = /* @__PURE__ */ new Set([
|
|
|
958
483
|
function normalizeProductStatusValue(value) {
|
|
959
484
|
return String(value ?? "").trim().toLowerCase().replace(/\s+/g, "");
|
|
960
485
|
}
|
|
961
|
-
function isSellableProductStatus(productStatus) {
|
|
962
|
-
return normalizeProductStatusValue(productStatus) === "sellable";
|
|
963
|
-
}
|
|
964
486
|
function resolveSkuStatusValues(suggestionOption) {
|
|
965
487
|
return [
|
|
966
488
|
suggestionOption["productStatus"],
|
|
@@ -1205,14 +727,6 @@ function getVisibleSuggestions(opts) {
|
|
|
1205
727
|
}
|
|
1206
728
|
return opts.suggestions;
|
|
1207
729
|
}
|
|
1208
|
-
function getSeeMoreResultsPayload(query, totalResultsCount, suggestions) {
|
|
1209
|
-
return {
|
|
1210
|
-
query,
|
|
1211
|
-
source: "autocomplete-footer",
|
|
1212
|
-
totalResultsCount,
|
|
1213
|
-
visibleResultsCount: suggestions.length
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
730
|
|
|
1217
731
|
// src/hooks/useSearchSuggestions.ts
|
|
1218
732
|
function normalizeSuggestionsResponse(response) {
|
|
@@ -1251,6 +765,7 @@ function useSearchSuggestions({
|
|
|
1251
765
|
const [suggestionFetchFailed, setSuggestionFetchFailed] = useState(false);
|
|
1252
766
|
const [hasMoreResults, setHasMoreResults] = useState(false);
|
|
1253
767
|
const [totalResultsCount, setTotalResultsCount] = useState(0);
|
|
768
|
+
const [settledQuery, setSettledQuery] = useState("");
|
|
1254
769
|
const fetchDebounced = useRef(null);
|
|
1255
770
|
const latestRequestId = useRef(0);
|
|
1256
771
|
const resetSuggestionsState = useCallback(() => {
|
|
@@ -1260,6 +775,7 @@ function useSearchSuggestions({
|
|
|
1260
775
|
setSuggestionFetchFailed(false);
|
|
1261
776
|
setHasMoreResults(false);
|
|
1262
777
|
setTotalResultsCount(0);
|
|
778
|
+
setSettledQuery("");
|
|
1263
779
|
setLoading(false);
|
|
1264
780
|
}, []);
|
|
1265
781
|
const updateQuery = useCallback(
|
|
@@ -1310,6 +826,7 @@ function useSearchSuggestions({
|
|
|
1310
826
|
);
|
|
1311
827
|
setTotalResultsCount(effectiveTotal);
|
|
1312
828
|
setSuggestionFetchFailed(false);
|
|
829
|
+
setSettledQuery(trimmedQuery);
|
|
1313
830
|
setEmptyFetched(
|
|
1314
831
|
hasDidYouMean ? false : visibleSuggestions.length === 0 && suggestedTerms.length === 0
|
|
1315
832
|
);
|
|
@@ -1323,6 +840,7 @@ function useSearchSuggestions({
|
|
|
1323
840
|
setSuggestionFetchFailed(true);
|
|
1324
841
|
setHasMoreResults(false);
|
|
1325
842
|
setTotalResultsCount(0);
|
|
843
|
+
setSettledQuery(trimmedQuery);
|
|
1326
844
|
} finally {
|
|
1327
845
|
if (requestId === latestRequestId.current) {
|
|
1328
846
|
setLoading(false);
|
|
@@ -1357,6 +875,7 @@ function useSearchSuggestions({
|
|
|
1357
875
|
didYouMean,
|
|
1358
876
|
loading,
|
|
1359
877
|
query,
|
|
878
|
+
settledQuery,
|
|
1360
879
|
setQuery,
|
|
1361
880
|
suggestionFetchFailed,
|
|
1362
881
|
suggestions,
|
|
@@ -1365,382 +884,6 @@ function useSearchSuggestions({
|
|
|
1365
884
|
};
|
|
1366
885
|
}
|
|
1367
886
|
|
|
1368
|
-
// node_modules/zustand/esm/vanilla.mjs
|
|
1369
|
-
var createStoreImpl = (createState) => {
|
|
1370
|
-
let state;
|
|
1371
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1372
|
-
const setState = (partial, replace) => {
|
|
1373
|
-
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
1374
|
-
if (!Object.is(nextState, state)) {
|
|
1375
|
-
const previousState = state;
|
|
1376
|
-
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
1377
|
-
listeners.forEach((listener) => listener(state, previousState));
|
|
1378
|
-
}
|
|
1379
|
-
};
|
|
1380
|
-
const getState = () => state;
|
|
1381
|
-
const getInitialState = () => initialState;
|
|
1382
|
-
const subscribe = (listener) => {
|
|
1383
|
-
listeners.add(listener);
|
|
1384
|
-
return () => listeners.delete(listener);
|
|
1385
|
-
};
|
|
1386
|
-
const api = { setState, getState, getInitialState, subscribe };
|
|
1387
|
-
const initialState = state = createState(setState, getState, api);
|
|
1388
|
-
return api;
|
|
1389
|
-
};
|
|
1390
|
-
var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
1391
|
-
var identity = (arg) => arg;
|
|
1392
|
-
function useStore(api, selector = identity) {
|
|
1393
|
-
const slice = React.useSyncExternalStore(
|
|
1394
|
-
api.subscribe,
|
|
1395
|
-
() => selector(api.getState()),
|
|
1396
|
-
() => selector(api.getInitialState())
|
|
1397
|
-
);
|
|
1398
|
-
React.useDebugValue(slice);
|
|
1399
|
-
return slice;
|
|
1400
|
-
}
|
|
1401
|
-
var createImpl = (createState) => {
|
|
1402
|
-
const api = createStore(createState);
|
|
1403
|
-
const useBoundStore = (selector) => useStore(api, selector);
|
|
1404
|
-
Object.assign(useBoundStore, api);
|
|
1405
|
-
return useBoundStore;
|
|
1406
|
-
};
|
|
1407
|
-
var create = (createState) => createImpl;
|
|
1408
|
-
|
|
1409
|
-
// node_modules/zustand/esm/middleware.mjs
|
|
1410
|
-
function createJSONStorage(getStorage, options) {
|
|
1411
|
-
let storage;
|
|
1412
|
-
try {
|
|
1413
|
-
storage = getStorage();
|
|
1414
|
-
} catch (e) {
|
|
1415
|
-
return;
|
|
1416
|
-
}
|
|
1417
|
-
const persistStorage = {
|
|
1418
|
-
getItem: (name) => {
|
|
1419
|
-
var _a;
|
|
1420
|
-
const parse = (str2) => {
|
|
1421
|
-
if (str2 === null) {
|
|
1422
|
-
return null;
|
|
1423
|
-
}
|
|
1424
|
-
return JSON.parse(str2, void 0 );
|
|
1425
|
-
};
|
|
1426
|
-
const str = (_a = storage.getItem(name)) != null ? _a : null;
|
|
1427
|
-
if (str instanceof Promise) {
|
|
1428
|
-
return str.then(parse);
|
|
1429
|
-
}
|
|
1430
|
-
return parse(str);
|
|
1431
|
-
},
|
|
1432
|
-
setItem: (name, newValue) => storage.setItem(
|
|
1433
|
-
name,
|
|
1434
|
-
JSON.stringify(newValue, void 0 )
|
|
1435
|
-
),
|
|
1436
|
-
removeItem: (name) => storage.removeItem(name)
|
|
1437
|
-
};
|
|
1438
|
-
return persistStorage;
|
|
1439
|
-
}
|
|
1440
|
-
var toThenable = (fn) => (input) => {
|
|
1441
|
-
try {
|
|
1442
|
-
const result = fn(input);
|
|
1443
|
-
if (result instanceof Promise) {
|
|
1444
|
-
return result;
|
|
1445
|
-
}
|
|
1446
|
-
return {
|
|
1447
|
-
then(onFulfilled) {
|
|
1448
|
-
return toThenable(onFulfilled)(result);
|
|
1449
|
-
},
|
|
1450
|
-
catch(_onRejected) {
|
|
1451
|
-
return this;
|
|
1452
|
-
}
|
|
1453
|
-
};
|
|
1454
|
-
} catch (e) {
|
|
1455
|
-
return {
|
|
1456
|
-
then(_onFulfilled) {
|
|
1457
|
-
return this;
|
|
1458
|
-
},
|
|
1459
|
-
catch(onRejected) {
|
|
1460
|
-
return toThenable(onRejected)(e);
|
|
1461
|
-
}
|
|
1462
|
-
};
|
|
1463
|
-
}
|
|
1464
|
-
};
|
|
1465
|
-
var persistImpl = (config, baseOptions) => (set, get, api) => {
|
|
1466
|
-
let options = {
|
|
1467
|
-
storage: createJSONStorage(() => localStorage),
|
|
1468
|
-
partialize: (state) => state,
|
|
1469
|
-
version: 0,
|
|
1470
|
-
merge: (persistedState, currentState) => ({
|
|
1471
|
-
...currentState,
|
|
1472
|
-
...persistedState
|
|
1473
|
-
}),
|
|
1474
|
-
...baseOptions
|
|
1475
|
-
};
|
|
1476
|
-
let hasHydrated = false;
|
|
1477
|
-
const hydrationListeners = /* @__PURE__ */ new Set();
|
|
1478
|
-
const finishHydrationListeners = /* @__PURE__ */ new Set();
|
|
1479
|
-
let storage = options.storage;
|
|
1480
|
-
if (!storage) {
|
|
1481
|
-
return config(
|
|
1482
|
-
(...args) => {
|
|
1483
|
-
console.warn(
|
|
1484
|
-
`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
|
|
1485
|
-
);
|
|
1486
|
-
set(...args);
|
|
1487
|
-
},
|
|
1488
|
-
get,
|
|
1489
|
-
api
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
const setItem = () => {
|
|
1493
|
-
const state = options.partialize({ ...get() });
|
|
1494
|
-
return storage.setItem(options.name, {
|
|
1495
|
-
state,
|
|
1496
|
-
version: options.version
|
|
1497
|
-
});
|
|
1498
|
-
};
|
|
1499
|
-
const savedSetState = api.setState;
|
|
1500
|
-
api.setState = (state, replace) => {
|
|
1501
|
-
savedSetState(state, replace);
|
|
1502
|
-
void setItem();
|
|
1503
|
-
};
|
|
1504
|
-
const configResult = config(
|
|
1505
|
-
(...args) => {
|
|
1506
|
-
set(...args);
|
|
1507
|
-
void setItem();
|
|
1508
|
-
},
|
|
1509
|
-
get,
|
|
1510
|
-
api
|
|
1511
|
-
);
|
|
1512
|
-
api.getInitialState = () => configResult;
|
|
1513
|
-
let stateFromStorage;
|
|
1514
|
-
const hydrate = () => {
|
|
1515
|
-
var _a, _b;
|
|
1516
|
-
if (!storage) return;
|
|
1517
|
-
hasHydrated = false;
|
|
1518
|
-
hydrationListeners.forEach((cb) => {
|
|
1519
|
-
var _a2;
|
|
1520
|
-
return cb((_a2 = get()) != null ? _a2 : configResult);
|
|
1521
|
-
});
|
|
1522
|
-
const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;
|
|
1523
|
-
return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {
|
|
1524
|
-
if (deserializedStorageValue) {
|
|
1525
|
-
if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) {
|
|
1526
|
-
if (options.migrate) {
|
|
1527
|
-
return [
|
|
1528
|
-
true,
|
|
1529
|
-
options.migrate(
|
|
1530
|
-
deserializedStorageValue.state,
|
|
1531
|
-
deserializedStorageValue.version
|
|
1532
|
-
)
|
|
1533
|
-
];
|
|
1534
|
-
}
|
|
1535
|
-
console.error(
|
|
1536
|
-
`State loaded from storage couldn't be migrated since no migrate function was provided`
|
|
1537
|
-
);
|
|
1538
|
-
} else {
|
|
1539
|
-
return [false, deserializedStorageValue.state];
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
return [false, void 0];
|
|
1543
|
-
}).then((migrationResult) => {
|
|
1544
|
-
var _a2;
|
|
1545
|
-
const [migrated, migratedState] = migrationResult;
|
|
1546
|
-
stateFromStorage = options.merge(
|
|
1547
|
-
migratedState,
|
|
1548
|
-
(_a2 = get()) != null ? _a2 : configResult
|
|
1549
|
-
);
|
|
1550
|
-
set(stateFromStorage, true);
|
|
1551
|
-
if (migrated) {
|
|
1552
|
-
return setItem();
|
|
1553
|
-
}
|
|
1554
|
-
}).then(() => {
|
|
1555
|
-
postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);
|
|
1556
|
-
stateFromStorage = get();
|
|
1557
|
-
hasHydrated = true;
|
|
1558
|
-
finishHydrationListeners.forEach((cb) => cb(stateFromStorage));
|
|
1559
|
-
}).catch((e) => {
|
|
1560
|
-
postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);
|
|
1561
|
-
});
|
|
1562
|
-
};
|
|
1563
|
-
api.persist = {
|
|
1564
|
-
setOptions: (newOptions) => {
|
|
1565
|
-
options = {
|
|
1566
|
-
...options,
|
|
1567
|
-
...newOptions
|
|
1568
|
-
};
|
|
1569
|
-
if (newOptions.storage) {
|
|
1570
|
-
storage = newOptions.storage;
|
|
1571
|
-
}
|
|
1572
|
-
},
|
|
1573
|
-
clearStorage: () => {
|
|
1574
|
-
storage == null ? void 0 : storage.removeItem(options.name);
|
|
1575
|
-
},
|
|
1576
|
-
getOptions: () => options,
|
|
1577
|
-
rehydrate: () => hydrate(),
|
|
1578
|
-
hasHydrated: () => hasHydrated,
|
|
1579
|
-
onHydrate: (cb) => {
|
|
1580
|
-
hydrationListeners.add(cb);
|
|
1581
|
-
return () => {
|
|
1582
|
-
hydrationListeners.delete(cb);
|
|
1583
|
-
};
|
|
1584
|
-
},
|
|
1585
|
-
onFinishHydration: (cb) => {
|
|
1586
|
-
finishHydrationListeners.add(cb);
|
|
1587
|
-
return () => {
|
|
1588
|
-
finishHydrationListeners.delete(cb);
|
|
1589
|
-
};
|
|
1590
|
-
}
|
|
1591
|
-
};
|
|
1592
|
-
if (!options.skipHydration) {
|
|
1593
|
-
hydrate();
|
|
1594
|
-
}
|
|
1595
|
-
return stateFromStorage || configResult;
|
|
1596
|
-
};
|
|
1597
|
-
var persist = persistImpl;
|
|
1598
|
-
|
|
1599
|
-
// src/store/searchHistoryStore.ts
|
|
1600
|
-
var MAX_HISTORY = 10;
|
|
1601
|
-
var useSearchHistoryStore = create()(
|
|
1602
|
-
persist(
|
|
1603
|
-
(set) => ({
|
|
1604
|
-
entries: [],
|
|
1605
|
-
addEntry: (entry) => {
|
|
1606
|
-
const trimmed = entry.query.trim();
|
|
1607
|
-
if (!trimmed) return;
|
|
1608
|
-
set((state) => {
|
|
1609
|
-
const deduped = state.entries.filter(
|
|
1610
|
-
(e) => e.query.toLowerCase() !== trimmed.toLowerCase()
|
|
1611
|
-
);
|
|
1612
|
-
const newEntry = {
|
|
1613
|
-
...entry,
|
|
1614
|
-
query: trimmed,
|
|
1615
|
-
timestamp: Date.now()
|
|
1616
|
-
};
|
|
1617
|
-
return {
|
|
1618
|
-
entries: [newEntry, ...deduped].slice(0, MAX_HISTORY)
|
|
1619
|
-
};
|
|
1620
|
-
});
|
|
1621
|
-
},
|
|
1622
|
-
addQuery: (query) => {
|
|
1623
|
-
const trimmed = query.trim();
|
|
1624
|
-
if (!trimmed) return;
|
|
1625
|
-
set((state) => {
|
|
1626
|
-
const deduped = state.entries.filter(
|
|
1627
|
-
(e) => e.query.toLowerCase() !== trimmed.toLowerCase()
|
|
1628
|
-
);
|
|
1629
|
-
return {
|
|
1630
|
-
entries: [
|
|
1631
|
-
{
|
|
1632
|
-
query: trimmed,
|
|
1633
|
-
isAI: false,
|
|
1634
|
-
timestamp: Date.now()
|
|
1635
|
-
},
|
|
1636
|
-
...deduped
|
|
1637
|
-
].slice(0, MAX_HISTORY)
|
|
1638
|
-
};
|
|
1639
|
-
});
|
|
1640
|
-
},
|
|
1641
|
-
removeEntry: (query) => set((state) => ({
|
|
1642
|
-
entries: state.entries.filter((e) => e.query !== query)
|
|
1643
|
-
})),
|
|
1644
|
-
clearAll: () => set({ entries: [] })
|
|
1645
|
-
}),
|
|
1646
|
-
{
|
|
1647
|
-
name: "ai_search_recent_queries",
|
|
1648
|
-
version: 1,
|
|
1649
|
-
storage: createJSONStorage(
|
|
1650
|
-
() => typeof window !== "undefined" ? window.sessionStorage : {}
|
|
1651
|
-
),
|
|
1652
|
-
// Migrate from v0 (queries: string[]) to v1 (entries: SearchHistoryEntry[])
|
|
1653
|
-
migrate(persistedState, version) {
|
|
1654
|
-
if (version === 0) {
|
|
1655
|
-
const old = persistedState;
|
|
1656
|
-
if (Array.isArray(old?.queries)) {
|
|
1657
|
-
return {
|
|
1658
|
-
entries: old.queries.map((q) => ({
|
|
1659
|
-
query: q,
|
|
1660
|
-
isAI: false,
|
|
1661
|
-
timestamp: Date.now()
|
|
1662
|
-
}))
|
|
1663
|
-
};
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
return persistedState;
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
)
|
|
1670
|
-
);
|
|
1671
|
-
|
|
1672
|
-
// src/hooks/useSearchHistory.ts
|
|
1673
|
-
function useSearchHistory() {
|
|
1674
|
-
const entries = useSearchHistoryStore((s) => s.entries);
|
|
1675
|
-
const addEntry = useSearchHistoryStore((s) => s.addEntry);
|
|
1676
|
-
const addQuery = useSearchHistoryStore((s) => s.addQuery);
|
|
1677
|
-
const removeEntry = useSearchHistoryStore((s) => s.removeEntry);
|
|
1678
|
-
const clearAll = useSearchHistoryStore((s) => s.clearAll);
|
|
1679
|
-
return {
|
|
1680
|
-
entries,
|
|
1681
|
-
queries: entries.map((e) => e.query),
|
|
1682
|
-
addEntry,
|
|
1683
|
-
addQuery,
|
|
1684
|
-
removeEntry,
|
|
1685
|
-
clearAll
|
|
1686
|
-
};
|
|
1687
|
-
}
|
|
1688
|
-
function getSpeechRecognition() {
|
|
1689
|
-
if (typeof window === "undefined") return null;
|
|
1690
|
-
const w = window;
|
|
1691
|
-
return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
|
|
1692
|
-
}
|
|
1693
|
-
function useVoiceSearch({
|
|
1694
|
-
lang,
|
|
1695
|
-
onInterimResult,
|
|
1696
|
-
onResult,
|
|
1697
|
-
onError
|
|
1698
|
-
}) {
|
|
1699
|
-
const [listening, setListening] = useState(false);
|
|
1700
|
-
const recognitionRef = useRef(null);
|
|
1701
|
-
const supported = getSpeechRecognition() !== null;
|
|
1702
|
-
const stop = useCallback(() => {
|
|
1703
|
-
recognitionRef.current?.stop();
|
|
1704
|
-
recognitionRef.current = null;
|
|
1705
|
-
setListening(false);
|
|
1706
|
-
}, []);
|
|
1707
|
-
const start = useCallback(() => {
|
|
1708
|
-
const SR = getSpeechRecognition();
|
|
1709
|
-
if (!SR) return;
|
|
1710
|
-
stop();
|
|
1711
|
-
const rec = new SR();
|
|
1712
|
-
rec.lang = lang ?? navigator.language ?? "en-US";
|
|
1713
|
-
rec.interimResults = true;
|
|
1714
|
-
rec.maxAlternatives = 1;
|
|
1715
|
-
rec.onresult = (event) => {
|
|
1716
|
-
let finalTranscript = "";
|
|
1717
|
-
let interimTranscript = "";
|
|
1718
|
-
const startIndex = event.resultIndex ?? 0;
|
|
1719
|
-
for (let index = startIndex; index < event.results.length; index += 1) {
|
|
1720
|
-
const result = event.results[index];
|
|
1721
|
-
const transcript = result?.[0]?.transcript ?? "";
|
|
1722
|
-
if (!transcript) continue;
|
|
1723
|
-
if (result?.isFinal) {
|
|
1724
|
-
finalTranscript += transcript;
|
|
1725
|
-
} else {
|
|
1726
|
-
interimTranscript += transcript;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
if (interimTranscript) onInterimResult?.(interimTranscript);
|
|
1730
|
-
if (finalTranscript) onResult(finalTranscript);
|
|
1731
|
-
};
|
|
1732
|
-
rec.onerror = (event) => {
|
|
1733
|
-
onError?.(event);
|
|
1734
|
-
setListening(false);
|
|
1735
|
-
};
|
|
1736
|
-
rec.onend = () => setListening(false);
|
|
1737
|
-
recognitionRef.current = rec;
|
|
1738
|
-
rec.start();
|
|
1739
|
-
setListening(true);
|
|
1740
|
-
}, [lang, onInterimResult, onResult, onError, stop]);
|
|
1741
|
-
return { listening, supported, start, stop };
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
887
|
// src/utils/commonUtils.ts
|
|
1745
888
|
var IS_BROWSER = typeof window !== "undefined";
|
|
1746
889
|
function toEventString(value, fallback = " ") {
|
|
@@ -1748,119 +891,24 @@ function toEventString(value, fallback = " ") {
|
|
|
1748
891
|
const str = String(value).trim();
|
|
1749
892
|
return str.length > 0 ? str : fallback;
|
|
1750
893
|
}
|
|
1751
|
-
function getLocaleFromUrl(pathname) {
|
|
1752
|
-
const path = pathname ?? (IS_BROWSER ? window.location.pathname : "");
|
|
1753
|
-
const [country, language] = path.split("/").filter(Boolean);
|
|
1754
|
-
if (!/^[a-z]{2}$/i.test(country ?? "") || !/^[a-z]{2}$/i.test(language ?? "")) {
|
|
1755
|
-
return {};
|
|
1756
|
-
}
|
|
1757
|
-
return {
|
|
1758
|
-
countryCode: country.toUpperCase(),
|
|
1759
|
-
language: language.toLowerCase()
|
|
1760
|
-
};
|
|
1761
|
-
}
|
|
1762
|
-
function safeParse(raw) {
|
|
1763
|
-
try {
|
|
1764
|
-
return JSON.parse(raw);
|
|
1765
|
-
} catch {
|
|
1766
|
-
return raw;
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
function getLocalStorage(key) {
|
|
1770
|
-
if (!IS_BROWSER) return void 0;
|
|
1771
|
-
try {
|
|
1772
|
-
const raw = window.localStorage.getItem(key);
|
|
1773
|
-
return raw === null ? void 0 : safeParse(raw);
|
|
1774
|
-
} catch {
|
|
1775
|
-
return void 0;
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
function getSessionStorage2(key) {
|
|
1779
|
-
if (!IS_BROWSER) return void 0;
|
|
1780
|
-
try {
|
|
1781
|
-
const raw = window.sessionStorage.getItem(key);
|
|
1782
|
-
return raw === null ? void 0 : safeParse(raw);
|
|
1783
|
-
} catch {
|
|
1784
|
-
return void 0;
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
// src/utils/gtmConstants.ts
|
|
1789
|
-
var SEARCH_GTM_EVENTS = {
|
|
1790
|
-
CHAT_ENTRY_POINT: "Begin_chat",
|
|
1791
|
-
SEARCH_TYPE: "Search_Type",
|
|
1792
|
-
PRODUCT_SEARCH: "ProductSearch",
|
|
1793
|
-
ERROR_POPUP: "Error_popup"
|
|
1794
|
-
};
|
|
1795
|
-
var SEARCH_GTM_SEARCH_TYPES = {
|
|
1796
|
-
SEMANTIC: "semantic search",
|
|
1797
|
-
AI_AGENT: "AI agent"
|
|
1798
|
-
};
|
|
1799
|
-
var SEARCH_GTM_USER_STATUS = {
|
|
1800
|
-
GUEST: "guest",
|
|
1801
|
-
LOGGED_IN: "logged_in"
|
|
1802
|
-
};
|
|
1803
|
-
var AI_CHAT_CONVERSATION_ID_KEY = "ai_chat_conversation_id";
|
|
1804
|
-
var IS_AUTHENTICATED_KEY = "IsAuthenticated";
|
|
1805
|
-
var ACCOUNT_TYPE_KEY = "accountType";
|
|
1806
|
-
var NUSKIN_ACCOUNT_DATA_KEY = "nuskin.account.data";
|
|
1807
|
-
var GTM_FALLBACK_NA = "NA";
|
|
1808
894
|
|
|
1809
895
|
// src/utils/gtmTracking.ts
|
|
1810
|
-
function readSessionChatId() {
|
|
1811
|
-
const raw = getSessionStorage2(AI_CHAT_CONVERSATION_ID_KEY);
|
|
1812
|
-
return typeof raw === "string" || typeof raw === "number" ? String(raw) : void 0;
|
|
1813
|
-
}
|
|
1814
|
-
function readUserStatusFromStorage() {
|
|
1815
|
-
const value = getLocalStorage(IS_AUTHENTICATED_KEY);
|
|
1816
|
-
if (value === void 0) return void 0;
|
|
1817
|
-
if (value === SEARCH_GTM_USER_STATUS.GUEST || value === SEARCH_GTM_USER_STATUS.LOGGED_IN) {
|
|
1818
|
-
return value;
|
|
1819
|
-
}
|
|
1820
|
-
const isAuthenticated = value === true || typeof value === "string" && ["true", "1", "yes", "y"].includes(value.toLowerCase().trim());
|
|
1821
|
-
return isAuthenticated ? SEARCH_GTM_USER_STATUS.LOGGED_IN : SEARCH_GTM_USER_STATUS.GUEST;
|
|
1822
|
-
}
|
|
1823
|
-
function readAccountTypeFromStorage() {
|
|
1824
|
-
const value = getLocalStorage(ACCOUNT_TYPE_KEY);
|
|
1825
|
-
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1826
|
-
}
|
|
1827
|
-
function readUserIdFromStorage() {
|
|
1828
|
-
const raw = getLocalStorage(NUSKIN_ACCOUNT_DATA_KEY);
|
|
1829
|
-
if (!raw || typeof raw !== "object") return void 0;
|
|
1830
|
-
const user = raw.user;
|
|
1831
|
-
if (!user || typeof user !== "object") return void 0;
|
|
1832
|
-
const id = user.id;
|
|
1833
|
-
return typeof id === "string" && id.trim().length > 0 ? id : void 0;
|
|
1834
|
-
}
|
|
1835
896
|
function resolveUserId(explicit) {
|
|
1836
|
-
return toEventString(explicit
|
|
897
|
+
return toEventString(explicit, GTM_FALLBACK_NA);
|
|
1837
898
|
}
|
|
1838
899
|
function baseEventPayload(ctx) {
|
|
1839
|
-
const urlLocale = getLocaleFromUrl();
|
|
1840
|
-
const chatId = ctx.chatId ?? readSessionChatId();
|
|
1841
|
-
const userStatus = ctx.userStatus ?? readUserStatusFromStorage();
|
|
1842
|
-
const accountType = ctx.accountType ?? readAccountTypeFromStorage();
|
|
1843
|
-
const resolvedUserStatus = userStatus ?? SEARCH_GTM_USER_STATUS.GUEST;
|
|
1844
|
-
const documentTitle = IS_BROWSER && typeof document?.title === "string" ? document.title : void 0;
|
|
1845
900
|
return {
|
|
1846
|
-
Chat_ID: toEventString(chatId, GTM_FALLBACK_NA),
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
),
|
|
1851
|
-
|
|
1852
|
-
ctx.countryCode ?? urlLocale.countryCode,
|
|
1853
|
-
GTM_FALLBACK_NA
|
|
1854
|
-
),
|
|
1855
|
-
language: toEventString(
|
|
1856
|
-
ctx.language ?? urlLocale.language,
|
|
1857
|
-
GTM_FALLBACK_NA
|
|
1858
|
-
),
|
|
901
|
+
Chat_ID: toEventString(ctx.chatId, GTM_FALLBACK_NA),
|
|
902
|
+
// TODO: COMX-3277 puts `page_type` in every event but defines no values. Send `NA` until
|
|
903
|
+
// the data team says what it should hold (perhaps the page path).
|
|
904
|
+
page_type: GTM_FALLBACK_NA,
|
|
905
|
+
country_code: toEventString(ctx.countryCode, GTM_FALLBACK_NA),
|
|
906
|
+
language: toEventString(ctx.language, GTM_FALLBACK_NA),
|
|
1859
907
|
user_status: toEventString(
|
|
1860
|
-
|
|
908
|
+
ctx.userStatus ?? SEARCH_GTM_USER_STATUS.GUEST,
|
|
1861
909
|
SEARCH_GTM_USER_STATUS.GUEST
|
|
1862
910
|
),
|
|
1863
|
-
account_type: toEventString(accountType, GTM_FALLBACK_NA)
|
|
911
|
+
account_type: toEventString(ctx.accountType, GTM_FALLBACK_NA)
|
|
1864
912
|
};
|
|
1865
913
|
}
|
|
1866
914
|
function serializeProductResults(productResultsData, productResults) {
|
|
@@ -1893,15 +941,6 @@ function trackChatEntryPoint(ctx) {
|
|
|
1893
941
|
...baseEventPayload(ctx)
|
|
1894
942
|
});
|
|
1895
943
|
}
|
|
1896
|
-
function trackSearchType(input) {
|
|
1897
|
-
pushGtmEvent({
|
|
1898
|
-
event: SEARCH_GTM_EVENTS.SEARCH_TYPE,
|
|
1899
|
-
...baseEventPayload(input),
|
|
1900
|
-
id: resolveUserId(input.userId),
|
|
1901
|
-
type: input.searchType ?? SEARCH_GTM_SEARCH_TYPES.SEMANTIC,
|
|
1902
|
-
search_response: toEventString(input.searchResponse, GTM_FALLBACK_NA)
|
|
1903
|
-
});
|
|
1904
|
-
}
|
|
1905
944
|
function trackProductSearch(input) {
|
|
1906
945
|
pushGtmEvent({
|
|
1907
946
|
event: SEARCH_GTM_EVENTS.PRODUCT_SEARCH,
|
|
@@ -1914,15 +953,471 @@ function trackProductSearch(input) {
|
|
|
1914
953
|
)
|
|
1915
954
|
});
|
|
1916
955
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
956
|
+
|
|
957
|
+
// src/utils/nuiSearchGate.ts
|
|
958
|
+
var NUI_SEARCH_FLAG = "enableNuiSearch";
|
|
959
|
+
function parseFlagValue(raw) {
|
|
960
|
+
const value = raw?.trim().toLowerCase();
|
|
961
|
+
if (value === "true" || value === "1") return true;
|
|
962
|
+
if (value === "false" || value === "0") return false;
|
|
963
|
+
return void 0;
|
|
964
|
+
}
|
|
965
|
+
function readCookieFlag(cookie) {
|
|
966
|
+
for (const part of cookie.split(";")) {
|
|
967
|
+
const separator = part.indexOf("=");
|
|
968
|
+
if (separator < 0) continue;
|
|
969
|
+
if (part.slice(0, separator).trim() !== NUI_SEARCH_FLAG) continue;
|
|
970
|
+
const rawValue = part.slice(separator + 1);
|
|
971
|
+
try {
|
|
972
|
+
return parseFlagValue(decodeURIComponent(rawValue));
|
|
973
|
+
} catch {
|
|
974
|
+
return void 0;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return void 0;
|
|
978
|
+
}
|
|
979
|
+
function resolveNuiSearchFlag(env = {}) {
|
|
980
|
+
const inBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
981
|
+
const search = env.search ?? (inBrowser ? window.location.search : "");
|
|
982
|
+
const cookie = env.cookie ?? (inBrowser ? document.cookie : "");
|
|
983
|
+
const setCookie = env.setCookie ?? (inBrowser ? (value) => {
|
|
984
|
+
document.cookie = value;
|
|
985
|
+
} : void 0);
|
|
986
|
+
const fromQuery = parseFlagValue(
|
|
987
|
+
new URLSearchParams(search).get(NUI_SEARCH_FLAG)
|
|
988
|
+
);
|
|
989
|
+
if (fromQuery !== void 0) {
|
|
990
|
+
const secure = inBrowser && window.location.protocol === "https:" ? "; Secure" : "";
|
|
991
|
+
setCookie?.(
|
|
992
|
+
`${NUI_SEARCH_FLAG}=${fromQuery}; path=/; SameSite=Lax${secure}`
|
|
993
|
+
);
|
|
994
|
+
return fromQuery;
|
|
995
|
+
}
|
|
996
|
+
return readCookieFlag(cookie);
|
|
997
|
+
}
|
|
998
|
+
function isNuiSearchEnabled({
|
|
999
|
+
isAuthenticated,
|
|
1000
|
+
flag
|
|
1001
|
+
}) {
|
|
1002
|
+
return isAuthenticated === true && flag === true;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/utils/searchRouting.ts
|
|
1006
|
+
function decideSearchRoute({
|
|
1007
|
+
enabled,
|
|
1008
|
+
snapshot
|
|
1009
|
+
}) {
|
|
1010
|
+
const term = snapshot.query.trim();
|
|
1011
|
+
if (!enabled || !term) return "search";
|
|
1012
|
+
if (snapshot.loading || snapshot.settledQuery !== term) return "wait";
|
|
1013
|
+
if (snapshot.suggestionFetchFailed) return "search";
|
|
1014
|
+
return snapshot.suggestions.length > 0 ? "search" : "handoff";
|
|
1015
|
+
}
|
|
1016
|
+
var HANDLE_POLL_MS = 50;
|
|
1017
|
+
var HANDLE_TIMEOUT_MS = 15e3;
|
|
1018
|
+
var SalesAIChatWidget = lazy(async () => {
|
|
1019
|
+
const [widget] = await Promise.all([
|
|
1020
|
+
import('@nuskin/react-nui-chat-sdk'),
|
|
1021
|
+
import('@nuskin/react-nui-chat-sdk/styles.css')
|
|
1022
|
+
]);
|
|
1023
|
+
return { default: widget.SalesAIChatWidget };
|
|
1024
|
+
});
|
|
1025
|
+
function getSalesAIHandle() {
|
|
1026
|
+
return window.SalesAI;
|
|
1027
|
+
}
|
|
1028
|
+
var AgentErrorBoundary = class extends Component {
|
|
1029
|
+
constructor() {
|
|
1030
|
+
super(...arguments);
|
|
1031
|
+
this.state = { failed: false };
|
|
1032
|
+
}
|
|
1033
|
+
static getDerivedStateFromError() {
|
|
1034
|
+
return { failed: true };
|
|
1035
|
+
}
|
|
1036
|
+
componentDidCatch(_error, _info) {
|
|
1037
|
+
this.props.onError();
|
|
1038
|
+
}
|
|
1039
|
+
render() {
|
|
1040
|
+
return this.state.failed ? null : this.props.children;
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
function useHandoff({
|
|
1044
|
+
query,
|
|
1045
|
+
handoffContext,
|
|
1046
|
+
agentContext,
|
|
1047
|
+
onUnavailable
|
|
1048
|
+
}) {
|
|
1049
|
+
useEffect(() => {
|
|
1050
|
+
let cancelled = false;
|
|
1051
|
+
let timer;
|
|
1052
|
+
const startedAt = Date.now();
|
|
1053
|
+
const tryOpen = () => {
|
|
1054
|
+
if (cancelled) return;
|
|
1055
|
+
const handle = getSalesAIHandle();
|
|
1056
|
+
if (handle) {
|
|
1057
|
+
handle.open({
|
|
1058
|
+
query,
|
|
1059
|
+
context: { ...agentContext, ...handoffContext }
|
|
1060
|
+
});
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
if (Date.now() - startedAt >= HANDLE_TIMEOUT_MS) {
|
|
1064
|
+
onUnavailable();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
timer = setTimeout(tryOpen, HANDLE_POLL_MS);
|
|
1068
|
+
};
|
|
1069
|
+
tryOpen();
|
|
1070
|
+
return () => {
|
|
1071
|
+
cancelled = true;
|
|
1072
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1073
|
+
};
|
|
1074
|
+
}, [query]);
|
|
1075
|
+
}
|
|
1076
|
+
function NuiAgentPanel({
|
|
1077
|
+
agent,
|
|
1078
|
+
query,
|
|
1079
|
+
handoffContext,
|
|
1080
|
+
onUnavailable
|
|
1081
|
+
}) {
|
|
1082
|
+
useHandoff({
|
|
1083
|
+
query,
|
|
1084
|
+
handoffContext,
|
|
1085
|
+
agentContext: agent.context,
|
|
1086
|
+
onUnavailable
|
|
1087
|
+
});
|
|
1088
|
+
return /* @__PURE__ */ jsx(AgentErrorBoundary, { onError: onUnavailable, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
1089
|
+
SalesAIChatWidget,
|
|
1090
|
+
{
|
|
1091
|
+
appKey: agent.appKey,
|
|
1092
|
+
apiBaseUrl: agent.apiBaseUrl,
|
|
1093
|
+
theme: agent.theme ?? "nui-light",
|
|
1094
|
+
defaultOpen: true,
|
|
1095
|
+
hideFab: true,
|
|
1096
|
+
placement: "host",
|
|
1097
|
+
...agent.language === void 0 ? {} : { language: agent.language },
|
|
1098
|
+
...agent.renderProduct === void 0 ? {} : { renderProduct: agent.renderProduct }
|
|
1099
|
+
}
|
|
1100
|
+
) }) });
|
|
1101
|
+
}
|
|
1102
|
+
var SUBMIT_WAIT_TIMEOUT_MS = 2e3;
|
|
1103
|
+
function uniqueRecentSearches(queries) {
|
|
1104
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1105
|
+
return queries.filter((query) => {
|
|
1106
|
+
const key = query.trim().toLowerCase();
|
|
1107
|
+
if (!key || seen.has(key)) return false;
|
|
1108
|
+
seen.add(key);
|
|
1109
|
+
return true;
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
function CommerceSearchView({
|
|
1113
|
+
isOpen,
|
|
1114
|
+
onClose,
|
|
1115
|
+
service,
|
|
1116
|
+
navigate,
|
|
1117
|
+
isAuthenticated,
|
|
1118
|
+
agent,
|
|
1119
|
+
logo,
|
|
1120
|
+
minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH,
|
|
1121
|
+
gtmContext
|
|
1122
|
+
}) {
|
|
1123
|
+
const [flag, setFlag] = useState(void 0);
|
|
1124
|
+
const [handoff, setHandoff] = useState(null);
|
|
1125
|
+
const agentQuery = handoff?.query ?? null;
|
|
1126
|
+
const [pendingTerm, setPendingTerm] = useState(null);
|
|
1127
|
+
const [recentSearches, setRecentSearches] = useState([]);
|
|
1128
|
+
const search = useSearchSuggestions({
|
|
1129
|
+
service,
|
|
1130
|
+
enabled: isOpen && agentQuery === null
|
|
1131
|
+
});
|
|
1132
|
+
const {
|
|
1133
|
+
clearQuery,
|
|
1134
|
+
didYouMean,
|
|
1135
|
+
emptyFetched,
|
|
1136
|
+
hasMoreResults,
|
|
1137
|
+
loading,
|
|
1138
|
+
query,
|
|
1139
|
+
settledQuery,
|
|
1140
|
+
suggestionFetchFailed,
|
|
1141
|
+
suggestions,
|
|
1142
|
+
updateQuery
|
|
1143
|
+
} = search;
|
|
1144
|
+
const enabled = agent !== void 0 && isNuiSearchEnabled({ isAuthenticated, flag });
|
|
1145
|
+
const items = getVisibleSuggestions({
|
|
1146
|
+
didYouMean,
|
|
1147
|
+
suggestions
|
|
1923
1148
|
});
|
|
1149
|
+
const showDidYouMean = didYouMean.length > 0;
|
|
1150
|
+
useEffect(() => {
|
|
1151
|
+
if (isOpen) setFlag(resolveNuiSearchFlag());
|
|
1152
|
+
}, [isOpen]);
|
|
1153
|
+
useEffect(() => {
|
|
1154
|
+
if (isOpen) {
|
|
1155
|
+
setRecentSearches(
|
|
1156
|
+
uniqueRecentSearches(service.getRecentSearches())
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
}, [isOpen, service, agentQuery]);
|
|
1160
|
+
const close = useCallback(() => {
|
|
1161
|
+
setHandoff(null);
|
|
1162
|
+
setPendingTerm(null);
|
|
1163
|
+
clearQuery();
|
|
1164
|
+
onClose();
|
|
1165
|
+
}, [clearQuery, onClose]);
|
|
1166
|
+
useEffect(() => {
|
|
1167
|
+
if (!isOpen) {
|
|
1168
|
+
setHandoff(null);
|
|
1169
|
+
setPendingTerm(null);
|
|
1170
|
+
clearQuery();
|
|
1171
|
+
}
|
|
1172
|
+
}, [isOpen, clearQuery]);
|
|
1173
|
+
const navigateTo = useCallback(
|
|
1174
|
+
(path) => {
|
|
1175
|
+
if (!path) return;
|
|
1176
|
+
navigate(path);
|
|
1177
|
+
close();
|
|
1178
|
+
},
|
|
1179
|
+
[navigate, close]
|
|
1180
|
+
);
|
|
1181
|
+
const trackSemantic = useCallback(
|
|
1182
|
+
(productResultsData) => {
|
|
1183
|
+
trackProductSearch({
|
|
1184
|
+
...gtmContext,
|
|
1185
|
+
searchType: SEARCH_GTM_SEARCH_TYPES.SEMANTIC,
|
|
1186
|
+
productResultsData
|
|
1187
|
+
});
|
|
1188
|
+
},
|
|
1189
|
+
[gtmContext]
|
|
1190
|
+
);
|
|
1191
|
+
const runSearch = useCallback(
|
|
1192
|
+
(term) => {
|
|
1193
|
+
trackSemantic(items.length > 0 ? items : { query: term });
|
|
1194
|
+
navigateTo(service.submitProductSearch(term));
|
|
1195
|
+
},
|
|
1196
|
+
[items, navigateTo, service, trackSemantic]
|
|
1197
|
+
);
|
|
1198
|
+
const decide = useCallback(
|
|
1199
|
+
(term) => decideSearchRoute({
|
|
1200
|
+
enabled,
|
|
1201
|
+
snapshot: {
|
|
1202
|
+
query: term,
|
|
1203
|
+
loading,
|
|
1204
|
+
settledQuery,
|
|
1205
|
+
suggestions,
|
|
1206
|
+
suggestionFetchFailed
|
|
1207
|
+
}
|
|
1208
|
+
}),
|
|
1209
|
+
[enabled, loading, settledQuery, suggestions, suggestionFetchFailed]
|
|
1210
|
+
);
|
|
1211
|
+
const act = useCallback(
|
|
1212
|
+
(term, route) => {
|
|
1213
|
+
setPendingTerm(null);
|
|
1214
|
+
if (route === "handoff") {
|
|
1215
|
+
trackChatEntryPoint(gtmContext ?? {});
|
|
1216
|
+
setHandoff({
|
|
1217
|
+
query: term,
|
|
1218
|
+
didYouMean: didYouMean.map((item) => item.value)
|
|
1219
|
+
});
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
runSearch(term);
|
|
1223
|
+
},
|
|
1224
|
+
[didYouMean, gtmContext, runSearch]
|
|
1225
|
+
);
|
|
1226
|
+
const handleSubmit = useCallback(
|
|
1227
|
+
(value) => {
|
|
1228
|
+
const term = value.trim();
|
|
1229
|
+
if (!term) return;
|
|
1230
|
+
if (term.length < minimumSearchLength) {
|
|
1231
|
+
runSearch(term);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
const route = decide(term);
|
|
1235
|
+
if (route === "wait") {
|
|
1236
|
+
setPendingTerm(term);
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
act(term, route);
|
|
1240
|
+
},
|
|
1241
|
+
[act, decide, minimumSearchLength, runSearch]
|
|
1242
|
+
);
|
|
1243
|
+
const latest = useRef({ act, decide });
|
|
1244
|
+
latest.current = { act, decide };
|
|
1245
|
+
useEffect(() => {
|
|
1246
|
+
if (pendingTerm === null) return void 0;
|
|
1247
|
+
const route = latest.current.decide(pendingTerm);
|
|
1248
|
+
if (route !== "wait") {
|
|
1249
|
+
latest.current.act(pendingTerm, route);
|
|
1250
|
+
return void 0;
|
|
1251
|
+
}
|
|
1252
|
+
const timer = setTimeout(
|
|
1253
|
+
() => latest.current.act(pendingTerm, "search"),
|
|
1254
|
+
SUBMIT_WAIT_TIMEOUT_MS
|
|
1255
|
+
);
|
|
1256
|
+
return () => clearTimeout(timer);
|
|
1257
|
+
}, [
|
|
1258
|
+
pendingTerm,
|
|
1259
|
+
loading,
|
|
1260
|
+
settledQuery,
|
|
1261
|
+
suggestions,
|
|
1262
|
+
suggestionFetchFailed
|
|
1263
|
+
]);
|
|
1264
|
+
const handleSelect = useCallback(
|
|
1265
|
+
(option) => {
|
|
1266
|
+
if (isSuggestionDisabled(
|
|
1267
|
+
option
|
|
1268
|
+
)) {
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
const term = String(option["value"] ?? option["label"] ?? "");
|
|
1272
|
+
if (term.trim()) trackSemantic([option]);
|
|
1273
|
+
const suggestionOption = option;
|
|
1274
|
+
const href = service.getSuggestionHref(suggestionOption);
|
|
1275
|
+
navigateTo(
|
|
1276
|
+
href || service.submitProductSearch(term, suggestionOption)
|
|
1277
|
+
);
|
|
1278
|
+
},
|
|
1279
|
+
[navigateTo, service, trackSemantic]
|
|
1280
|
+
);
|
|
1281
|
+
const handleRunAction = useCallback(
|
|
1282
|
+
(option) => {
|
|
1283
|
+
if (isSuggestionDisabled(
|
|
1284
|
+
option
|
|
1285
|
+
)) {
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
const result = service.activateSuggestionAction(
|
|
1289
|
+
option
|
|
1290
|
+
);
|
|
1291
|
+
void Promise.resolve(result).then(navigateTo).catch(() => void 0);
|
|
1292
|
+
},
|
|
1293
|
+
[navigateTo, service]
|
|
1294
|
+
);
|
|
1295
|
+
const handleSeeMore = useCallback(() => {
|
|
1296
|
+
const term = query.trim();
|
|
1297
|
+
if (term) navigateTo(service.viewMoreResults({ query: term }));
|
|
1298
|
+
}, [navigateTo, query, service]);
|
|
1299
|
+
const handleRecentSelect = useCallback(
|
|
1300
|
+
(recentQuery) => {
|
|
1301
|
+
trackSemantic({ query: recentQuery });
|
|
1302
|
+
navigateTo(service.submitProductSearch(recentQuery));
|
|
1303
|
+
},
|
|
1304
|
+
[navigateTo, service, trackSemantic]
|
|
1305
|
+
);
|
|
1306
|
+
const handleClearRecent = useCallback(() => {
|
|
1307
|
+
service.clearRecentSearches();
|
|
1308
|
+
setRecentSearches([]);
|
|
1309
|
+
}, [service]);
|
|
1310
|
+
const handleAgentUnavailable = useCallback(() => {
|
|
1311
|
+
if (agentQuery !== null)
|
|
1312
|
+
navigateTo(service.submitProductSearch(agentQuery));
|
|
1313
|
+
}, [agentQuery, navigateTo, service]);
|
|
1314
|
+
return /* @__PURE__ */ jsx(
|
|
1315
|
+
SearchModal,
|
|
1316
|
+
{
|
|
1317
|
+
isOpen,
|
|
1318
|
+
onClose: close,
|
|
1319
|
+
logo,
|
|
1320
|
+
input: {
|
|
1321
|
+
value: query,
|
|
1322
|
+
onChange: updateQuery,
|
|
1323
|
+
onSubmit: handleSubmit
|
|
1324
|
+
},
|
|
1325
|
+
suggestions: {
|
|
1326
|
+
items,
|
|
1327
|
+
loading,
|
|
1328
|
+
showNoResults: !loading && emptyFetched && items.length === 0,
|
|
1329
|
+
showDidYouMean,
|
|
1330
|
+
hasMore: hasMoreResults,
|
|
1331
|
+
query,
|
|
1332
|
+
onSelect: handleSelect,
|
|
1333
|
+
onRunAction: handleRunAction,
|
|
1334
|
+
onSeeMore: handleSeeMore
|
|
1335
|
+
},
|
|
1336
|
+
recentSearches: {
|
|
1337
|
+
items: recentSearches,
|
|
1338
|
+
onSelect: handleRecentSelect,
|
|
1339
|
+
onClear: handleClearRecent
|
|
1340
|
+
},
|
|
1341
|
+
agentPanel: handoff === null || agent === void 0 ? void 0 : /* @__PURE__ */ jsx(
|
|
1342
|
+
NuiAgentPanel,
|
|
1343
|
+
{
|
|
1344
|
+
agent,
|
|
1345
|
+
query: handoff.query,
|
|
1346
|
+
handoffContext: {
|
|
1347
|
+
source: "storefront_search",
|
|
1348
|
+
trigger: "zero_results",
|
|
1349
|
+
query: handoff.query,
|
|
1350
|
+
did_you_mean: handoff.didYouMean
|
|
1351
|
+
},
|
|
1352
|
+
onUnavailable: handleAgentUnavailable
|
|
1353
|
+
}
|
|
1354
|
+
)
|
|
1355
|
+
}
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
var GUEST = "guest";
|
|
1359
|
+
function CommerceSearch({
|
|
1360
|
+
isOpen,
|
|
1361
|
+
onClose,
|
|
1362
|
+
navigate,
|
|
1363
|
+
customer,
|
|
1364
|
+
country,
|
|
1365
|
+
language,
|
|
1366
|
+
logo
|
|
1367
|
+
}) {
|
|
1368
|
+
const [hasOpened, setHasOpened] = useState(false);
|
|
1369
|
+
useEffect(() => {
|
|
1370
|
+
if (isOpen) setHasOpened(true);
|
|
1371
|
+
}, [isOpen]);
|
|
1372
|
+
const { minimumSearchLength, agent } = useCommerceSearchConfig({
|
|
1373
|
+
country,
|
|
1374
|
+
language,
|
|
1375
|
+
enabled: hasOpened
|
|
1376
|
+
});
|
|
1377
|
+
const customerTypeRef = useRef(GUEST);
|
|
1378
|
+
customerTypeRef.current = customer.isAuthenticated ? customer.accountType ?? GUEST : GUEST;
|
|
1379
|
+
const service = useMemo(
|
|
1380
|
+
() => createSearchService({
|
|
1381
|
+
country,
|
|
1382
|
+
language,
|
|
1383
|
+
locale: `${country.toUpperCase()}-${language.toLowerCase()}`,
|
|
1384
|
+
getMarketConfig: fetchMarketConfig,
|
|
1385
|
+
minimumSearchLength,
|
|
1386
|
+
getCustomerType: () => customerTypeRef.current
|
|
1387
|
+
}),
|
|
1388
|
+
[country, language, minimumSearchLength]
|
|
1389
|
+
);
|
|
1390
|
+
const navigateToPath = useCallback(
|
|
1391
|
+
(path) => navigate(toNavigationTarget(path)),
|
|
1392
|
+
[navigate]
|
|
1393
|
+
);
|
|
1394
|
+
const { isAuthenticated, accountType, userId } = customer;
|
|
1395
|
+
const gtmContext = useMemo(
|
|
1396
|
+
() => ({
|
|
1397
|
+
countryCode: country,
|
|
1398
|
+
language,
|
|
1399
|
+
userStatus: isAuthenticated ? SEARCH_GTM_USER_STATUS.LOGGED_IN : SEARCH_GTM_USER_STATUS.GUEST,
|
|
1400
|
+
...isAuthenticated && accountType ? { accountType } : {},
|
|
1401
|
+
...isAuthenticated && userId ? { userId } : {}
|
|
1402
|
+
}),
|
|
1403
|
+
[country, language, isAuthenticated, accountType, userId]
|
|
1404
|
+
);
|
|
1405
|
+
return /* @__PURE__ */ jsx(
|
|
1406
|
+
CommerceSearchView,
|
|
1407
|
+
{
|
|
1408
|
+
isOpen,
|
|
1409
|
+
onClose,
|
|
1410
|
+
service,
|
|
1411
|
+
navigate: navigateToPath,
|
|
1412
|
+
isAuthenticated,
|
|
1413
|
+
...agent ? { agent } : {},
|
|
1414
|
+
logo,
|
|
1415
|
+
minimumSearchLength,
|
|
1416
|
+
gtmContext
|
|
1417
|
+
}
|
|
1418
|
+
);
|
|
1924
1419
|
}
|
|
1925
1420
|
|
|
1926
|
-
export {
|
|
1421
|
+
export { CommerceSearch };
|
|
1927
1422
|
//# sourceMappingURL=index.mjs.map
|
|
1928
1423
|
//# sourceMappingURL=index.mjs.map
|