@envive-ai/react-hooks 0.3.11 → 0.3.13

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.
Files changed (210) hide show
  1. package/dist/application/commerce-api.cjs +21 -41
  2. package/dist/application/commerce-api.d.cts +2 -4
  3. package/dist/application/commerce-api.d.ts +2 -4
  4. package/dist/application/commerce-api.js +21 -41
  5. package/dist/application/models/api/widgetTextRequest.d.cts +2 -1
  6. package/dist/application/models/api/widgetTextRequest.d.ts +2 -1
  7. package/dist/application/models/chatElementDisplayLocationV3.cjs +3 -1
  8. package/dist/application/models/chatElementDisplayLocationV3.d.cts +3 -1
  9. package/dist/application/models/chatElementDisplayLocationV3.d.ts +3 -1
  10. package/dist/application/models/chatElementDisplayLocationV3.js +3 -1
  11. package/dist/application/models/guards/api/index.cjs +6 -6
  12. package/dist/application/models/guards/api/index.js +6 -6
  13. package/dist/application/models/index.cjs +18 -18
  14. package/dist/application/models/index.js +18 -18
  15. package/dist/application/models/validators/validateResponse.cjs +3 -3
  16. package/dist/application/models/validators/validateResponse.js +3 -3
  17. package/dist/application/models/variantInfo/variantInfo.cjs +1 -1
  18. package/dist/application/models/variantInfo/variantInfo.d.cts +1 -1
  19. package/dist/application/models/variantInfo/variantInfo.d.ts +1 -1
  20. package/dist/application/models/variantInfo/variantInfo.js +1 -1
  21. package/dist/application/utils/analyticsUtils.cjs +5 -5
  22. package/dist/application/utils/analyticsUtils.d.cts +2 -2
  23. package/dist/application/utils/analyticsUtils.d.ts +2 -2
  24. package/dist/application/utils/analyticsUtils.js +3 -3
  25. package/dist/application/utils/nodeSelector.cjs +12 -14
  26. package/dist/application/utils/nodeSelector.js +12 -14
  27. package/dist/application/utils/widgetTextRequestToApiRequest.cjs +2 -1
  28. package/dist/application/utils/widgetTextRequestToApiRequest.js +2 -1
  29. package/dist/atoms/app/index.d.cts +7 -7
  30. package/dist/atoms/app/index.d.ts +7 -7
  31. package/dist/atoms/chat/chatState.cjs +5 -5
  32. package/dist/atoms/chat/chatState.d.cts +19 -19
  33. package/dist/atoms/chat/chatState.d.ts +19 -19
  34. package/dist/atoms/chat/chatState.js +3 -3
  35. package/dist/atoms/chat/form.d.cts +2 -2
  36. package/dist/atoms/chat/form.d.ts +2 -2
  37. package/dist/atoms/chat/index.d.ts +2 -2
  38. package/dist/atoms/chat/lastMessage.d.cts +2 -2
  39. package/dist/atoms/chat/lastMessage.d.ts +2 -2
  40. package/dist/atoms/chat/messageQueue.d.cts +6 -6
  41. package/dist/atoms/chat/messageQueue.d.ts +6 -6
  42. package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
  43. package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
  44. package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
  45. package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
  46. package/dist/atoms/chat/replies.cjs +4 -4
  47. package/dist/atoms/chat/replies.d.cts +3 -3
  48. package/dist/atoms/chat/replies.d.ts +3 -3
  49. package/dist/atoms/chat/replies.js +3 -3
  50. package/dist/atoms/chat/suggestions.d.cts +2 -2
  51. package/dist/atoms/chat/suggestions.d.ts +2 -2
  52. package/dist/atoms/envive/enviveConfig.d.cts +13 -13
  53. package/dist/atoms/envive/enviveConfig.d.ts +13 -13
  54. package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
  55. package/dist/atoms/globalSearch/globalSearch.d.ts +5 -5
  56. package/dist/atoms/org/customerService.d.cts +6 -6
  57. package/dist/atoms/org/customerService.d.ts +6 -6
  58. package/dist/atoms/org/graphqlConfig.d.cts +4 -4
  59. package/dist/atoms/org/graphqlConfig.d.ts +4 -4
  60. package/dist/atoms/org/index.cjs +1 -1
  61. package/dist/atoms/org/index.js +1 -1
  62. package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
  63. package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
  64. package/dist/atoms/org/orgAnalyticsConfig.d.cts +5 -5
  65. package/dist/atoms/org/orgAnalyticsConfig.d.ts +5 -5
  66. package/dist/atoms/search/chatSearch.d.cts +17 -17
  67. package/dist/atoms/search/searchAPI.d.cts +13 -13
  68. package/dist/atoms/search/searchAPI.d.ts +13 -13
  69. package/dist/atoms/search/utils.d.cts +1 -1
  70. package/dist/atoms/search/utils.d.ts +1 -1
  71. package/dist/atoms/widget/chatPreviewLoading.d.cts +2 -2
  72. package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
  73. package/dist/contexts/amplitudeContext/amplitudeContext.cjs +5 -5
  74. package/dist/contexts/amplitudeContext/amplitudeContext.d.cts +3 -2
  75. package/dist/contexts/amplitudeContext/amplitudeContext.d.ts +3 -2
  76. package/dist/contexts/amplitudeContext/amplitudeContext.js +5 -5
  77. package/dist/contexts/amplitudeContext/index.cjs +3 -3
  78. package/dist/contexts/amplitudeContext/index.d.cts +1 -1
  79. package/dist/contexts/amplitudeContext/index.d.ts +1 -1
  80. package/dist/contexts/amplitudeContext/index.js +1 -1
  81. package/dist/contexts/cdnContext/cdnContext.cjs +1 -1
  82. package/dist/contexts/cdnContext/cdnContext.js +1 -1
  83. package/dist/contexts/enviveConfigContext/enviveConfigContext.cjs +1 -1
  84. package/dist/contexts/enviveConfigContext/enviveConfigContext.js +1 -1
  85. package/dist/contexts/enviveContext/enviveContext.cjs +17 -14
  86. package/dist/contexts/enviveContext/enviveContext.d.cts +4 -1
  87. package/dist/contexts/enviveContext/enviveContext.d.ts +4 -1
  88. package/dist/contexts/enviveContext/enviveContext.js +17 -14
  89. package/dist/contexts/graphqlContext/graphqlContext.cjs +2 -2
  90. package/dist/contexts/graphqlContext/graphqlContext.js +2 -2
  91. package/dist/contexts/graphqlContext/mockV3Config.cjs +3 -2
  92. package/dist/contexts/graphqlContext/mockV3Config.js +3 -2
  93. package/dist/contexts/hardcopyContext/hardcopyContext.cjs +18 -8
  94. package/dist/contexts/hardcopyContext/hardcopyContext.d.cts +6 -4
  95. package/dist/contexts/hardcopyContext/hardcopyContext.d.ts +6 -4
  96. package/dist/contexts/hardcopyContext/hardcopyContext.js +16 -6
  97. package/dist/contexts/newOrgConfigContext/newOrgConfigContext.cjs +1 -1
  98. package/dist/contexts/newOrgConfigContext/newOrgConfigContext.js +1 -1
  99. package/dist/contexts/pageContext/pageContext.cjs +11 -7
  100. package/dist/contexts/pageContext/pageContext.js +11 -7
  101. package/dist/contexts/pageContext/types.d.cts +1 -1
  102. package/dist/contexts/salesAgentContext/chatAPI.cjs +11 -11
  103. package/dist/contexts/salesAgentContext/chatAPI.js +4 -4
  104. package/dist/contexts/salesAgentContext/salesAgentContext.cjs +1 -1
  105. package/dist/contexts/salesAgentContext/salesAgentContext.js +1 -1
  106. package/dist/contexts/salesAgentContext/salesAgentService.cjs +9 -8
  107. package/dist/contexts/salesAgentContext/salesAgentService.js +7 -6
  108. package/dist/contexts/searchContext/searchContext.cjs +2 -2
  109. package/dist/contexts/searchContext/searchContext.js +2 -2
  110. package/dist/contexts/systemSettingsContext/systemSettingsContext.cjs +1 -1
  111. package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
  112. package/dist/contexts/systemSettingsContext/systemSettingsContext.d.ts +2 -2
  113. package/dist/contexts/systemSettingsContext/systemSettingsContext.js +1 -1
  114. package/dist/contexts/types.cjs +1 -1
  115. package/dist/contexts/types.d.cts +4 -2
  116. package/dist/contexts/types.d.ts +4 -2
  117. package/dist/contexts/types.js +1 -1
  118. package/dist/contexts/typesV3.cjs +1 -1
  119. package/dist/contexts/typesV3.d.cts +27 -2
  120. package/dist/contexts/typesV3.d.ts +27 -2
  121. package/dist/contexts/typesV3.js +1 -1
  122. package/dist/contexts/userIdentityContext/userIdentityContext.cjs +2 -34
  123. package/dist/contexts/userIdentityContext/userIdentityContext.d.cts +1 -2
  124. package/dist/contexts/userIdentityContext/userIdentityContext.d.ts +1 -2
  125. package/dist/contexts/userIdentityContext/userIdentityContext.js +2 -33
  126. package/dist/hooks/AmplitudeOperations/useAmplitudeOperations.cjs +1 -1
  127. package/dist/hooks/AmplitudeOperations/useAmplitudeOperations.d.cts +2 -2
  128. package/dist/hooks/AmplitudeOperations/useAmplitudeOperations.d.ts +2 -2
  129. package/dist/hooks/AmplitudeOperations/useAmplitudeOperations.js +1 -1
  130. package/dist/hooks/ChatToggle/useChatToggle.cjs +6 -5
  131. package/dist/hooks/ChatToggle/useChatToggle.js +4 -3
  132. package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
  133. package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.ts +2 -2
  134. package/dist/hooks/Search/useSearch.cjs +9 -8
  135. package/dist/hooks/Search/useSearch.js +4 -3
  136. package/dist/hooks/Search/useSearchInput.cjs +7 -7
  137. package/dist/hooks/Search/useSearchInput.js +4 -4
  138. package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.cjs +6 -5
  139. package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.d.cts +2 -2
  140. package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.d.ts +2 -2
  141. package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.js +3 -2
  142. package/dist/hooks/UpdateAnalyticsProps/useUpdateAnalyticsProps.cjs +4 -3
  143. package/dist/hooks/UpdateAnalyticsProps/useUpdateAnalyticsProps.js +3 -2
  144. package/dist/hooks/utils.d.ts +1 -1
  145. package/dist/services/amplitudeService/amplitudeService.cjs +17 -57
  146. package/dist/services/amplitudeService/amplitudeService.d.cts +4 -47
  147. package/dist/services/amplitudeService/amplitudeService.d.ts +4 -47
  148. package/dist/services/amplitudeService/amplitudeService.js +14 -52
  149. package/dist/services/amplitudeService/eventNames.cjs +59 -0
  150. package/dist/services/amplitudeService/eventNames.d.cts +54 -0
  151. package/dist/services/amplitudeService/eventNames.d.ts +54 -0
  152. package/dist/services/amplitudeService/eventNames.js +57 -0
  153. package/dist/services/amplitudeService/index.cjs +3 -2
  154. package/dist/services/amplitudeService/index.d.cts +2 -1
  155. package/dist/services/amplitudeService/index.d.ts +2 -1
  156. package/dist/services/amplitudeService/index.js +2 -1
  157. package/dist/services/ga4ProjectionService/ga4EventSchema.cjs +108 -0
  158. package/dist/services/ga4ProjectionService/ga4EventSchema.js +108 -0
  159. package/dist/services/ga4ProjectionService/ga4ProjectionService.cjs +71 -0
  160. package/dist/services/ga4ProjectionService/ga4ProjectionService.js +71 -0
  161. package/dist/types/customerService.cjs +2 -1
  162. package/dist/types/customerService.d.cts +2 -1
  163. package/dist/types/customerService.d.ts +2 -1
  164. package/dist/types/customerService.js +2 -1
  165. package/dist/types/enviveConfig.d.cts +4 -1
  166. package/dist/types/enviveConfig.d.ts +4 -1
  167. package/package.json +2 -7
  168. package/src/application/commerce-api.ts +25 -49
  169. package/src/application/models/api/widgetTextRequest.ts +1 -0
  170. package/src/application/models/chatElementDisplayLocationV3.ts +2 -0
  171. package/src/application/models/variantInfo/variantInfo.ts +1 -1
  172. package/src/application/utils/analyticsUtils.ts +1 -1
  173. package/src/application/utils/nodeSelector.ts +15 -14
  174. package/src/application/utils/widgetTextRequestToApiRequest.ts +1 -0
  175. package/src/atoms/chat/chatState.ts +1 -1
  176. package/src/atoms/chat/replies.ts +1 -1
  177. package/src/contexts/amplitudeContext/amplitudeContext.tsx +0 -1
  178. package/src/contexts/enviveContext/enviveContext.tsx +5 -2
  179. package/src/contexts/graphqlContext/mockV3Config.ts +3 -0
  180. package/src/contexts/hardcopyContext/hardcopyContext.tsx +20 -2
  181. package/src/contexts/pageContext/pageContext.tsx +9 -5
  182. package/src/contexts/salesAgentContext/chatAPI.ts +0 -1
  183. package/src/contexts/salesAgentContext/salesAgentService.ts +5 -2
  184. package/src/contexts/types.ts +3 -1
  185. package/src/contexts/typesV3.ts +28 -0
  186. package/src/contexts/userIdentityContext/__tests__/userIdentityContext.test.tsx +1 -66
  187. package/src/contexts/userIdentityContext/userIdentityContext.tsx +0 -47
  188. package/src/hooks/AmplitudeOperations/useAmplitudeOperations.ts +2 -4
  189. package/src/hooks/ChatToggle/useChatToggle.ts +2 -4
  190. package/src/hooks/Search/useSearch.tsx +2 -4
  191. package/src/hooks/Search/useSearchInput.ts +1 -1
  192. package/src/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.ts +2 -4
  193. package/src/hooks/UpdateAnalyticsProps/useUpdateAnalyticsProps.ts +2 -4
  194. package/src/services/amplitudeService/amplitudeService.ts +14 -50
  195. package/src/services/amplitudeService/eventNames.ts +52 -0
  196. package/src/services/ga4ProjectionService/__tests__/ga4ProjectionService.test.ts +334 -0
  197. package/src/services/ga4ProjectionService/ga4EventSchema.ts +143 -0
  198. package/src/services/ga4ProjectionService/ga4ProjectionService.ts +137 -0
  199. package/src/types/customerService.ts +1 -0
  200. package/src/types/enviveConfig.ts +3 -0
  201. package/dist/hooks/IdentifyUser/index.cjs +0 -3
  202. package/dist/hooks/IdentifyUser/index.d.cts +0 -2
  203. package/dist/hooks/IdentifyUser/index.d.ts +0 -2
  204. package/dist/hooks/IdentifyUser/index.js +0 -3
  205. package/dist/hooks/IdentifyUser/useIdentifyUser.cjs +0 -35
  206. package/dist/hooks/IdentifyUser/useIdentifyUser.d.cts +0 -10
  207. package/dist/hooks/IdentifyUser/useIdentifyUser.d.ts +0 -10
  208. package/dist/hooks/IdentifyUser/useIdentifyUser.js +0 -34
  209. package/src/hooks/IdentifyUser/index.ts +0 -1
  210. package/src/hooks/IdentifyUser/useIdentifyUser.ts +0 -29
@@ -18,10 +18,8 @@ import {
18
18
  import { isFilterOpenAtom } from 'src/atoms/globalSearch/globalSearch';
19
19
  import { formatFilterDisplayName } from 'src/atoms/search/utils';
20
20
  import { ProductSorting } from 'src/atoms/search/types';
21
- import {
22
- SpiffyMetricsEventName,
23
- useAmplitude,
24
- } from 'src/contexts/amplitudeContext/amplitudeContext';
21
+ import { useAmplitude } from 'src/contexts/amplitudeContext/amplitudeContext';
22
+ import { SpiffyMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
25
23
  import { SpiffyWidgets } from 'src/application/models/spiffyWidgets';
26
24
  import { ProductCardConfig } from 'src/contexts/types';
27
25
  import { SearchResult } from 'src/application/models/api/search';
@@ -4,7 +4,7 @@ import Fuse from 'fuse.js';
4
4
 
5
5
  import { autocompleteStateAtom } from 'src/atoms/globalSearch';
6
6
  import { amplitudeTrackEventAtom } from 'src/atoms/amplitude/amplitudeTrackEventAtom';
7
- import { SpiffyMetricsEventName } from 'src/contexts/amplitudeContext';
7
+ import { SpiffyMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
8
8
  import { SpiffyWidgets } from 'src/application/models/spiffyWidgets';
9
9
 
10
10
  export interface UseSearchInputProps {
@@ -1,10 +1,8 @@
1
1
  import { RefObject, useEffect, useRef } from 'react';
2
2
  import { SpiffyWidgets } from 'src/application/models/spiffyWidgets';
3
3
  import { useIntersection } from 'src/hooks/Intersection/useIntersection';
4
- import {
5
- SpiffyMetricsEventName,
6
- useAmplitude,
7
- } from 'src/contexts/amplitudeContext/amplitudeContext';
4
+ import { useAmplitude } from 'src/contexts/amplitudeContext/amplitudeContext';
5
+ import { SpiffyMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
8
6
 
9
7
  /**
10
8
  * Tracks a component and logs an event to Amplitude when the component is visible.
@@ -1,9 +1,7 @@
1
1
  import { useAtomValue } from 'jotai';
2
2
  import { useEffect, useRef } from 'react';
3
- import {
4
- SpiffyMetricsEventName,
5
- useAmplitude,
6
- } from 'src/contexts/amplitudeContext/amplitudeContext';
3
+ import { useAmplitude } from 'src/contexts/amplitudeContext/amplitudeContext';
4
+ import { SpiffyMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
7
5
  import { hasParsedVariantInfoAtom, variantInfoAtom } from 'src/atoms/app/variant';
8
6
 
9
7
  /**
@@ -11,52 +11,11 @@ import type {
11
11
  import { FeatureFlagService } from 'src/contexts/featureFlagServiceContext/featureFlagServiceContext';
12
12
  import { WidgetTypeV3 } from 'src/contexts/typesV3';
13
13
  import { ChatElementDisplayLocationV3 } from 'src/application/models/chatElementDisplayLocationV3';
14
+ import { projectToGA4 } from '../ga4ProjectionService/ga4ProjectionService';
15
+ import { EnviveMetricsEventName, SpiffyMetricsEventName } from './eventNames';
14
16
 
15
- export enum SpiffyMetricsEventName {
16
- BundleLoaded = 'Bundle Loaded',
17
- EnvironmentInitialized = 'Environment Initialized',
18
- ChatLiveAgentBtnClick = 'Chat Live Agent Btn Click',
19
- ChatFloatingButtonVisible = 'Chat Floating Button Visible',
20
- ChatComponentVisible = 'Chat Component Visible',
21
- ChatComponentExpanded = 'Chat Component Expanded',
22
- ChatComponentCollapsed = 'Chat Component Collapsed',
23
- ChatUserMessageInput = 'Chat User Message Input',
24
- ChatSuggestionClicked = 'Chat Suggestion Clicked',
25
- ChatAssistantResponse = 'Chat Assistant Response',
26
- ProductCardClicked = 'Product Card Clicked',
27
- ProductReviewCardClicked = 'Product Review Card Clicked',
28
- AddToCartClicked = 'Add To Cart Clicked',
29
- PromptCardClicked = 'Prompt Card Clicked',
30
- SupportedEvent = 'Supported Event',
31
- SearchBackToResponseClicked = 'Search Back to Response Clicked',
32
- PerformanceMetrics = 'Performance Metrics',
33
- SearchBarClicked = 'Search Bar Clicked',
34
- OrderLookupStarted = 'Order Lookup Started',
35
- OrderLookupFormSubmitted = 'Order Lookup Form Submitted',
36
- SearchComponentVisible = 'Search Component Visible',
37
- SearchZeroStateSuggestionClicked = 'Search Zero State Suggestion Clicked', // This is the scrolling list of suggestion buttons in global search
38
- SearchInputStarted = 'Search Input Started',
39
- SearchQuerySubmitted = 'Search Query Submitted',
40
- // SearchAutocompleteViewed = 'Search Autocomplete Viewed', // TODO: add this when autocomplete is added
41
- // SearchAutocompleteClicked = 'Search Autocomplete Clicked', // TODO: add this when autocomplete is added
42
- SearchResultsViewed = 'Search Results Viewed',
43
- SearchTimeToFirstClick = 'Search Time to First Click',
44
- SearchZeroResultsRate = 'Search Zero Results Rate',
45
- SearchFilterClicked = 'Search Filter Clicked',
46
- SearchSortClicked = 'Search Sort Clicked',
47
- }
48
-
49
- export enum EnviveMetricsEventName {
50
- WidgetRendered = 'Widget Rendered',
51
- WidgetInteraction = 'Widget Interaction',
52
- ChatRequest = 'Chat Request',
53
- ChatResponse = 'Chat Response',
54
- EnviveInitialized = 'Envive Initialized',
55
- PageViewed = 'Page Viewed', // Will this be useful?
56
- WidgetTextRequest = 'Widget Text Request',
57
- WidgetTextResponse = 'Widget Text Response',
58
- WidgetTextClicked = 'Widget Text Clicked',
59
- }
17
+ // Re-export event names for convenience
18
+ export { EnviveMetricsEventName, SpiffyMetricsEventName };
60
19
 
61
20
  export interface TrackEventParams {
62
21
  eventName: SpiffyMetricsEventName | EnviveMetricsEventName;
@@ -142,7 +101,7 @@ export class AmplitudeService {
142
101
  'environment.envive_enabled': this.config.enabledFeatures?.envive,
143
102
  };
144
103
 
145
- const orgLevelAmplitudeTrackingProps = AmplitudeService.isEnviveEvent(eventName)
104
+ const orgLevelAmplitudeTrackingProps = AmplitudeService.isV3EnviveEvent(eventName)
146
105
  ? {
147
106
  ...this.config.featureFlagService.getFullFlagValues(),
148
107
  ...experimentProps,
@@ -375,12 +334,12 @@ export class AmplitudeService {
375
334
  return eventProps ?? {};
376
335
  }
377
336
 
378
- static isEnviveEvent(eventName: SpiffyMetricsEventName | EnviveMetricsEventName): boolean {
337
+ static isV3EnviveEvent(eventName: SpiffyMetricsEventName | EnviveMetricsEventName): boolean {
379
338
  return Object.values(EnviveMetricsEventName).includes(eventName as EnviveMetricsEventName);
380
339
  }
381
340
 
382
341
  static decorateEventName(eventName: SpiffyMetricsEventName | EnviveMetricsEventName): string {
383
- if (AmplitudeService.isEnviveEvent(eventName)) {
342
+ if (AmplitudeService.isV3EnviveEvent(eventName)) {
384
343
  return `[Envive] ${eventName}`;
385
344
  }
386
345
  return `[Spiffy] ${eventName}`;
@@ -438,8 +397,13 @@ export class AmplitudeService {
438
397
  },
439
398
  );
440
399
 
441
- if (alsoSendToGoogleAnalytics && this.config.orgGaConfig) {
442
- // TODO: Add in windowDataLayerService or context alternative and hook it up here
400
+ if (AmplitudeService.isV3EnviveEvent(eventName) && this.config.orgGaConfig) {
401
+ const mergedPropsForGA4 = {
402
+ ...this.getDefaultTrackingProps(eventName),
403
+ ...mappedEventProps,
404
+ };
405
+ projectToGA4(eventName as EnviveMetricsEventName, mergedPropsForGA4);
406
+ } else if (alsoSendToGoogleAnalytics && this.config.orgGaConfig) {
443
407
  Logger.logDebug('[spiffy-ai] GA tracking', decoratedEventName);
444
408
  if (typeof window !== 'undefined' && window.dataLayer) {
445
409
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Event name enums for Amplitude tracking.
3
+ *
4
+ * NOTE: This file exists separately to avoid circular dependency issues.
5
+ * The amplitudeService imports from ga4ProjectionService, which needs these enums.
6
+ * By keeping the enums in a separate file, ga4ProjectionService can import them
7
+ * without creating a circular dependency with amplitudeService.
8
+ */
9
+
10
+ export enum SpiffyMetricsEventName {
11
+ BundleLoaded = 'Bundle Loaded',
12
+ EnvironmentInitialized = 'Environment Initialized',
13
+ ChatLiveAgentBtnClick = 'Chat Live Agent Btn Click',
14
+ ChatFloatingButtonVisible = 'Chat Floating Button Visible',
15
+ ChatComponentVisible = 'Chat Component Visible',
16
+ ChatComponentExpanded = 'Chat Component Expanded',
17
+ ChatComponentCollapsed = 'Chat Component Collapsed',
18
+ ChatUserMessageInput = 'Chat User Message Input',
19
+ ChatSuggestionClicked = 'Chat Suggestion Clicked',
20
+ ChatAssistantResponse = 'Chat Assistant Response',
21
+ ProductCardClicked = 'Product Card Clicked',
22
+ ProductReviewCardClicked = 'Product Review Card Clicked',
23
+ AddToCartClicked = 'Add To Cart Clicked',
24
+ PromptCardClicked = 'Prompt Card Clicked',
25
+ SupportedEvent = 'Supported Event',
26
+ SearchBackToResponseClicked = 'Search Back to Response Clicked',
27
+ PerformanceMetrics = 'Performance Metrics',
28
+ SearchBarClicked = 'Search Bar Clicked',
29
+ OrderLookupStarted = 'Order Lookup Started',
30
+ OrderLookupFormSubmitted = 'Order Lookup Form Submitted',
31
+ SearchComponentVisible = 'Search Component Visible',
32
+ SearchZeroStateSuggestionClicked = 'Search Zero State Suggestion Clicked',
33
+ SearchInputStarted = 'Search Input Started',
34
+ SearchQuerySubmitted = 'Search Query Submitted',
35
+ SearchResultsViewed = 'Search Results Viewed',
36
+ SearchTimeToFirstClick = 'Search Time to First Click',
37
+ SearchZeroResultsRate = 'Search Zero Results Rate',
38
+ SearchFilterClicked = 'Search Filter Clicked',
39
+ SearchSortClicked = 'Search Sort Clicked',
40
+ }
41
+
42
+ export enum EnviveMetricsEventName {
43
+ WidgetRendered = 'Widget Rendered',
44
+ WidgetInteraction = 'Widget Interaction',
45
+ ChatRequest = 'Chat Request',
46
+ ChatResponse = 'Chat Response',
47
+ EnviveInitialized = 'Envive Initialized',
48
+ PageViewed = 'Page Viewed',
49
+ WidgetTextRequest = 'Widget Text Request',
50
+ WidgetTextResponse = 'Widget Text Response',
51
+ WidgetTextClicked = 'Widget Text Clicked',
52
+ }
@@ -0,0 +1,334 @@
1
+ import { EnviveMetricsEventName } from '../../amplitudeService/eventNames';
2
+ import { projectToGA4 } from '../ga4ProjectionService';
3
+
4
+ describe('projectToGA4', () => {
5
+ beforeEach(() => {
6
+ window.dataLayer = [];
7
+ });
8
+
9
+ afterEach(() => {
10
+ delete (window as any).dataLayer;
11
+ });
12
+
13
+ describe('excluded events', () => {
14
+ it('should not push to dataLayer for WidgetTextRequest', () => {
15
+ projectToGA4(EnviveMetricsEventName.WidgetTextRequest, { widget_type: 'test' });
16
+ expect(window.dataLayer).toHaveLength(0);
17
+ });
18
+
19
+ it('should not push to dataLayer for WidgetTextResponse', () => {
20
+ projectToGA4(EnviveMetricsEventName.WidgetTextResponse, { widget_type: 'test' });
21
+ expect(window.dataLayer).toHaveLength(0);
22
+ });
23
+
24
+ it('should not push to dataLayer for WidgetTextClicked', () => {
25
+ projectToGA4(EnviveMetricsEventName.WidgetTextClicked, { text: 'hello' });
26
+ expect(window.dataLayer).toHaveLength(0);
27
+ });
28
+ });
29
+
30
+ describe('Widget Rendered', () => {
31
+ it('should push filtered and flattened props', () => {
32
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
33
+ 'context.page_type': 'pdp',
34
+ 'context.page_id': 'product-123',
35
+ 'trigger.widget': 'floating_button',
36
+ 'trigger.interaction_id': 'abc-123',
37
+ });
38
+
39
+ expect(window.dataLayer).toHaveLength(1);
40
+ expect(window.dataLayer[0]).toEqual({
41
+ event: 'envive_widget_rendered',
42
+ context_page_type: 'pdp',
43
+ context_page_id: 'product-123',
44
+ trigger_widget: 'floating_button',
45
+ });
46
+ });
47
+
48
+ it('should exclude fields not in allowedFields', () => {
49
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
50
+ 'context.page_type': 'pdp',
51
+ 'context.page_id': 'product-123',
52
+ 'trigger.widget': 'floating_button',
53
+ 'trigger.interaction_id': 'should-be-dropped',
54
+ 'some.random_field': 'also-dropped',
55
+ });
56
+
57
+ const pushed = window.dataLayer[0];
58
+ expect(pushed).not.toHaveProperty('trigger_interaction_id');
59
+ expect(pushed).not.toHaveProperty('some_random_field');
60
+ });
61
+ });
62
+
63
+ describe('page_id sanitization', () => {
64
+ it('should keep page_id for pdp page type', () => {
65
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
66
+ 'context.page_type': 'pdp',
67
+ 'context.page_id': 'product-123',
68
+ 'trigger.widget': 'floating_button',
69
+ });
70
+
71
+ expect(window.dataLayer[0]).toHaveProperty('context_page_id', 'product-123');
72
+ });
73
+
74
+ it('should keep page_id for plp page type', () => {
75
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
76
+ 'context.page_type': 'plp',
77
+ 'context.page_id': 'category-456',
78
+ 'trigger.widget': 'floating_button',
79
+ });
80
+
81
+ expect(window.dataLayer[0]).toHaveProperty('context_page_id', 'category-456');
82
+ });
83
+
84
+ it('should omit page_id for search page type', () => {
85
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
86
+ 'context.page_type': 'search',
87
+ 'context.page_id': 'some search query',
88
+ 'trigger.widget': 'floating_button',
89
+ });
90
+
91
+ expect(window.dataLayer[0]).not.toHaveProperty('context_page_id');
92
+ });
93
+
94
+ it('should omit page_id for homepage page type', () => {
95
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
96
+ 'context.page_type': 'homepage',
97
+ 'context.page_id': 'https://example.com',
98
+ 'trigger.widget': 'floating_button',
99
+ });
100
+
101
+ expect(window.dataLayer[0]).not.toHaveProperty('context_page_id');
102
+ });
103
+
104
+ it('should omit page_id for other page type', () => {
105
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
106
+ 'context.page_type': 'other',
107
+ 'context.page_id': 'https://example.com/about',
108
+ 'trigger.widget': 'floating_button',
109
+ });
110
+
111
+ expect(window.dataLayer[0]).not.toHaveProperty('context_page_id');
112
+ });
113
+ });
114
+
115
+ describe('Widget Interaction', () => {
116
+ it('should push base fields and flatten dot keys', () => {
117
+ projectToGA4(EnviveMetricsEventName.WidgetInteraction, {
118
+ 'context.page_type': 'pdp',
119
+ 'context.page_id': 'product-123',
120
+ 'trigger.widget': 'chat_overlay',
121
+ 'trigger.widget_interaction': 'link_clicked',
122
+ });
123
+
124
+ expect(window.dataLayer[0]).toEqual({
125
+ event: 'envive_widget_interaction',
126
+ context_page_type: 'pdp',
127
+ context_page_id: 'product-123',
128
+ trigger_widget: 'chat_overlay',
129
+ trigger_widget_interaction: 'link_clicked',
130
+ });
131
+ });
132
+
133
+ it('should extract collapse_source from widget_interaction_data for widget_collapsed', () => {
134
+ projectToGA4(EnviveMetricsEventName.WidgetInteraction, {
135
+ 'context.page_type': 'pdp',
136
+ 'context.page_id': 'product-123',
137
+ 'trigger.widget': 'chat_overlay',
138
+ 'trigger.widget_interaction': 'widget_collapsed',
139
+ 'trigger.widget_interaction_data': { collapse_source: 'close_button' },
140
+ });
141
+
142
+ expect(window.dataLayer[0]).toHaveProperty('trigger_collapse_source', 'close_button');
143
+ });
144
+
145
+ it('should extract product_id from widget_interaction_data for product_card_clicked', () => {
146
+ projectToGA4(EnviveMetricsEventName.WidgetInteraction, {
147
+ 'context.page_type': 'pdp',
148
+ 'context.page_id': 'product-123',
149
+ 'trigger.widget': 'chat_overlay',
150
+ 'trigger.widget_interaction': 'product_card_clicked',
151
+ 'trigger.widget_interaction_data': { product_id: 'sku-789', url: 'https://example.com' },
152
+ });
153
+
154
+ const pushed = window.dataLayer[0];
155
+ expect(pushed).toHaveProperty('trigger_product_id', 'sku-789');
156
+ expect(pushed).not.toHaveProperty('url');
157
+ });
158
+
159
+ it('should extract suggestion_id from widget_interaction_data for suggestion_scrolled', () => {
160
+ projectToGA4(EnviveMetricsEventName.WidgetInteraction, {
161
+ 'context.page_type': 'pdp',
162
+ 'context.page_id': 'product-123',
163
+ 'trigger.widget': 'chat_overlay',
164
+ 'trigger.widget_interaction': 'suggestion_scrolled',
165
+ 'trigger.widget_interaction_data': { suggestion_id: 'sug-456' },
166
+ });
167
+
168
+ expect(window.dataLayer[0]).toHaveProperty('trigger_suggestion_id', 'sug-456');
169
+ });
170
+
171
+ it('should drop widget_interaction_data for unwhitelisted interactions', () => {
172
+ projectToGA4(EnviveMetricsEventName.WidgetInteraction, {
173
+ 'context.page_type': 'pdp',
174
+ 'context.page_id': 'product-123',
175
+ 'trigger.widget': 'chat_overlay',
176
+ 'trigger.widget_interaction': 'link_clicked',
177
+ 'trigger.widget_interaction_data': { url: 'https://example.com' },
178
+ });
179
+
180
+ const pushed = window.dataLayer[0];
181
+ expect(pushed).not.toHaveProperty('url');
182
+ expect(pushed).not.toHaveProperty('trigger_widget_interaction_data');
183
+ });
184
+ });
185
+
186
+ describe('Chat Request', () => {
187
+ it('should include only allowed fields', () => {
188
+ projectToGA4(EnviveMetricsEventName.ChatRequest, {
189
+ page_type: 'pdp',
190
+ page_id: 'product-123',
191
+ 'trigger.widget': 'chat_overlay',
192
+ 'chat.request_id': 'req-001',
193
+ 'chat.request_type': 'suggestion',
194
+ 'chat.request_text': 'Tell me about this product',
195
+ 'chat.user_typed': false,
196
+ 'chat.suggestion_id': 'sug-123',
197
+ 'chat.suggestion_category': 'product_info',
198
+ });
199
+
200
+ const pushed = window.dataLayer[0];
201
+ expect(pushed).toEqual({
202
+ event: 'envive_chat_request',
203
+ page_type: 'pdp',
204
+ page_id: 'product-123',
205
+ trigger_widget: 'chat_overlay',
206
+ chat_request_type: 'suggestion',
207
+ chat_user_typed: false,
208
+ chat_suggestion_id: 'sug-123',
209
+ chat_suggestion_category: 'product_info',
210
+ });
211
+ });
212
+ });
213
+
214
+ describe('Chat Response', () => {
215
+ it('should include only allowed fields', () => {
216
+ projectToGA4(EnviveMetricsEventName.ChatResponse, {
217
+ page_type: 'plp',
218
+ page_id: 'category-456',
219
+ 'trigger.widget': 'chat_overlay',
220
+ 'chat.response_time_ms': '1200',
221
+ 'chat.request_type': 'text',
222
+ 'chat.request_id': 'req-001',
223
+ 'chat.request_text': 'Show me shoes',
224
+ 'chat.user_typed': true,
225
+ 'chat.product_cards_returned': 3,
226
+ 'chat.review_cards_returned': 0,
227
+ });
228
+
229
+ const pushed = window.dataLayer[0];
230
+ expect(pushed).toEqual({
231
+ event: 'envive_chat_response',
232
+ page_type: 'plp',
233
+ page_id: 'category-456',
234
+ trigger_widget: 'chat_overlay',
235
+ chat_user_typed: true,
236
+ chat_response_time_ms: '1200',
237
+ chat_product_cards_returned: 3,
238
+ chat_review_cards_returned: 0,
239
+ });
240
+ });
241
+ });
242
+
243
+ describe('Envive Initialized', () => {
244
+ it('should include only allowed fields and drop environment details', () => {
245
+ projectToGA4(EnviveMetricsEventName.EnviveInitialized, {
246
+ 'environment.execution_context': 'bundle',
247
+ 'environment.page_url': 'https://example.com/products/shoe',
248
+ 'environment.envive_user_id': 'user-abc',
249
+ 'environment.execution_environment': 'prod',
250
+ 'environment.context_source': 'app',
251
+ 'environment.sales_agent_enabled': true,
252
+ 'environment.search_enabled': false,
253
+ 'environment.envive_on_query_param': false,
254
+ 'environment.window_show': true,
255
+ 'environment.client_session': true,
256
+ 'environment.envive_enabled': true,
257
+ 'performance.start_time_ms': 150,
258
+ 'performance.initialize_time_ms': 320,
259
+ });
260
+
261
+ expect(window.dataLayer[0]).toEqual({
262
+ event: 'envive_initialized',
263
+ environment_sales_agent_enabled: true,
264
+ environment_envive_enabled: true,
265
+ performance_start_time_ms: 150,
266
+ performance_initialize_time_ms: 320,
267
+ });
268
+ });
269
+ });
270
+
271
+ describe('Page Viewed', () => {
272
+ it('should use envive_page_context_evaluated as event name', () => {
273
+ projectToGA4(EnviveMetricsEventName.PageViewed, {
274
+ 'context.page_type': 'pdp',
275
+ 'context.page_id': 'product-123',
276
+ 'context.supported': true,
277
+ 'context.ready': true,
278
+ 'context.page_variant_id': 'variant-A',
279
+ 'environment.envive_enabled': true,
280
+ });
281
+
282
+ expect(window.dataLayer[0]).toEqual({
283
+ event: 'envive_page_context_evaluated',
284
+ context_page_type: 'pdp',
285
+ context_page_id: 'product-123',
286
+ context_supported: true,
287
+ context_ready: true,
288
+ context_page_variant_id: 'variant-A',
289
+ environment_envive_enabled: true,
290
+ });
291
+ });
292
+
293
+ it('should omit page_id for non-pdp/plp page types', () => {
294
+ projectToGA4(EnviveMetricsEventName.PageViewed, {
295
+ 'context.page_type': 'search',
296
+ 'context.page_id': 'shoes',
297
+ 'context.supported': true,
298
+ 'context.ready': true,
299
+ 'environment.envive_enabled': true,
300
+ });
301
+
302
+ const pushed = window.dataLayer[0];
303
+ expect(pushed).not.toHaveProperty('context_page_id');
304
+ expect(pushed).toHaveProperty('context_page_type', 'search');
305
+ });
306
+ });
307
+
308
+ describe('edge cases', () => {
309
+ it('should handle undefined eventProps', () => {
310
+ projectToGA4(EnviveMetricsEventName.EnviveInitialized);
311
+
312
+ expect(window.dataLayer).toHaveLength(1);
313
+ expect(window.dataLayer[0]).toEqual({ event: 'envive_initialized' });
314
+ });
315
+
316
+ it('should handle empty eventProps', () => {
317
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {});
318
+
319
+ expect(window.dataLayer).toHaveLength(1);
320
+ expect(window.dataLayer[0]).toEqual({ event: 'envive_widget_rendered' });
321
+ });
322
+
323
+ it('should not throw when window.dataLayer is undefined', () => {
324
+ delete (window as any).dataLayer;
325
+
326
+ expect(() => {
327
+ projectToGA4(EnviveMetricsEventName.WidgetRendered, {
328
+ 'context.page_type': 'pdp',
329
+ 'trigger.widget': 'floating_button',
330
+ });
331
+ }).not.toThrow();
332
+ });
333
+ });
334
+ });
@@ -0,0 +1,143 @@
1
+ /**
2
+ * GA4 Event Schema — single source of truth for Envive → GA4 projection.
3
+ *
4
+ * Every EnviveMetricsEventName MUST have an entry here. The Record type enforces
5
+ * this at compile time — adding a new enum member without a schema entry will
6
+ * cause a TypeScript error.
7
+ *
8
+ * ## How to update this file
9
+ *
10
+ * When adding or modifying event properties, decide whether they belong in `allowedFields` using these criteria:
11
+ * - **Low cardinality** — avoid unbounded values (free text, IDs, URLs, timestamps).
12
+ * - **Non-sensitive** — no PII, user input text, or internal implementation details.
13
+ * - **Flat scalar** — GA4 custom dimensions are flat strings/numbers/booleans.
14
+ * Nested objects should NOT be added; extract specific sub-fields instead.
15
+ * - **Snake_case key** — must already be dot-notation snake_case (e.g. `chat.user_typed`).
16
+ * - **Analytically useful** — the field should enable meaningful filtering/segmentation
17
+ * for merchants in their GA4 dashboards.
18
+ *
19
+ * If excluded from `allowedFields`, the projection layer will automatically drop it.
20
+ *
21
+ * IF IN DOUBT, DON'T HESITATE TO REACH OUT TO THE ANALYTICS TEAM!!!
22
+ */
23
+ import { EnviveMetricsEventName } from '../amplitudeService/eventNames';
24
+
25
+ /**
26
+ * Defines which fields to extract from `trigger.widget_interaction_data`
27
+ * for a given `trigger.widget_interaction` value.
28
+ *
29
+ * Key: the value of `trigger.widget_interaction` (e.g. "widget_collapsed")
30
+ * Value: mapping of { ga4FlatKey: sourceFieldInWidgetInteractionData }
31
+ */
32
+ export type WidgetInteractionDataProjection = Record<string, Record<string, string>>;
33
+
34
+ export interface GA4ProjectedEventConfig {
35
+ gaEventName: string;
36
+ allowedFields: readonly string[];
37
+ widgetInteractionDataProjections?: WidgetInteractionDataProjection;
38
+ }
39
+
40
+ export interface GA4ExcludedEventConfig {
41
+ gaEventName: null;
42
+ }
43
+
44
+ export type GA4EventSchemaEntry = GA4ProjectedEventConfig | GA4ExcludedEventConfig;
45
+
46
+ const WIDGET_INTERACTION_DATA_PROJECTIONS: WidgetInteractionDataProjection = {
47
+ widget_collapsed: { trigger_collapse_source: 'collapse_source' },
48
+ product_card_clicked: { trigger_product_id: 'product_id' },
49
+ suggestion_scrolled: { trigger_suggestion_id: 'suggestion_id' },
50
+ };
51
+
52
+ export const GA4_EVENT_SCHEMA: Record<EnviveMetricsEventName, GA4EventSchemaEntry> = {
53
+ [EnviveMetricsEventName.WidgetRendered]: {
54
+ gaEventName: 'envive_widget_rendered',
55
+ allowedFields: [
56
+ 'context.page_type',
57
+ 'context.page_id',
58
+ 'trigger.widget',
59
+ 'trigger.widget_role',
60
+ 'context.surface',
61
+ ],
62
+ },
63
+
64
+ // This event is not currently implemented
65
+ [EnviveMetricsEventName.WidgetInteraction]: {
66
+ gaEventName: 'envive_widget_interaction',
67
+ allowedFields: [
68
+ 'context.page_type',
69
+ 'context.page_id',
70
+ 'trigger.widget',
71
+ 'trigger.widget_interaction',
72
+ 'trigger.entity_role',
73
+ 'context.surface',
74
+ ],
75
+ widgetInteractionDataProjections: WIDGET_INTERACTION_DATA_PROJECTIONS,
76
+ },
77
+
78
+ [EnviveMetricsEventName.ChatRequest]: {
79
+ gaEventName: 'envive_chat_request',
80
+ allowedFields: [
81
+ 'page_type',
82
+ 'page_id',
83
+ 'trigger.widget',
84
+ 'chat.request_type',
85
+ // 'chat.request_text', // This is high cardinality and potentially PII but we might want to include it later
86
+ 'chat.user_typed',
87
+ 'chat.suggestion_id', // not currently implemented
88
+ 'chat.suggestion_category', // not currently implemented
89
+ 'chat.suggestion_created_at', // not currently implemented
90
+ 'chat.suggestion_is_answer', // not currently implemented
91
+ 'chat.form_type', // not currently implemented
92
+ ],
93
+ },
94
+
95
+ [EnviveMetricsEventName.ChatResponse]: {
96
+ gaEventName: 'envive_chat_response',
97
+ allowedFields: [
98
+ 'page_type',
99
+ 'page_id',
100
+ 'trigger.widget', // not currently implemented (maybe it should'nt be???)
101
+ 'chat.user_typed',
102
+ 'chat.response_time_ms',
103
+ 'chat.product_cards_returned',
104
+ 'chat.review_cards_returned',
105
+ ],
106
+ },
107
+
108
+ [EnviveMetricsEventName.EnviveInitialized]: {
109
+ gaEventName: 'envive_initialized',
110
+ allowedFields: [
111
+ 'environment.sales_agent_enabled',
112
+ 'environment.envive_enabled',
113
+ // 'environment.search_enabled', // For now, search is not implemented. We will add it later.
114
+ 'performance.start_time_ms',
115
+ 'performance.initialize_time_ms',
116
+ ],
117
+ },
118
+
119
+ // This event is not currently implemented
120
+ [EnviveMetricsEventName.PageViewed]: {
121
+ gaEventName: 'envive_page_context_evaluated',
122
+ allowedFields: [
123
+ 'context.page_type',
124
+ 'context.page_id',
125
+ 'context.supported',
126
+ 'context.ready',
127
+ 'context.page_variant_id',
128
+ 'environment.envive_enabled',
129
+ ],
130
+ },
131
+
132
+ [EnviveMetricsEventName.WidgetTextRequest]: {
133
+ gaEventName: null,
134
+ },
135
+
136
+ [EnviveMetricsEventName.WidgetTextResponse]: {
137
+ gaEventName: null,
138
+ },
139
+
140
+ [EnviveMetricsEventName.WidgetTextClicked]: {
141
+ gaEventName: null,
142
+ },
143
+ };