@envive-ai/react-hooks 0.3.46 → 0.3.48
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/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/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/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/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.d.cts +6 -6
- package/dist/atoms/app/variant.d.ts +6 -6
- 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 +3 -3
- package/dist/atoms/chat/index.d.cts +2 -2
- package/dist/atoms/chat/index.d.ts +3 -3
- 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 +7 -7
- 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 +3 -3
- package/dist/atoms/chat/replies.d.cts +1 -1
- package/dist/atoms/chat/replies.d.ts +3 -3
- package/dist/atoms/chat/suggestions.d.cts +2 -2
- package/dist/atoms/chat/suggestions.d.ts +3 -3
- 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/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.d.cts +17 -17
- package/dist/atoms/search/chatSearch.d.ts +17 -17
- package/dist/atoms/search/searchAPI.d.cts +13 -13
- package/dist/atoms/search/searchAPI.d.ts +13 -13
- package/dist/atoms/search/types.d.ts +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/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/salesAgentContext/chatAPI.cjs +13 -2
- package/dist/contexts/salesAgentContext/chatAPI.d.cts +4 -1
- package/dist/contexts/salesAgentContext/chatAPI.d.ts +4 -1
- package/dist/contexts/salesAgentContext/chatAPI.js +14 -3
- package/dist/contexts/salesAgentContext/salesAgentContext.cjs +12 -4
- package/dist/contexts/salesAgentContext/salesAgentContext.js +12 -4
- package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +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/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/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
- package/dist/hooks/LocalStorageOperations/useLocalStorageOperations.cjs +6 -5
- package/dist/hooks/LocalStorageOperations/useLocalStorageOperations.js +3 -2
- package/dist/hooks/PageViewedEvent/usePageViewedEvent.cjs +5 -2
- package/dist/hooks/PageViewedEvent/usePageViewedEvent.js +5 -2
- 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.d.cts +1 -1
- package/dist/hooks/Search/useSearch.d.ts +1 -1
- 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 +12 -2
- package/dist/hooks/WidgetInteraction/types.d.cts +28 -3
- package/dist/hooks/WidgetInteraction/types.d.ts +28 -3
- package/dist/hooks/WidgetInteraction/types.js +12 -2
- package/dist/hooks/utils.d.cts +1 -1
- package/dist/hooks/utils.d.ts +1 -1
- package/dist/services/amplitudeService/amplitudeService.cjs +16 -10
- package/dist/services/amplitudeService/amplitudeService.d.cts +5 -2
- package/dist/services/amplitudeService/amplitudeService.d.ts +5 -2
- package/dist/services/amplitudeService/amplitudeService.js +14 -7
- package/dist/services/enviveConfigService/enviveConfigService.cjs +3 -4
- package/dist/services/enviveConfigService/enviveConfigService.d.cts +1 -1
- package/dist/services/enviveConfigService/enviveConfigService.d.ts +1 -1
- package/dist/services/enviveConfigService/enviveConfigService.js +2 -3
- package/dist/services/enviveConfigService/fetchGraphQLConfig.cjs +8 -7
- package/dist/services/enviveConfigService/fetchGraphQLConfig.js +8 -6
- 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.d.cts +1 -1
- package/dist/services/hardcopyService/hardcopyService.d.ts +1 -1
- 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 +17 -1
- 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/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/atoms/app/index.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/salesAgentContext/__tests__/chatAPI.test.tsx +63 -0
- package/src/contexts/salesAgentContext/chatAPI.ts +20 -1
- package/src/contexts/salesAgentContext/salesAgentContext.tsx +10 -0
- 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/usePageViewedEvent.ts +4 -0
- 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 +42 -1
- package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +8 -8
- package/src/services/amplitudeService/amplitudeService.ts +42 -23
- package/src/services/enviveConfigService/enviveConfigService.ts +2 -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/urlResolverWarmupService/__tests__/urlResolverWarmupService.test.ts +85 -0
- package/src/services/urlResolverWarmupService/index.ts +89 -0
- package/src/types/enviveConfig.ts +3 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
|
|
2
|
+
import { Message, MessageRole, MessageType, UserEvent } from 'src/application/models';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transforms an AddToCart UserEvent object into a Message object for presentation.
|
|
6
|
+
*
|
|
7
|
+
* @param event The user event object received from the server
|
|
8
|
+
* @param productTitle Resolved product title to display instead of the raw
|
|
9
|
+
* productId. Falls back to `event.attributes.productTitle` (set on freshly
|
|
10
|
+
* created, not-yet-serialized events) and finally to the productId itself.
|
|
11
|
+
*
|
|
12
|
+
* @returns A Message if the event is an AddToCart event, otherwise undefined
|
|
13
|
+
*/
|
|
14
|
+
export const messageFromAddToCartEvent = (
|
|
15
|
+
event: UserEvent,
|
|
16
|
+
productTitle?: string,
|
|
17
|
+
): Message | undefined => {
|
|
18
|
+
if (event.category === UserEventCategory.AddToCart) {
|
|
19
|
+
const label = productTitle ?? event.attributes.productTitle ?? event.attributes.productId;
|
|
20
|
+
return {
|
|
21
|
+
id: event.eventId,
|
|
22
|
+
role: MessageRole.User,
|
|
23
|
+
type: MessageType.QueryTyped,
|
|
24
|
+
createdAt: event.createdAt,
|
|
25
|
+
metadata: {
|
|
26
|
+
content: `Add ${label} to my cart`,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return undefined;
|
|
32
|
+
};
|
package/src/atoms/app/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ContextEnvEnum, ContextSourceEnum } from '@spiffy-ai/commerce-api-clien
|
|
|
2
2
|
import { atom } from 'jotai';
|
|
3
3
|
import { atomWithStorage } from 'jotai/utils';
|
|
4
4
|
import { VariantInfo } from 'src/application/models';
|
|
5
|
-
import { UserIdentityContextType } from 'src/contexts/userIdentityContext/userIdentityContext';
|
|
5
|
+
import type { UserIdentityContextType } from 'src/contexts/userIdentityContext/userIdentityContext';
|
|
6
6
|
import { v4 as uuid } from 'uuid';
|
|
7
7
|
import { contextSourceAtom, envAtom, orgIdAtom, orgShortNameAtom } from '../envive/enviveConfig';
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import Logger from 'src/application/logging/logger';
|
|
|
2
2
|
import { atom } from 'jotai';
|
|
3
3
|
import { EnviveConfig } from 'src/contexts/types';
|
|
4
4
|
import { ContextSourceEnum } from '@spiffy-ai/commerce-api-client';
|
|
5
|
-
import { LocalStorageKeys } from 'src/contexts/localStorageContext';
|
|
5
|
+
import { LocalStorageKeys } from 'src/contexts/localStorageContext/localStorageKeys';
|
|
6
6
|
|
|
7
7
|
const logger = new Logger('enviveConfig');
|
|
8
8
|
|
|
@@ -49,6 +49,11 @@ export const orgIdAtom = atom(get => {
|
|
|
49
49
|
return config?.orgId;
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
export const addToCartCallbackAtom = atom(get => {
|
|
53
|
+
const config = get(enviveConfigAtom);
|
|
54
|
+
return config?.addToCart;
|
|
55
|
+
});
|
|
56
|
+
|
|
52
57
|
export const contextSourceAtom = atom(get => {
|
|
53
58
|
const config = get(enviveConfigAtom);
|
|
54
59
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { atom } from 'jotai';
|
|
2
2
|
import { ColorMapping } from 'src/application/models/colorsConfig';
|
|
3
3
|
import { FrontendConfig } from 'src/application/models/frontendConfig';
|
|
4
|
-
import { FeatureFlagContextType } from 'src/contexts/featureFlagServiceContext/featureFlagServiceContext';
|
|
4
|
+
import type { FeatureFlagContextType } from 'src/contexts/featureFlagServiceContext/featureFlagServiceContext';
|
|
5
5
|
|
|
6
6
|
export const internalGraphQLColorsConfigAtom = atom<ColorMapping | undefined>(undefined);
|
|
7
7
|
const internalGraphQLFrontendConfigAtom = atom<FrontendConfig | undefined>(undefined);
|
|
@@ -3,6 +3,7 @@ import { useSetAtom } from 'jotai';
|
|
|
3
3
|
import { enviveConfigAtom } from 'src/atoms/envive/enviveConfig';
|
|
4
4
|
import Logger from 'src/application/logging/logger';
|
|
5
5
|
import { OrgConfigFeatureGate } from 'src/application/models';
|
|
6
|
+
import { AddToCartCallback } from 'src/types/enviveConfig';
|
|
6
7
|
|
|
7
8
|
const logger = new Logger('enviveConfigContext');
|
|
8
9
|
|
|
@@ -24,6 +25,7 @@ interface EnviveConfigProviderProps {
|
|
|
24
25
|
publicKey?: string;
|
|
25
26
|
featureOverrides?: Record<string, boolean>;
|
|
26
27
|
featureGates?: OrgConfigFeatureGate[];
|
|
28
|
+
addToCart?: AddToCartCallback;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export const EnviveConfigProvider: React.FC<EnviveConfigProviderProps> = ({
|
|
@@ -44,6 +46,7 @@ export const EnviveConfigProvider: React.FC<EnviveConfigProviderProps> = ({
|
|
|
44
46
|
publicKey,
|
|
45
47
|
featureOverrides,
|
|
46
48
|
featureGates,
|
|
49
|
+
addToCart,
|
|
47
50
|
}) => {
|
|
48
51
|
const setEnviveConfig = useSetAtom(enviveConfigAtom);
|
|
49
52
|
|
|
@@ -65,6 +68,7 @@ export const EnviveConfigProvider: React.FC<EnviveConfigProviderProps> = ({
|
|
|
65
68
|
publicKey,
|
|
66
69
|
featureOverrides,
|
|
67
70
|
featureGates,
|
|
71
|
+
addToCart,
|
|
68
72
|
}),
|
|
69
73
|
[
|
|
70
74
|
identifyingPrefix,
|
|
@@ -83,6 +87,7 @@ export const EnviveConfigProvider: React.FC<EnviveConfigProviderProps> = ({
|
|
|
83
87
|
publicKey,
|
|
84
88
|
featureOverrides,
|
|
85
89
|
featureGates,
|
|
90
|
+
addToCart,
|
|
86
91
|
],
|
|
87
92
|
);
|
|
88
93
|
|
|
@@ -7,6 +7,7 @@ import { resolvedBaseConfigVersionAtom } from 'src/atoms/envive/resolvedBaseConf
|
|
|
7
7
|
import { IHardcopyService } from 'src/services/hardcopyService';
|
|
8
8
|
import { IUserIdentityService } from 'src/services/userIdentityService';
|
|
9
9
|
import Logger from 'src/application/logging/logger';
|
|
10
|
+
import { AddToCartCallback } from 'src/types/enviveConfig';
|
|
10
11
|
import { AmplitudeProvider } from '../amplitudeContext';
|
|
11
12
|
import { EnviveConfigProvider } from '../enviveConfigContext';
|
|
12
13
|
import { EnviveCssProvider } from '../enviveCssContext';
|
|
@@ -51,8 +52,11 @@ interface EnviveProviderProps extends AgentWrapperProps {
|
|
|
51
52
|
enviveOn?: boolean;
|
|
52
53
|
cdnUrl?: string;
|
|
53
54
|
reactAppName?: string;
|
|
55
|
+
env?: string;
|
|
56
|
+
contextSource?: string;
|
|
54
57
|
previewMode?: boolean;
|
|
55
58
|
mockSalesAgentData?: any;
|
|
59
|
+
addToCart?: AddToCartCallback;
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
const SearchAgentWrapper: React.FC<AgentWrapperProps> = ({ children, enabledAgents }) => {
|
|
@@ -100,7 +104,10 @@ export const EnviveProvider: React.FC<EnviveProviderProps> = ({
|
|
|
100
104
|
enviveOn,
|
|
101
105
|
cdnUrl,
|
|
102
106
|
reactAppName,
|
|
107
|
+
env,
|
|
108
|
+
contextSource,
|
|
103
109
|
identifyingPrefix,
|
|
110
|
+
addToCart,
|
|
104
111
|
}) => {
|
|
105
112
|
const { extensionService } = services;
|
|
106
113
|
const [enviveServiceConfig, setEnviveServiceConfig] = useState<EnviveServiceConfig | null>(null);
|
|
@@ -196,6 +203,9 @@ export const EnviveProvider: React.FC<EnviveProviderProps> = ({
|
|
|
196
203
|
enviveOn={enviveOn}
|
|
197
204
|
cdnUrl={cdnUrl}
|
|
198
205
|
reactAppName={reactAppName}
|
|
206
|
+
env={env}
|
|
207
|
+
contextSource={contextSource}
|
|
208
|
+
addToCart={addToCart}
|
|
199
209
|
>
|
|
200
210
|
<NewOrgConfigProvider config={enviveServiceConfig.orgConfig ?? {}}>
|
|
201
211
|
<LocalStorageProvider>
|
|
@@ -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
|
+
}
|
|
@@ -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,6 +4,7 @@ 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,
|
|
@@ -153,6 +154,12 @@ export const SalesAgentProvider: React.FC<SalesAgentProviderProps> = ({
|
|
|
153
154
|
};
|
|
154
155
|
setMessages(current => [...current, [newMessage]]);
|
|
155
156
|
}
|
|
157
|
+
if (userEvent && userEvent.category === UserEventCategory.AddToCart) {
|
|
158
|
+
const newMessage = messageFromAddToCartEvent(userEvent);
|
|
159
|
+
if (newMessage) {
|
|
160
|
+
setMessages(current => [...current, [newMessage]]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
156
163
|
if (userEvent && userEvent.category === UserEventCategory.FormSubmitted) {
|
|
157
164
|
const { formResponseId, filledSchema } = userEvent.attributes;
|
|
158
165
|
setMessages(prevMessagesState => {
|
|
@@ -239,6 +246,7 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
239
246
|
onSuggestionClicked,
|
|
240
247
|
onTypedMessageSubmitted,
|
|
241
248
|
onFormResponseSubmitted,
|
|
249
|
+
injectAssistantMessage,
|
|
242
250
|
} = useSalesAgentChatAPI(contextWidget);
|
|
243
251
|
|
|
244
252
|
const salesAgent: SalesAgent = useMemo(() => {
|
|
@@ -255,6 +263,7 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
255
263
|
onSuggestionClicked,
|
|
256
264
|
onTypedMessageSubmitted,
|
|
257
265
|
onFormResponseSubmitted,
|
|
266
|
+
injectAssistantMessage,
|
|
258
267
|
};
|
|
259
268
|
}, [
|
|
260
269
|
messages,
|
|
@@ -269,6 +278,7 @@ export const useSalesAgent = (contextWidget?: WidgetInteractionComponent): Sales
|
|
|
269
278
|
onSuggestionClicked,
|
|
270
279
|
onTypedMessageSubmitted,
|
|
271
280
|
onFormResponseSubmitted,
|
|
281
|
+
injectAssistantMessage,
|
|
272
282
|
]);
|
|
273
283
|
return salesAgent;
|
|
274
284
|
};
|
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
|
+
});
|