@envive-ai/react-hooks 0.3.45 → 0.3.47
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/dist/application/commerce-api.cjs +73 -3
- package/dist/application/commerce-api.d.cts +16 -1
- package/dist/application/commerce-api.d.ts +16 -1
- package/dist/application/commerce-api.js +73 -3
- package/dist/application/models/api/userEvent.d.cts +9 -2
- package/dist/application/models/api/userEvent.d.ts +9 -2
- package/dist/application/models/api/widgetText.d.cts +2 -1
- package/dist/application/models/api/widgetText.d.ts +2 -1
- package/dist/application/models/featureGates.cjs +2 -1
- package/dist/application/models/featureGates.d.cts +2 -1
- package/dist/application/models/featureGates.d.ts +2 -1
- package/dist/application/models/featureGates.js +2 -1
- package/dist/application/models/graphql/queries/getProductOptionsQuery.cjs +33 -0
- package/dist/application/models/graphql/queries/getProductOptionsQuery.d.cts +28 -0
- package/dist/application/models/graphql/queries/getProductOptionsQuery.d.ts +28 -0
- package/dist/application/models/graphql/queries/getProductOptionsQuery.js +32 -0
- package/dist/application/models/index.cjs +1 -1
- package/dist/application/models/index.js +1 -1
- package/dist/application/models/validators/validateGraphQLFrontendConfig.cjs +1 -1
- package/dist/application/models/validators/validateGraphQLFrontendConfig.js +1 -1
- package/dist/application/models/validators/validateResponse.cjs +2 -1
- package/dist/application/models/validators/validateResponse.js +2 -1
- package/dist/application/models/validators/validateUserEvent.cjs +3 -3
- package/dist/application/models/validators/validateUserEvent.js +3 -3
- package/dist/application/utils/elementObserver.d.cts +2 -2
- package/dist/application/utils/elementObserver.d.ts +2 -2
- package/dist/application/utils/index.cjs +2 -0
- package/dist/application/utils/index.d.cts +2 -1
- package/dist/application/utils/index.d.ts +2 -1
- package/dist/application/utils/index.js +2 -1
- package/dist/application/utils/messageFromAddToCartEvent.cjs +32 -0
- package/dist/application/utils/messageFromAddToCartEvent.d.cts +19 -0
- package/dist/application/utils/messageFromAddToCartEvent.d.ts +19 -0
- package/dist/application/utils/messageFromAddToCartEvent.js +31 -0
- package/dist/application/utils/widgetTextFromApiWidgetTextResponse.cjs +3 -2
- package/dist/application/utils/widgetTextFromApiWidgetTextResponse.js +3 -2
- package/dist/atoms/app/index.cjs +1 -1
- package/dist/atoms/app/index.d.cts +7 -7
- package/dist/atoms/app/index.d.ts +7 -7
- package/dist/atoms/app/index.js +1 -1
- package/dist/atoms/app/variant.cjs +2 -2
- package/dist/atoms/app/variant.d.cts +7 -7
- package/dist/atoms/app/variant.d.ts +7 -7
- package/dist/atoms/app/variant.js +2 -2
- package/dist/atoms/chat/chatState.d.cts +19 -19
- package/dist/atoms/chat/chatState.d.ts +19 -19
- package/dist/atoms/chat/form.d.cts +2 -2
- package/dist/atoms/chat/form.d.ts +2 -2
- package/dist/atoms/chat/index.cjs +1 -1
- package/dist/atoms/chat/index.d.cts +2 -2
- package/dist/atoms/chat/index.d.ts +2 -2
- package/dist/atoms/chat/index.js +1 -1
- package/dist/atoms/chat/lastMessage.d.cts +2 -2
- package/dist/atoms/chat/lastMessage.d.ts +2 -2
- package/dist/atoms/chat/messageQueue.d.cts +6 -6
- package/dist/atoms/chat/messageQueue.d.ts +6 -6
- package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
- package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
- package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
- package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
- package/dist/atoms/chat/replies.d.cts +3 -3
- package/dist/atoms/chat/replies.d.ts +2 -2
- package/dist/atoms/chat/suggestions.d.cts +2 -2
- package/dist/atoms/chat/suggestions.d.ts +2 -2
- package/dist/atoms/envive/enviveConfig.cjs +7 -4
- package/dist/atoms/envive/enviveConfig.d.cts +16 -14
- package/dist/atoms/envive/enviveConfig.d.ts +16 -14
- package/dist/atoms/envive/enviveConfig.js +6 -4
- package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
- package/dist/atoms/globalSearch/globalSearch.d.ts +5 -5
- package/dist/atoms/org/customerService.d.cts +6 -6
- package/dist/atoms/org/customerService.d.ts +6 -6
- package/dist/atoms/org/graphqlConfig.cjs +1 -1
- package/dist/atoms/org/graphqlConfig.d.cts +6 -6
- package/dist/atoms/org/graphqlConfig.d.ts +6 -6
- package/dist/atoms/org/graphqlConfig.js +1 -1
- package/dist/atoms/org/index.cjs +1 -1
- package/dist/atoms/org/index.js +1 -1
- package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
- package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
- package/dist/atoms/org/orgAnalyticsConfig.d.cts +4 -4
- package/dist/atoms/org/orgAnalyticsConfig.d.ts +4 -4
- package/dist/atoms/search/chatSearch.cjs +1 -1
- package/dist/atoms/search/chatSearch.d.cts +17 -17
- package/dist/atoms/search/chatSearch.d.ts +17 -17
- package/dist/atoms/search/chatSearch.js +1 -1
- package/dist/atoms/search/searchAPI.cjs +1 -1
- package/dist/atoms/search/searchAPI.d.cts +13 -13
- package/dist/atoms/search/searchAPI.d.ts +13 -13
- package/dist/atoms/search/searchAPI.js +1 -1
- package/dist/atoms/widget/chatPreviewLoading.d.cts +2 -2
- package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
- package/dist/contexts/enviveConfigContext/enviveConfigContext.cjs +6 -4
- package/dist/contexts/enviveConfigContext/enviveConfigContext.d.cts +3 -1
- package/dist/contexts/enviveConfigContext/enviveConfigContext.d.ts +3 -1
- package/dist/contexts/enviveConfigContext/enviveConfigContext.js +6 -4
- package/dist/contexts/enviveContext/enviveContext.cjs +7 -3
- package/dist/contexts/enviveContext/enviveContext.d.cts +5 -1
- package/dist/contexts/enviveContext/enviveContext.d.ts +5 -1
- package/dist/contexts/enviveContext/enviveContext.js +7 -3
- package/dist/contexts/enviveContext/types.d.cts +1 -1
- package/dist/contexts/enviveContext/types.d.ts +1 -1
- package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.cjs +2 -97
- package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.cts +3 -25
- package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.ts +3 -25
- package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.js +3 -97
- package/dist/contexts/featureFlagServiceContext/index.cjs +2 -1
- package/dist/contexts/featureFlagServiceContext/index.d.cts +2 -1
- package/dist/contexts/featureFlagServiceContext/index.d.ts +2 -1
- package/dist/contexts/featureFlagServiceContext/index.js +2 -1
- package/dist/contexts/graphqlContext/mockV3Config.cjs +1 -1
- package/dist/contexts/graphqlContext/mockV3Config.js +1 -1
- package/dist/contexts/localStorageContext/index.cjs +2 -1
- package/dist/contexts/localStorageContext/index.d.cts +2 -1
- package/dist/contexts/localStorageContext/index.d.ts +2 -1
- package/dist/contexts/localStorageContext/index.js +2 -1
- package/dist/contexts/localStorageContext/localStorageContext.cjs +5 -11
- package/dist/contexts/localStorageContext/localStorageContext.d.cts +3 -7
- package/dist/contexts/localStorageContext/localStorageContext.d.ts +3 -7
- package/dist/contexts/localStorageContext/localStorageContext.js +3 -8
- package/dist/contexts/localStorageContext/localStorageKeys.cjs +12 -0
- package/dist/contexts/localStorageContext/localStorageKeys.d.cts +9 -0
- package/dist/contexts/localStorageContext/localStorageKeys.d.ts +9 -0
- package/dist/contexts/localStorageContext/localStorageKeys.js +11 -0
- package/dist/contexts/pageContext/pageContext.cjs +2 -2
- package/dist/contexts/pageContext/pageContext.js +2 -2
- package/dist/contexts/salesAgentContext/chatAPI.cjs +15 -4
- package/dist/contexts/salesAgentContext/chatAPI.d.cts +4 -1
- package/dist/contexts/salesAgentContext/chatAPI.d.ts +4 -1
- package/dist/contexts/salesAgentContext/chatAPI.js +16 -5
- package/dist/contexts/salesAgentContext/salesAgentContext.cjs +15 -4
- package/dist/contexts/salesAgentContext/salesAgentContext.d.cts +2 -1
- package/dist/contexts/salesAgentContext/salesAgentContext.d.ts +2 -1
- package/dist/contexts/salesAgentContext/salesAgentContext.js +16 -5
- package/dist/contexts/salesAgentContext/salesAgentService.cjs +12 -4
- package/dist/contexts/salesAgentContext/salesAgentService.js +12 -4
- package/dist/contexts/searchContext/searchContext.cjs +1 -1
- package/dist/contexts/searchContext/searchContext.js +1 -1
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.ts +2 -2
- package/dist/contexts/types.cjs +1 -1
- package/dist/contexts/types.d.cts +3 -1
- package/dist/contexts/types.d.ts +3 -1
- package/dist/contexts/types.js +1 -1
- package/dist/contexts/typesV3.cjs +1 -1
- package/dist/contexts/typesV3.d.cts +11 -2
- package/dist/contexts/typesV3.d.ts +12 -3
- package/dist/contexts/typesV3.js +1 -1
- package/dist/contexts/widgetConfigContext/widgetConfigContext.cjs +1 -1
- package/dist/contexts/widgetConfigContext/widgetConfigContext.js +1 -1
- package/dist/hooks/AddToCart/addToCartErrorMessage.cjs +7 -0
- package/dist/hooks/AddToCart/addToCartErrorMessage.d.cts +5 -0
- package/dist/hooks/AddToCart/addToCartErrorMessage.d.ts +5 -0
- package/dist/hooks/AddToCart/addToCartErrorMessage.js +6 -0
- package/dist/hooks/AddToCart/createAddToCartUserEvent.cjs +20 -0
- package/dist/hooks/AddToCart/createAddToCartUserEvent.d.cts +17 -0
- package/dist/hooks/AddToCart/createAddToCartUserEvent.d.ts +17 -0
- package/dist/hooks/AddToCart/createAddToCartUserEvent.js +19 -0
- package/dist/hooks/AddToCart/index.cjs +9 -0
- package/dist/hooks/AddToCart/index.d.cts +5 -0
- package/dist/hooks/AddToCart/index.d.ts +5 -0
- package/dist/hooks/AddToCart/index.js +6 -0
- package/dist/hooks/AddToCart/parseAddToCartFunction.cjs +27 -0
- package/dist/hooks/AddToCart/parseAddToCartFunction.d.cts +13 -0
- package/dist/hooks/AddToCart/parseAddToCartFunction.d.ts +13 -0
- package/dist/hooks/AddToCart/parseAddToCartFunction.js +27 -0
- package/dist/hooks/AddToCart/useAddToCart.cjs +24 -0
- package/dist/hooks/AddToCart/useAddToCart.d.cts +14 -0
- package/dist/hooks/AddToCart/useAddToCart.d.ts +14 -0
- package/dist/hooks/AddToCart/useAddToCart.js +23 -0
- package/dist/hooks/ChatToggle/useChatToggle.cjs +1 -1
- package/dist/hooks/ChatToggle/useChatToggle.js +1 -1
- package/dist/hooks/LocalStorageOperations/useLocalStorageOperations.cjs +6 -5
- package/dist/hooks/LocalStorageOperations/useLocalStorageOperations.js +3 -2
- package/dist/hooks/PageViewedEvent/usePageViewedEvent.cjs +6 -3
- package/dist/hooks/PageViewedEvent/usePageViewedEvent.js +6 -3
- package/dist/hooks/ProductDetails/index.cjs +9 -0
- package/dist/hooks/ProductDetails/index.d.cts +4 -0
- package/dist/hooks/ProductDetails/index.d.ts +4 -0
- package/dist/hooks/ProductDetails/index.js +4 -0
- package/dist/hooks/ProductDetails/useProductDetails.cjs +53 -0
- package/dist/hooks/ProductDetails/useProductDetails.d.cts +20 -0
- package/dist/hooks/ProductDetails/useProductDetails.d.ts +20 -0
- package/dist/hooks/ProductDetails/useProductDetails.js +52 -0
- package/dist/hooks/ProductDetails/variantSelection.cjs +42 -0
- package/dist/hooks/ProductDetails/variantSelection.d.cts +35 -0
- package/dist/hooks/ProductDetails/variantSelection.d.ts +35 -0
- package/dist/hooks/ProductDetails/variantSelection.js +38 -0
- package/dist/hooks/Search/useSearch.cjs +3 -3
- package/dist/hooks/Search/useSearch.d.cts +1 -1
- package/dist/hooks/Search/useSearch.d.ts +1 -1
- package/dist/hooks/Search/useSearch.js +3 -3
- package/dist/hooks/SystemSettingsContext/useSystemSettingsContext.d.cts +2 -2
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.cjs +11 -2
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.d.cts +1 -10
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.d.ts +1 -10
- package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.js +11 -2
- package/dist/hooks/WidgetInteraction/types.cjs +13 -3
- package/dist/hooks/WidgetInteraction/types.d.cts +29 -4
- package/dist/hooks/WidgetInteraction/types.d.ts +29 -4
- package/dist/hooks/WidgetInteraction/types.js +13 -3
- package/dist/hooks/WidgetInteraction/utils.cjs +2 -2
- package/dist/hooks/WidgetInteraction/utils.js +2 -2
- package/dist/hooks/utils.d.cts +1 -1
- package/dist/hooks/utils.d.ts +1 -1
- package/dist/services/amplitudeService/amplitudeService.cjs +18 -11
- package/dist/services/amplitudeService/amplitudeService.d.cts +5 -2
- package/dist/services/amplitudeService/amplitudeService.d.ts +5 -2
- package/dist/services/amplitudeService/amplitudeService.js +16 -8
- package/dist/services/enviveConfigService/enviveConfigService.cjs +36 -4
- package/dist/services/enviveConfigService/enviveConfigService.d.cts +4 -1
- package/dist/services/enviveConfigService/enviveConfigService.d.ts +4 -1
- package/dist/services/enviveConfigService/enviveConfigService.js +35 -3
- package/dist/services/enviveConfigService/fetchGraphQLConfig.cjs +9 -8
- package/dist/services/enviveConfigService/fetchGraphQLConfig.js +9 -7
- package/dist/services/featureFlagService/index.cjs +101 -0
- package/dist/services/featureFlagService/index.d.cts +28 -0
- package/dist/services/featureFlagService/index.d.ts +28 -0
- package/dist/services/featureFlagService/index.js +101 -0
- package/dist/services/ga4ProjectionService/ga4EventSchema.cjs +8 -2
- package/dist/services/ga4ProjectionService/ga4EventSchema.js +8 -2
- package/dist/services/hardcopyService/hardcopyService.cjs +4 -3
- package/dist/services/hardcopyService/hardcopyService.d.cts +1 -1
- package/dist/services/hardcopyService/hardcopyService.d.ts +1 -1
- package/dist/services/hardcopyService/hardcopyService.js +4 -3
- package/dist/services/urlResolverWarmupService/index.cjs +54 -0
- package/dist/services/urlResolverWarmupService/index.d.cts +40 -0
- package/dist/services/urlResolverWarmupService/index.d.ts +40 -0
- package/dist/services/urlResolverWarmupService/index.js +54 -0
- package/dist/types/enviveConfig.d.cts +6 -2
- package/dist/types/enviveConfig.d.ts +6 -2
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/package.json +18 -2
- package/src/application/__tests__/commerceApiAddToCartRehydration.test.ts +126 -0
- package/src/application/commerce-api.ts +142 -23
- package/src/application/models/api/userEvent.ts +8 -1
- package/src/application/models/api/widgetText.ts +3 -0
- package/src/application/models/featureGates.ts +5 -0
- package/src/application/models/graphql/queries/getProductOptionsQuery.ts +59 -0
- package/src/application/models/validators/__tests__/validateUserEvent.test.ts +41 -0
- package/src/application/models/validators/validateResponse.ts +1 -0
- package/src/application/models/validators/validateUserEvent.ts +7 -2
- package/src/application/utils/__tests__/messageFromAddToCartEvent.test.ts +109 -0
- package/src/application/utils/index.ts +1 -0
- package/src/application/utils/messageFromAddToCartEvent.ts +32 -0
- package/src/application/utils/widgetTextFromApiWidgetTextResponse.ts +1 -0
- package/src/atoms/app/index.ts +1 -1
- package/src/atoms/app/variant.ts +1 -1
- package/src/atoms/envive/enviveConfig.ts +6 -1
- package/src/atoms/org/graphqlConfig.ts +1 -1
- package/src/contexts/enviveConfigContext/enviveConfigContext.tsx +5 -0
- package/src/contexts/enviveContext/enviveContext.tsx +10 -0
- package/src/contexts/featureFlagServiceContext/featureFlagServiceContext.tsx +6 -173
- package/src/contexts/localStorageContext/localStorageContext.tsx +2 -5
- package/src/contexts/localStorageContext/localStorageKeys.ts +8 -0
- package/src/contexts/pageContext/pageContext.tsx +1 -1
- package/src/contexts/salesAgentContext/__tests__/chatAPI.test.tsx +63 -0
- package/src/contexts/salesAgentContext/chatAPI.ts +20 -1
- package/src/contexts/salesAgentContext/salesAgentContext.tsx +17 -0
- package/src/contexts/salesAgentContext/salesAgentService.ts +12 -3
- package/src/contexts/types.ts +2 -0
- package/src/contexts/typesV3.ts +14 -0
- package/src/hooks/AddToCart/__tests__/parseAddToCartFunction.test.ts +31 -0
- package/src/hooks/AddToCart/__tests__/useAddToCart.test.tsx +204 -0
- package/src/hooks/AddToCart/addToCartErrorMessage.ts +2 -0
- package/src/hooks/AddToCart/createAddToCartUserEvent.ts +25 -0
- package/src/hooks/AddToCart/index.ts +4 -0
- package/src/hooks/AddToCart/parseAddToCartFunction.ts +31 -0
- package/src/hooks/AddToCart/useAddToCart.ts +35 -0
- package/src/hooks/PageViewedEvent/__tests__/usePageViewedEvent.test.ts +4 -4
- package/src/hooks/PageViewedEvent/usePageViewedEvent.ts +5 -1
- package/src/hooks/ProductDetails/__tests__/useProductDetails.test.tsx +129 -0
- package/src/hooks/ProductDetails/__tests__/variantSelection.test.ts +145 -0
- package/src/hooks/ProductDetails/index.ts +2 -0
- package/src/hooks/ProductDetails/useProductDetails.ts +62 -0
- package/src/hooks/ProductDetails/variantSelection.ts +61 -0
- package/src/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.ts +15 -0
- package/src/hooks/WidgetInteraction/types.ts +43 -2
- package/src/hooks/WidgetInteraction/utils.ts +1 -1
- package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +8 -8
- package/src/services/amplitudeService/amplitudeService.ts +43 -23
- package/src/services/enviveConfigService/__tests__/enviveConfigService.test.ts +208 -0
- package/src/services/enviveConfigService/enviveConfigService.ts +62 -2
- package/src/services/enviveConfigService/fetchGraphQLConfig.ts +17 -6
- package/src/services/featureFlagService/index.ts +176 -0
- package/src/services/ga4ProjectionService/__tests__/ga4ProjectionService.test.ts +36 -0
- package/src/services/ga4ProjectionService/ga4EventSchema.ts +7 -0
- package/src/services/hardcopyService/__tests__/hardcopyService.test.ts +47 -1
- package/src/services/hardcopyService/hardcopyService.ts +5 -0
- package/src/services/urlResolverWarmupService/__tests__/urlResolverWarmupService.test.ts +85 -0
- package/src/services/urlResolverWarmupService/index.ts +89 -0
- package/src/types/enviveConfig.ts +3 -0
|
@@ -1,178 +1,11 @@
|
|
|
1
1
|
import React, { ReactNode, createContext, useContext, useMemo } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { FeatureGates } from 'src/application/models/featureGates';
|
|
4
|
-
import Logger from 'src/application/logging/logger';
|
|
2
|
+
import { FeatureFlagService, FullFlagValue } from 'src/services/featureFlagService';
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export
|
|
11
|
-
resolved: boolean;
|
|
12
|
-
initial: boolean | undefined;
|
|
13
|
-
query_param_override: boolean | undefined;
|
|
14
|
-
window_param_override: boolean | undefined;
|
|
15
|
-
stored_override: boolean | undefined;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// This is the class that was previously implicitly used or defined elsewhere
|
|
19
|
-
export class FeatureFlagService {
|
|
20
|
-
private featureGates: OrgConfigFeatureGate[];
|
|
21
|
-
|
|
22
|
-
constructor(featureGates: OrgConfigFeatureGate[]) {
|
|
23
|
-
this.featureGates = featureGates;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
static getQueryParamFeatureGateOverrides(featureGate: FeatureGates): boolean | undefined {
|
|
27
|
-
const url = window.location?.href ?? '';
|
|
28
|
-
if (url.includes('?')) {
|
|
29
|
-
const urlObj = new URL(url);
|
|
30
|
-
const params = new URLSearchParams(urlObj.search);
|
|
31
|
-
const paramsObj = Object.fromEntries(params.entries());
|
|
32
|
-
const value = paramsObj[featureGate];
|
|
33
|
-
|
|
34
|
-
if (value != null) {
|
|
35
|
-
return value === 'true';
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
static getWindowFeatureGateOverrides(featureGate: FeatureGates): boolean | undefined {
|
|
43
|
-
if (typeof window === 'undefined') {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
const windowOverride = window as Window & {
|
|
47
|
-
_envive?: { featureOverrides?: Record<string, boolean> };
|
|
48
|
-
_spiffy?: { featureOverrides?: Record<string, boolean> };
|
|
49
|
-
};
|
|
50
|
-
const featureOverrides =
|
|
51
|
-
windowOverride?._envive?.featureOverrides ?? windowOverride?._spiffy?.featureOverrides;
|
|
52
|
-
const value = featureOverrides?.[String(featureGate)];
|
|
53
|
-
if (value != null) {
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
static getStoredFeatureGateOverrides(featureGate: FeatureGates): boolean | undefined {
|
|
60
|
-
if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
try {
|
|
64
|
-
const featureFlags = window.localStorage.getItem(FEATURE_FLAGS_STORAGE_KEY);
|
|
65
|
-
if (!featureFlags) {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
const featureFlagsObj = JSON.parse(featureFlags) as Record<string, boolean>;
|
|
69
|
-
return featureFlagsObj[featureGate];
|
|
70
|
-
} catch (err) {
|
|
71
|
-
logger.logError('getStoredFeatureGateOverrides: error parsing feature flags', err);
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
static getFeatureFlagOverride(featureGate: FeatureGates): boolean | undefined {
|
|
77
|
-
const queryOverride = FeatureFlagService.getQueryParamFeatureGateOverrides(featureGate);
|
|
78
|
-
if (queryOverride != null) {
|
|
79
|
-
logger.logDebug(
|
|
80
|
-
`isFeatureGateEnabled featureGate:${featureGate} overridden by URL param. New value: ${queryOverride}`,
|
|
81
|
-
);
|
|
82
|
-
return queryOverride;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const windowOverride = FeatureFlagService.getWindowFeatureGateOverrides(featureGate);
|
|
86
|
-
if (windowOverride != null) {
|
|
87
|
-
logger.logDebug(
|
|
88
|
-
`isFeatureGateEnabled featureGate:${featureGate} overridden by window param. New value: ${windowOverride}`,
|
|
89
|
-
);
|
|
90
|
-
return windowOverride;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const storedOverride = FeatureFlagService.getStoredFeatureGateOverrides(featureGate);
|
|
94
|
-
if (storedOverride != null) {
|
|
95
|
-
logger.logDebug(
|
|
96
|
-
`isFeatureGateEnabled featureGate:${featureGate} overridden by stored value. New value: ${storedOverride}`,
|
|
97
|
-
);
|
|
98
|
-
return storedOverride;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return undefined;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
isFeatureGateEnabled = (featureGate: FeatureGates): boolean => {
|
|
105
|
-
const gateValue = this.featureGates.find(gate => gate.name === featureGate);
|
|
106
|
-
|
|
107
|
-
const featureFlagOverride = FeatureFlagService.getFeatureFlagOverride(featureGate);
|
|
108
|
-
if (featureFlagOverride !== undefined) {
|
|
109
|
-
return featureFlagOverride;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (gateValue == null || gateValue.value == null) {
|
|
113
|
-
logger.logDebug(
|
|
114
|
-
`isFeatureGateEnabled featureGate:${featureGate} value is undefined - returning false`,
|
|
115
|
-
);
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
return gateValue.value;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// Ensure that standard overrides work as expected
|
|
122
|
-
isClientSessionEnabled = (): boolean =>
|
|
123
|
-
this.isFeatureGateEnabled(FeatureGates.IsClientSessionEnabled);
|
|
124
|
-
|
|
125
|
-
getFeatureFlags = (): Record<string, boolean> => {
|
|
126
|
-
return Object.fromEntries(
|
|
127
|
-
Object.values(FeatureGates).map((featureGate: FeatureGates) => [
|
|
128
|
-
featureGate,
|
|
129
|
-
this.isFeatureGateEnabled(featureGate),
|
|
130
|
-
]),
|
|
131
|
-
);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
getFullFlagValue = (featureGate: FeatureGates): FullFlagValue => {
|
|
135
|
-
const gateValue = this.featureGates.find(gate => gate.name === featureGate);
|
|
136
|
-
return {
|
|
137
|
-
resolved: this.isFeatureGateEnabled(featureGate),
|
|
138
|
-
initial: gateValue?.value,
|
|
139
|
-
query_param_override: FeatureFlagService.getQueryParamFeatureGateOverrides(featureGate),
|
|
140
|
-
window_param_override: FeatureFlagService.getWindowFeatureGateOverrides(featureGate),
|
|
141
|
-
stored_override: FeatureFlagService.getStoredFeatureGateOverrides(featureGate),
|
|
142
|
-
};
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
getFullFlagValues = (): Record<string, FullFlagValue> => {
|
|
146
|
-
const featureGates = Object.values(FeatureGates).map((featureGate: FeatureGates) => [
|
|
147
|
-
featureGate,
|
|
148
|
-
this.getFullFlagValue(featureGate),
|
|
149
|
-
]);
|
|
150
|
-
|
|
151
|
-
return featureGates.length > 0
|
|
152
|
-
? featureGates.reduce<Record<string, FullFlagValue>>((acc, entry) => {
|
|
153
|
-
const [name, value] = entry as [FeatureGates, FullFlagValue];
|
|
154
|
-
if (name && value != null) {
|
|
155
|
-
return { ...acc, [`feature_gate.${name}`]: value };
|
|
156
|
-
}
|
|
157
|
-
return acc;
|
|
158
|
-
}, {})
|
|
159
|
-
: {};
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
static persistFeatureGateOverrides(): void {
|
|
163
|
-
if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
const overrides = Object.values(FeatureGates)
|
|
167
|
-
.map(
|
|
168
|
-
featureGate =>
|
|
169
|
-
[featureGate, FeatureFlagService.getFeatureFlagOverride(featureGate)] as const,
|
|
170
|
-
)
|
|
171
|
-
.filter(([, value]) => value !== undefined)
|
|
172
|
-
.reduce<Record<string, boolean>>((acc, [key, value]) => ({ ...acc, [key]: value! }), {});
|
|
173
|
-
window.localStorage.setItem(FEATURE_FLAGS_STORAGE_KEY, JSON.stringify(overrides));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
4
|
+
// The service class lives in src/services/featureFlagService (a React-free
|
|
5
|
+
// module the injection bundle can import pre-config); re-export it here so
|
|
6
|
+
// existing imports keep working.
|
|
7
|
+
export { FeatureFlagService };
|
|
8
|
+
export type { FullFlagValue };
|
|
176
9
|
|
|
177
10
|
export interface FeatureFlagContextType {
|
|
178
11
|
featureFlagService: FeatureFlagService | undefined;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import React, { createContext, useCallback, useContext, useEffect, useMemo } from 'react';
|
|
2
2
|
import Logger from 'src/application/logging/logger';
|
|
3
3
|
import { LocalStorageEventListener } from 'src/application/models/localStorageEventListener';
|
|
4
|
+
import { LocalStorageKeys } from './localStorageKeys';
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
ChatId = 'v1-spiffy-chat-session-id',
|
|
7
|
-
SpiffyOnOverride = 'spiffy_on',
|
|
8
|
-
EnviveOnOverride = 'envive_on',
|
|
9
|
-
}
|
|
6
|
+
export { LocalStorageKeys };
|
|
10
7
|
|
|
11
8
|
interface LocalStorageContextType {
|
|
12
9
|
setItem: (key: string, value: string) => void;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Kept React-free: pre-config injection-bundle code (atoms/envive) needs
|
|
2
|
+
// these keys, and importing them through localStorageContext.tsx would pull
|
|
3
|
+
// React into the eager critical chunk.
|
|
4
|
+
export enum LocalStorageKeys {
|
|
5
|
+
ChatId = 'v1-spiffy-chat-session-id',
|
|
6
|
+
SpiffyOnOverride = 'spiffy_on',
|
|
7
|
+
EnviveOnOverride = 'envive_on',
|
|
8
|
+
}
|
|
@@ -176,7 +176,7 @@ export const PageProvider: React.FC<{
|
|
|
176
176
|
pageId = variantInfo.url;
|
|
177
177
|
break;
|
|
178
178
|
case VariantTypeEnum.FullPageSalesAgent:
|
|
179
|
-
pageType = '
|
|
179
|
+
pageType = 'adaptive_storefront';
|
|
180
180
|
pageId = variantInfo.url;
|
|
181
181
|
break;
|
|
182
182
|
default:
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import { Provider, createStore } from 'jotai';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Message, MessageRole, MessageType } from 'src/application/models/message';
|
|
5
|
+
import { messagesAtom } from 'src/atoms/chat/chatState';
|
|
6
|
+
import { useSalesAgentChatAPI } from '../chatAPI';
|
|
7
|
+
|
|
8
|
+
const mockTrackEvent = vi.fn();
|
|
9
|
+
vi.mock('../../amplitudeContext', async () => {
|
|
10
|
+
const actual = await vi.importActual('../../amplitudeContext');
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
useAmplitude: () => ({ trackEvent: mockTrackEvent }),
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
vi.mock('src/hooks/WidgetInteraction', () => ({
|
|
18
|
+
useWidgetInteraction: () => ({ trackWidgetInteraction: vi.fn() }),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const asTextMessage = (message: Message) => message as Extract<Message, { type: MessageType.Text }>;
|
|
22
|
+
|
|
23
|
+
const renderChatAPI = (store: ReturnType<typeof createStore>) => {
|
|
24
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
25
|
+
// The jotai Provider is typed against the root @types/react (18) while this
|
|
26
|
+
// package resolves @types/react 19; the cast bridges the two ReactNode types.
|
|
27
|
+
<Provider store={store}>{children as React.ReactElement<unknown, string>}</Provider>
|
|
28
|
+
);
|
|
29
|
+
return renderHook(() => useSalesAgentChatAPI(), { wrapper });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
describe('useSalesAgentChatAPI.injectAssistantMessage', () => {
|
|
33
|
+
it('appends an assistant text message turn onto messagesAtom', () => {
|
|
34
|
+
const store = createStore();
|
|
35
|
+
const { result } = renderChatAPI(store);
|
|
36
|
+
|
|
37
|
+
result.current.injectAssistantMessage("I'm sorry, something went wrong.");
|
|
38
|
+
|
|
39
|
+
const messages = store.get(messagesAtom);
|
|
40
|
+
expect(messages).toHaveLength(1);
|
|
41
|
+
expect(messages[0]).toHaveLength(1);
|
|
42
|
+
expect(messages[0][0]).toMatchObject({
|
|
43
|
+
role: MessageRole.Assistant,
|
|
44
|
+
type: MessageType.Text,
|
|
45
|
+
metadata: { content: "I'm sorry, something went wrong." },
|
|
46
|
+
});
|
|
47
|
+
expect(messages[0][0].id).toEqual(expect.any(String));
|
|
48
|
+
expect(messages[0][0].createdAt).toEqual(expect.any(String));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('appends subsequent messages as additional turns without clobbering prior ones', () => {
|
|
52
|
+
const store = createStore();
|
|
53
|
+
const { result } = renderChatAPI(store);
|
|
54
|
+
|
|
55
|
+
result.current.injectAssistantMessage('first');
|
|
56
|
+
result.current.injectAssistantMessage('second');
|
|
57
|
+
|
|
58
|
+
const messages = store.get(messagesAtom);
|
|
59
|
+
expect(messages).toHaveLength(2);
|
|
60
|
+
expect(asTextMessage(messages[0][0]).metadata).toMatchObject({ content: 'first' });
|
|
61
|
+
expect(asTextMessage(messages[1][0]).metadata).toMatchObject({ content: 'second' });
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -6,6 +6,7 @@ import Logger from 'src/application/logging/logger';
|
|
|
6
6
|
import {
|
|
7
7
|
ChatElementDisplayLocationV3,
|
|
8
8
|
FormSubmittedAttributes,
|
|
9
|
+
Message,
|
|
9
10
|
MessageRole,
|
|
10
11
|
MessageType,
|
|
11
12
|
Suggestion,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
import { hasParsedVariantInfoAtom } from 'src/atoms/app';
|
|
15
16
|
import { analyticsContextAtom } from 'src/atoms/app/variant';
|
|
16
17
|
import { formSubmitAtom, replyEventCategoryAtom } from 'src/atoms/chat';
|
|
17
|
-
import { suggestionsAtom } from 'src/atoms/chat/chatState';
|
|
18
|
+
import { messagesAtom, suggestionsAtom } from 'src/atoms/chat/chatState';
|
|
18
19
|
import { queueUserEventAtom } from 'src/atoms/chat/messageQueue';
|
|
19
20
|
import { useWidgetInteraction } from 'src/hooks/WidgetInteraction';
|
|
20
21
|
import {
|
|
@@ -49,6 +50,8 @@ export interface SalesAgentChatAPI {
|
|
|
49
50
|
displayLocation: ChatElementDisplayLocationV3;
|
|
50
51
|
}) => void;
|
|
51
52
|
onFormResponseSubmitted: (form: FormSubmittedAttributes) => void;
|
|
53
|
+
/** Injects a synthetic assistant text message into the chat transcript, e.g. to surface a failure. */
|
|
54
|
+
injectAssistantMessage: (content: string) => void;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export const useSalesAgentChatAPI = (widget?: WidgetInteractionComponent) => {
|
|
@@ -60,6 +63,7 @@ export const useSalesAgentChatAPI = (widget?: WidgetInteractionComponent) => {
|
|
|
60
63
|
const setSuggestions = useSetAtom(suggestionsAtom);
|
|
61
64
|
const setReplyEventCategory = useSetAtom(replyEventCategoryAtom);
|
|
62
65
|
const setFormSubmit = useSetAtom(formSubmitAtom);
|
|
66
|
+
const setMessages = useSetAtom(messagesAtom);
|
|
63
67
|
const { trackEvent } = useAmplitude();
|
|
64
68
|
const { trackWidgetInteraction } = useWidgetInteraction();
|
|
65
69
|
const contextWidget = widget || WidgetInteractionComponent.FLOATING_CHAT;
|
|
@@ -260,11 +264,26 @@ export const useSalesAgentChatAPI = (widget?: WidgetInteractionComponent) => {
|
|
|
260
264
|
[setReplyEventCategory, setFormSubmit, trackEvent, context, queueUserEvent],
|
|
261
265
|
);
|
|
262
266
|
|
|
267
|
+
const injectAssistantMessage = useCallback(
|
|
268
|
+
(content: string) => {
|
|
269
|
+
const message: Message = {
|
|
270
|
+
id: uuid(),
|
|
271
|
+
role: MessageRole.Assistant,
|
|
272
|
+
type: MessageType.Text,
|
|
273
|
+
createdAt: new Date().toISOString(),
|
|
274
|
+
metadata: { content },
|
|
275
|
+
};
|
|
276
|
+
setMessages(current => [...current, [message]]);
|
|
277
|
+
},
|
|
278
|
+
[setMessages],
|
|
279
|
+
);
|
|
280
|
+
|
|
263
281
|
return {
|
|
264
282
|
logPageVisit,
|
|
265
283
|
logUserEvent,
|
|
266
284
|
onSuggestionClicked,
|
|
267
285
|
onTypedMessageSubmitted,
|
|
268
286
|
onFormResponseSubmitted,
|
|
287
|
+
injectAssistantMessage,
|
|
269
288
|
};
|
|
270
289
|
};
|
|
@@ -4,12 +4,14 @@ import { ReactNode, createContext, useCallback, useEffect, useMemo, useRef, useS
|
|
|
4
4
|
import Logger from 'src/application/logging/logger';
|
|
5
5
|
import { NextMessageRequest, Suggestion, UserEvent } from 'src/application/models';
|
|
6
6
|
import { Message, MessageRole, MessageType } from 'src/application/models/message';
|
|
7
|
+
import { messageFromAddToCartEvent } from 'src/application/utils';
|
|
7
8
|
import {
|
|
8
9
|
initializedAtom,
|
|
9
10
|
messagesAtom,
|
|
10
11
|
pendingResponseAtom,
|
|
11
12
|
responseStreamingAtom,
|
|
12
13
|
suggestionsAtom,
|
|
14
|
+
suggestionsLoadingAtom,
|
|
13
15
|
} from 'src/atoms/chat/chatState';
|
|
14
16
|
import {
|
|
15
17
|
processUserEventAtom,
|
|
@@ -36,6 +38,9 @@ export interface SalesAgent extends SalesAgentChatAPI {
|
|
|
36
38
|
isResponseStreaming: boolean;
|
|
37
39
|
// True while the response is pending from the backend
|
|
38
40
|
isPendingResponse: boolean;
|
|
41
|
+
// True while suggestions for the most recent message are being fetched (separate from the
|
|
42
|
+
// message response itself — suggestions are requested as a follow-up call once it completes)
|
|
43
|
+
isLoadingSuggestions: boolean;
|
|
39
44
|
// True when the sales agent is initialized
|
|
40
45
|
isInitialized: boolean;
|
|
41
46
|
}
|
|
@@ -149,6 +154,12 @@ export const SalesAgentProvider: React.FC<SalesAgentProviderProps> = ({
|
|
|
149
154
|
};
|
|
150
155
|
setMessages(current => [...current, [newMessage]]);
|
|
151
156
|
}
|
|
157
|
+
if (userEvent && userEvent.category === UserEventCategory.AddToCart) {
|
|
158
|
+
const newMessage = messageFromAddToCartEvent(userEvent);
|
|
159
|
+
if (newMessage) {
|
|
160
|
+
setMessages(current => [...current, [newMessage]]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
152
163
|
if (userEvent && userEvent.category === UserEventCategory.FormSubmitted) {
|
|
153
164
|
const { formResponseId, filledSchema } = userEvent.attributes;
|
|
154
165
|
setMessages(prevMessagesState => {
|
|
@@ -227,6 +238,7 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
227
238
|
const pendingMessages = useAtomValue(userEventQueueAtom);
|
|
228
239
|
const isResponseStreaming = useAtomValue(responseStreamingAtom);
|
|
229
240
|
const isPendingResponse = useAtomValue(pendingResponseAtom);
|
|
241
|
+
const isLoadingSuggestions = useAtomValue(suggestionsLoadingAtom);
|
|
230
242
|
const isInitialized = useAtomValue(initializedAtom);
|
|
231
243
|
const {
|
|
232
244
|
logPageVisit,
|
|
@@ -234,6 +246,7 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
234
246
|
onSuggestionClicked,
|
|
235
247
|
onTypedMessageSubmitted,
|
|
236
248
|
onFormResponseSubmitted,
|
|
249
|
+
injectAssistantMessage,
|
|
237
250
|
} = useSalesAgentChatAPI(contextWidget);
|
|
238
251
|
|
|
239
252
|
const salesAgent: SalesAgent = useMemo(() => {
|
|
@@ -244,11 +257,13 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
244
257
|
isInitialized,
|
|
245
258
|
isResponseStreaming,
|
|
246
259
|
isPendingResponse,
|
|
260
|
+
isLoadingSuggestions,
|
|
247
261
|
logPageVisit,
|
|
248
262
|
logUserEvent,
|
|
249
263
|
onSuggestionClicked,
|
|
250
264
|
onTypedMessageSubmitted,
|
|
251
265
|
onFormResponseSubmitted,
|
|
266
|
+
injectAssistantMessage,
|
|
252
267
|
};
|
|
253
268
|
}, [
|
|
254
269
|
messages,
|
|
@@ -257,11 +272,13 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
257
272
|
isInitialized,
|
|
258
273
|
isResponseStreaming,
|
|
259
274
|
isPendingResponse,
|
|
275
|
+
isLoadingSuggestions,
|
|
260
276
|
logPageVisit,
|
|
261
277
|
logUserEvent,
|
|
262
278
|
onSuggestionClicked,
|
|
263
279
|
onTypedMessageSubmitted,
|
|
264
280
|
onFormResponseSubmitted,
|
|
281
|
+
injectAssistantMessage,
|
|
265
282
|
]);
|
|
266
283
|
return salesAgent;
|
|
267
284
|
};
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
requestFailureAtom,
|
|
23
23
|
responseStreamingAtom,
|
|
24
24
|
suggestionsAtom,
|
|
25
|
+
suggestionsLoadingAtom,
|
|
25
26
|
} from 'src/atoms/chat/chatState';
|
|
26
27
|
import { useAmplitude } from 'src/contexts/amplitudeContext';
|
|
27
28
|
import { useFeatureFlagService } from 'src/contexts/featureFlagServiceContext';
|
|
@@ -242,6 +243,7 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
242
243
|
const setSuggestions = useSetAtom(suggestionsAtom);
|
|
243
244
|
const setPendingResponse = useSetAtom(pendingResponseAtom);
|
|
244
245
|
const setResponseStreaming = useSetAtom(responseStreamingAtom);
|
|
246
|
+
const setSuggestionsLoading = useSetAtom(suggestionsLoadingAtom);
|
|
245
247
|
const analyticsContext = useAtomValue(analyticsContextAtom);
|
|
246
248
|
const messageInterceptor = useMessageInterceptor();
|
|
247
249
|
const { trackEvent } = useAmplitude();
|
|
@@ -285,6 +287,7 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
285
287
|
let successfulResponse: boolean | undefined;
|
|
286
288
|
setPendingResponse(true);
|
|
287
289
|
setSuggestions([]);
|
|
290
|
+
setSuggestionsLoading(true);
|
|
288
291
|
const stream = CommerceApiClient.getNextResponseStreaming(payload);
|
|
289
292
|
let responseAnalytics: Record<string, unknown> | undefined;
|
|
290
293
|
try {
|
|
@@ -360,6 +363,7 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
360
363
|
// logPerfMetric,
|
|
361
364
|
setRequestFailure,
|
|
362
365
|
setSuggestions,
|
|
366
|
+
setSuggestionsLoading,
|
|
363
367
|
messageInterceptor,
|
|
364
368
|
setMessages,
|
|
365
369
|
setPendingResponse,
|
|
@@ -368,10 +372,15 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
368
372
|
|
|
369
373
|
const getSuggestions = useCallback(
|
|
370
374
|
async (payload: NextMessageRequest): Promise<void> => {
|
|
371
|
-
|
|
372
|
-
|
|
375
|
+
setSuggestionsLoading(true);
|
|
376
|
+
try {
|
|
377
|
+
const newSuggestions = await CommerceApiClient.getNextSuggestions(payload);
|
|
378
|
+
setSuggestions(newSuggestions);
|
|
379
|
+
} finally {
|
|
380
|
+
setSuggestionsLoading(false);
|
|
381
|
+
}
|
|
373
382
|
},
|
|
374
|
-
[setSuggestions],
|
|
383
|
+
[setSuggestions, setSuggestionsLoading],
|
|
375
384
|
);
|
|
376
385
|
|
|
377
386
|
const hydrateMessages = useCallback(async () => {
|
package/src/contexts/types.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { ChatState } from 'src/types/custservice-types';
|
|
|
13
13
|
import { SuggestionBarLocationForMetrics } from 'src/types/suggestionBarV2-types';
|
|
14
14
|
import { TestProps } from 'src/types/test-types';
|
|
15
15
|
import { OrgConfigFeatureGate } from 'src/application/models/api/orgConfigResults';
|
|
16
|
+
import type { AddToCartCallback } from 'src/types/enviveConfig';
|
|
16
17
|
import { WidgetConfigV3, WidgetTypeV3 } from './typesV3';
|
|
17
18
|
import type { HardcopyResponse } from './hardcopyContext/hardcopyContext';
|
|
18
19
|
|
|
@@ -939,6 +940,7 @@ export interface EnviveConfig {
|
|
|
939
940
|
publicKey?: string;
|
|
940
941
|
featureGates?: OrgConfigFeatureGate[];
|
|
941
942
|
hardcopyOverride?: Partial<Record<WidgetTypeV3, HardcopyResponse>>;
|
|
943
|
+
addToCart?: AddToCartCallback;
|
|
942
944
|
}
|
|
943
945
|
|
|
944
946
|
export type {
|
package/src/contexts/typesV3.ts
CHANGED
|
@@ -111,6 +111,17 @@ export const enum ButtonColorsVars {
|
|
|
111
111
|
|
|
112
112
|
type ImageAspectRatio = '1/1' | '3/4';
|
|
113
113
|
|
|
114
|
+
type AddToCartConfig = {
|
|
115
|
+
enabled: boolean;
|
|
116
|
+
// Colors are CSS variable strings, e.g. 'var(--envive-colors-background-dark)'
|
|
117
|
+
backgroundColor?: string;
|
|
118
|
+
textColor?: string;
|
|
119
|
+
ctaText?: string;
|
|
120
|
+
// Merchant-supplied JS function, as a string, used to invoke their cart.
|
|
121
|
+
// Ignored when window._spiffy/_envive.addToCart is set - the window variable always takes precedence.
|
|
122
|
+
addToCartFunction?: string;
|
|
123
|
+
};
|
|
124
|
+
|
|
114
125
|
type ButtonColors = {
|
|
115
126
|
lightButtonBackgroundColor: string;
|
|
116
127
|
lightButtonBorderColor?: string;
|
|
@@ -193,6 +204,7 @@ type LookAndFeelConfig = {
|
|
|
193
204
|
imageAspectRatio: ImageAspectRatio;
|
|
194
205
|
buttonColors: ButtonColors;
|
|
195
206
|
typography: Typography;
|
|
207
|
+
addToCart?: AddToCartConfig;
|
|
196
208
|
};
|
|
197
209
|
|
|
198
210
|
export enum ConfigVersionEnum {
|
|
@@ -385,6 +397,7 @@ interface FullPageSalesAgentWidgetV3Config extends BaseWidgetConfig<WidgetTypeV3
|
|
|
385
397
|
mobileBackgroundImage?: string;
|
|
386
398
|
desktopBackgroundImage?: string;
|
|
387
399
|
backgroundColor?: string;
|
|
400
|
+
chatBackgroundColor?: string;
|
|
388
401
|
suggestionButtonType?: PromptButtonVariant;
|
|
389
402
|
}
|
|
390
403
|
|
|
@@ -402,6 +415,7 @@ type WidgetConfigV3 =
|
|
|
402
415
|
| FullPageSalesAgentWidgetV3Config;
|
|
403
416
|
|
|
404
417
|
export type {
|
|
418
|
+
AddToCartConfig,
|
|
405
419
|
ChatPreviewComparisonWidgetV3Config,
|
|
406
420
|
ChatPreviewLoadingWidgetV3Config,
|
|
407
421
|
ChatPreviewWidgetV3Config,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { parseAddToCartFunction } from '../parseAddToCartFunction';
|
|
2
|
+
|
|
3
|
+
describe('parseAddToCartFunction', () => {
|
|
4
|
+
it('returns undefined when no string is provided', () => {
|
|
5
|
+
expect(parseAddToCartFunction(undefined)).toBeUndefined();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('parses a function-expression string into a callable', async () => {
|
|
9
|
+
const fn = parseAddToCartFunction('function(params) { globalThis.__testCalls = [params]; }');
|
|
10
|
+
|
|
11
|
+
expect(fn).toBeInstanceOf(Function);
|
|
12
|
+
fn?.({ product_id: 'abc' });
|
|
13
|
+
expect((globalThis as Record<string, unknown>).__testCalls).toEqual([{ product_id: 'abc' }]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('parses an arrow-function string into a callable', () => {
|
|
17
|
+
const fn = parseAddToCartFunction('(params) => { globalThis.__testCalls = [params]; }');
|
|
18
|
+
|
|
19
|
+
expect(fn).toBeInstanceOf(Function);
|
|
20
|
+
fn?.({ product_id: 'xyz' });
|
|
21
|
+
expect((globalThis as Record<string, unknown>).__testCalls).toEqual([{ product_id: 'xyz' }]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns undefined when the string has a syntax error', () => {
|
|
25
|
+
expect(parseAddToCartFunction('function( { broken')).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns undefined when the string evaluates to a non-function value', () => {
|
|
29
|
+
expect(parseAddToCartFunction('42')).toBeUndefined();
|
|
30
|
+
});
|
|
31
|
+
});
|