@envive-ai/react-hooks 0.3.10 → 0.3.12

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 (215) hide show
  1. package/dist/application/commerce-api.cjs +26 -22
  2. package/dist/application/commerce-api.d.cts +58 -0
  3. package/dist/application/commerce-api.d.ts +59 -0
  4. package/dist/application/commerce-api.js +16 -12
  5. package/dist/application/models/api/widgetText.d.cts +14 -0
  6. package/dist/application/models/api/widgetText.d.ts +14 -0
  7. package/dist/application/models/api/widgetTextRequest.d.cts +16 -0
  8. package/dist/application/models/api/widgetTextRequest.d.ts +16 -0
  9. package/dist/application/models/chatElementDisplayLocationV3.cjs +4 -1
  10. package/dist/application/models/chatElementDisplayLocationV3.d.cts +4 -1
  11. package/dist/application/models/chatElementDisplayLocationV3.d.ts +4 -1
  12. package/dist/application/models/chatElementDisplayLocationV3.js +4 -1
  13. package/dist/application/models/guards/api/index.cjs +7 -7
  14. package/dist/application/models/guards/api/index.js +7 -7
  15. package/dist/application/models/guards/api/isApiFormResponse.cjs +1 -1
  16. package/dist/application/models/guards/api/isApiFormResponse.js +1 -1
  17. package/dist/application/models/guards/api/isApiFormSubmittedResponseAttributes.cjs +1 -1
  18. package/dist/application/models/guards/api/isApiFormSubmittedResponseAttributes.js +1 -1
  19. package/dist/application/models/guards/api/isApiOrderResponseAttributes.cjs +1 -1
  20. package/dist/application/models/guards/api/isApiOrderResponseAttributes.js +1 -1
  21. package/dist/application/models/guards/api/isApiProductResponseAttributes.cjs +1 -1
  22. package/dist/application/models/guards/api/isApiProductResponseAttributes.js +1 -1
  23. package/dist/application/models/guards/api/isApiResponse.cjs +1 -1
  24. package/dist/application/models/guards/api/isApiResponse.js +1 -1
  25. package/dist/application/models/index.cjs +29 -29
  26. package/dist/application/models/index.d.cts +25 -25
  27. package/dist/application/models/index.d.ts +25 -25
  28. package/dist/application/models/index.js +29 -29
  29. package/dist/application/models/validators/validateGraphQLFrontendConfig.cjs +1 -1
  30. package/dist/application/models/validators/validateGraphQLFrontendConfig.js +1 -1
  31. package/dist/application/models/validators/validateResponse.cjs +3 -3
  32. package/dist/application/models/validators/validateResponse.js +3 -3
  33. package/dist/application/models/validators/validateUserEvent.cjs +2 -2
  34. package/dist/application/models/validators/validateUserEvent.js +2 -2
  35. package/dist/application/models/variantInfo/variantInfo.cjs +2 -1
  36. package/dist/application/models/variantInfo/variantInfo.d.cts +6 -1
  37. package/dist/application/models/variantInfo/variantInfo.d.ts +6 -1
  38. package/dist/application/models/variantInfo/variantInfo.js +2 -1
  39. package/dist/application/utils/analyticsUtils.cjs +1 -1
  40. package/dist/application/utils/analyticsUtils.js +1 -1
  41. package/dist/application/utils/nodeSelector.cjs +12 -14
  42. package/dist/application/utils/nodeSelector.js +12 -14
  43. package/dist/application/utils/widgetTextFromApiWidgetTextResponse.cjs +3 -1
  44. package/dist/application/utils/widgetTextFromApiWidgetTextResponse.js +3 -1
  45. package/dist/application/utils/widgetTextRequestToApiRequest.cjs +4 -1
  46. package/dist/application/utils/widgetTextRequestToApiRequest.js +4 -1
  47. package/dist/atoms/app/index.cjs +7 -7
  48. package/dist/atoms/app/index.d.cts +1 -1
  49. package/dist/atoms/app/index.d.ts +8 -8
  50. package/dist/atoms/app/index.js +1 -1
  51. package/dist/atoms/app/variant.cjs +44 -4
  52. package/dist/atoms/app/variant.d.cts +8 -8
  53. package/dist/atoms/app/variant.d.ts +8 -8
  54. package/dist/atoms/app/variant.js +44 -5
  55. package/dist/atoms/chat/chatState.d.cts +18 -18
  56. package/dist/atoms/chat/chatState.d.ts +17 -17
  57. package/dist/atoms/chat/form.cjs +1 -1
  58. package/dist/atoms/chat/form.d.cts +2 -2
  59. package/dist/atoms/chat/form.d.ts +2 -2
  60. package/dist/atoms/chat/form.js +1 -1
  61. package/dist/atoms/chat/index.d.cts +3 -3
  62. package/dist/atoms/chat/index.d.ts +2 -2
  63. package/dist/atoms/chat/lastMessage.d.cts +2 -2
  64. package/dist/atoms/chat/lastMessage.d.ts +2 -2
  65. package/dist/atoms/chat/messageQueue.d.cts +6 -6
  66. package/dist/atoms/chat/messageQueue.d.ts +6 -6
  67. package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
  68. package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
  69. package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
  70. package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
  71. package/dist/atoms/chat/replies.d.cts +3 -3
  72. package/dist/atoms/chat/replies.d.ts +3 -3
  73. package/dist/atoms/chat/suggestions.d.cts +2 -2
  74. package/dist/atoms/chat/suggestions.d.ts +2 -2
  75. package/dist/atoms/envive/enviveConfig.d.cts +19 -0
  76. package/dist/atoms/envive/enviveConfig.d.ts +19 -0
  77. package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
  78. package/dist/atoms/globalSearch/globalSearch.d.ts +5 -5
  79. package/dist/atoms/org/customerService.d.cts +6 -6
  80. package/dist/atoms/org/customerService.d.ts +6 -6
  81. package/dist/atoms/org/graphqlConfig.d.cts +4 -4
  82. package/dist/atoms/org/graphqlConfig.d.ts +4 -4
  83. package/dist/atoms/org/index.cjs +1 -1
  84. package/dist/atoms/org/index.js +1 -1
  85. package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
  86. package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
  87. package/dist/atoms/org/orgAnalyticsConfig.d.cts +5 -5
  88. package/dist/atoms/org/orgAnalyticsConfig.d.ts +5 -5
  89. package/dist/atoms/search/chatSearch.cjs +1 -1
  90. package/dist/atoms/search/chatSearch.d.cts +17 -17
  91. package/dist/atoms/search/chatSearch.d.ts +17 -17
  92. package/dist/atoms/search/chatSearch.js +1 -1
  93. package/dist/atoms/search/searchAPI.d.cts +13 -13
  94. package/dist/atoms/search/searchAPI.d.ts +13 -13
  95. package/dist/atoms/search/types.d.cts +1 -1
  96. package/dist/atoms/search/types.d.ts +1 -1
  97. package/dist/atoms/search/utils.d.cts +1 -1
  98. package/dist/atoms/search/utils.d.ts +1 -1
  99. package/dist/atoms/widget/chatPreviewLoading.d.cts +2 -2
  100. package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
  101. package/dist/config/locators/components/search/index.d.cts +1 -1
  102. package/dist/contexts/amplitudeContext/amplitudeContext.cjs +9 -7
  103. package/dist/contexts/amplitudeContext/amplitudeContext.d.cts +2 -2
  104. package/dist/contexts/amplitudeContext/amplitudeContext.d.ts +2 -2
  105. package/dist/contexts/amplitudeContext/amplitudeContext.js +5 -3
  106. package/dist/contexts/amplitudeContext/index.cjs +1 -0
  107. package/dist/contexts/amplitudeContext/index.d.cts +2 -2
  108. package/dist/contexts/amplitudeContext/index.d.ts +2 -2
  109. package/dist/contexts/amplitudeContext/index.js +2 -2
  110. package/dist/contexts/cdnContext/cdnContext.cjs +3 -3
  111. package/dist/contexts/enviveConfigContext/enviveConfigContext.cjs +6 -5
  112. package/dist/contexts/enviveConfigContext/enviveConfigContext.d.cts +2 -1
  113. package/dist/contexts/enviveConfigContext/enviveConfigContext.d.ts +2 -1
  114. package/dist/contexts/enviveConfigContext/enviveConfigContext.js +3 -2
  115. package/dist/contexts/enviveContext/WindowChatToggleBinder.cjs +4 -3
  116. package/dist/contexts/enviveContext/WindowChatToggleBinder.js +4 -3
  117. package/dist/contexts/enviveContext/enviveContext.cjs +23 -16
  118. package/dist/contexts/enviveContext/enviveContext.d.cts +5 -1
  119. package/dist/contexts/enviveContext/enviveContext.d.ts +5 -1
  120. package/dist/contexts/enviveContext/enviveContext.js +23 -16
  121. package/dist/contexts/enviveContext/types.d.cts +1 -1
  122. package/dist/contexts/enviveContext/types.d.ts +1 -1
  123. package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.cjs +23 -2
  124. package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.cts +11 -2
  125. package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.d.ts +11 -2
  126. package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.js +23 -2
  127. package/dist/contexts/featureFlagServiceContext/index.d.cts +2 -2
  128. package/dist/contexts/featureFlagServiceContext/index.d.ts +2 -2
  129. package/dist/contexts/graphqlContext/graphqlContext.cjs +7 -7
  130. package/dist/contexts/graphqlContext/graphqlContext.d.cts +1 -1
  131. package/dist/contexts/graphqlContext/graphqlContext.d.ts +1 -1
  132. package/dist/contexts/graphqlContext/graphqlContext.js +3 -3
  133. package/dist/contexts/graphqlContext/mockV3Config.cjs +10 -4
  134. package/dist/contexts/graphqlContext/mockV3Config.js +10 -4
  135. package/dist/contexts/hardcopyContext/hardcopyContext.cjs +60 -6
  136. package/dist/contexts/hardcopyContext/hardcopyContext.d.cts +8 -3
  137. package/dist/contexts/hardcopyContext/hardcopyContext.d.ts +8 -3
  138. package/dist/contexts/hardcopyContext/hardcopyContext.js +58 -4
  139. package/dist/contexts/newOrgConfigContext/newOrgConfigContext.cjs +3 -3
  140. package/dist/contexts/pageContext/mapping.cjs +6 -2
  141. package/dist/contexts/pageContext/mapping.js +6 -2
  142. package/dist/contexts/pageContext/pageContext.cjs +12 -7
  143. package/dist/contexts/pageContext/pageContext.js +10 -5
  144. package/dist/contexts/pageContext/types.d.cts +7 -3
  145. package/dist/contexts/pageContext/types.d.ts +7 -3
  146. package/dist/contexts/salesAgentContext/chatAPI.cjs +101 -12
  147. package/dist/contexts/salesAgentContext/chatAPI.d.cts +8 -4
  148. package/dist/contexts/salesAgentContext/chatAPI.d.ts +8 -4
  149. package/dist/contexts/salesAgentContext/chatAPI.js +103 -14
  150. package/dist/contexts/salesAgentContext/salesAgentContext.cjs +1 -1
  151. package/dist/contexts/salesAgentContext/salesAgentContext.js +1 -1
  152. package/dist/contexts/salesAgentContext/salesAgentService.cjs +52 -10
  153. package/dist/contexts/salesAgentContext/salesAgentService.js +50 -8
  154. package/dist/contexts/searchContext/searchContext.cjs +4 -4
  155. package/dist/contexts/searchContext/searchContext.js +1 -1
  156. package/dist/contexts/systemSettingsContext/systemSettingsContext.cjs +3 -3
  157. package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
  158. package/dist/contexts/types.cjs +1 -1
  159. package/dist/contexts/types.d.cts +10 -3
  160. package/dist/contexts/types.d.ts +10 -3
  161. package/dist/contexts/types.js +1 -1
  162. package/dist/contexts/typesV3.cjs +2 -1
  163. package/dist/contexts/typesV3.d.cts +28 -7
  164. package/dist/contexts/typesV3.d.ts +26 -5
  165. package/dist/contexts/typesV3.js +2 -1
  166. package/dist/contexts/userIdentityContext/userIdentityContext.cjs +3 -3
  167. package/dist/contexts/widgetConfigContext/widgetConfigContext.cjs +1 -1
  168. package/dist/contexts/widgetConfigContext/widgetConfigContext.js +1 -1
  169. package/dist/hooks/AppDetails/useAppDetails.cjs +6 -6
  170. package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
  171. package/dist/hooks/ImageResolver/useImageResolver.cjs +3 -3
  172. package/dist/hooks/Search/useSearch.cjs +4 -4
  173. package/dist/hooks/Search/useSearch.d.cts +1 -1
  174. package/dist/hooks/Search/useSearch.d.ts +1 -1
  175. package/dist/hooks/Search/useSearch.js +1 -1
  176. package/dist/services/amplitudeService/amplitudeService.cjs +49 -11
  177. package/dist/services/amplitudeService/amplitudeService.d.cts +18 -3
  178. package/dist/services/amplitudeService/amplitudeService.d.ts +18 -3
  179. package/dist/services/amplitudeService/amplitudeService.js +49 -12
  180. package/dist/services/amplitudeService/index.cjs +1 -0
  181. package/dist/services/amplitudeService/index.d.cts +2 -2
  182. package/dist/services/amplitudeService/index.d.ts +2 -2
  183. package/dist/services/amplitudeService/index.js +2 -2
  184. package/dist/types/customerService.d.cts +3 -1
  185. package/dist/types/customerService.d.ts +1 -1
  186. package/dist/types/enviveConfig.d.cts +4 -1
  187. package/dist/types/enviveConfig.d.ts +4 -1
  188. package/package.json +10 -2
  189. package/src/application/commerce-api.ts +25 -20
  190. package/src/application/models/api/widgetText.ts +2 -0
  191. package/src/application/models/api/widgetTextRequest.ts +2 -0
  192. package/src/application/models/chatElementDisplayLocationV3.ts +3 -0
  193. package/src/application/models/variantInfo/variantInfo.ts +5 -0
  194. package/src/application/utils/nodeSelector.ts +15 -14
  195. package/src/application/utils/widgetTextFromApiWidgetTextResponse.ts +2 -0
  196. package/src/application/utils/widgetTextRequestToApiRequest.ts +5 -0
  197. package/src/atoms/app/variant.ts +56 -2
  198. package/src/contexts/amplitudeContext/__tests__/amplitudeContext.test.tsx +2 -0
  199. package/src/contexts/amplitudeContext/amplitudeContext.tsx +5 -1
  200. package/src/contexts/enviveConfigContext/enviveConfigContext.tsx +3 -0
  201. package/src/contexts/enviveContext/WindowChatToggleBinder.tsx +5 -1
  202. package/src/contexts/enviveContext/enviveContext.tsx +10 -3
  203. package/src/contexts/featureFlagServiceContext/featureFlagServiceContext.tsx +36 -0
  204. package/src/contexts/graphqlContext/mockV3Config.ts +13 -7
  205. package/src/contexts/hardcopyContext/hardcopyContext.tsx +67 -4
  206. package/src/contexts/pageContext/mapping.ts +13 -1
  207. package/src/contexts/pageContext/pageContext.tsx +9 -4
  208. package/src/contexts/pageContext/types.ts +7 -1
  209. package/src/contexts/salesAgentContext/chatAPI.ts +105 -13
  210. package/src/contexts/salesAgentContext/salesAgentService.ts +66 -4
  211. package/src/contexts/types.ts +11 -2
  212. package/src/contexts/typesV3.ts +42 -16
  213. package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +80 -0
  214. package/src/services/amplitudeService/amplitudeService.ts +55 -11
  215. package/src/types/enviveConfig.ts +3 -0
@@ -1,5 +1,5 @@
1
1
  // This component will interact with the backend API to get the responses from the sales agent.
2
-
2
+ import Logger from 'src/application/logging/logger';
3
3
  import { PageVisitCategory, UserEventCategory } from '@spiffy-ai/commerce-api-client';
4
4
  import {
5
5
  ChatElementDisplayLocationV3,
@@ -9,13 +9,21 @@ import {
9
9
  Suggestion,
10
10
  UserEvent,
11
11
  } from 'src/application/models';
12
- import { useSetAtom } from 'jotai';
12
+ import { useAtomValue, useSetAtom } from 'jotai';
13
13
  import { useCallback } from 'react';
14
14
  import { v4 as uuid } from 'uuid';
15
15
  import { formSubmitAtom, replyEventCategoryAtom } from 'src/atoms/chat';
16
16
  import { queueUserEventAtom } from 'src/atoms/chat/messageQueue';
17
+ import { EnviveMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
18
+ import { analyticsContextAtom } from 'src/atoms/app/variant';
19
+ import { hasParsedVariantInfoAtom } from 'src/atoms/app';
17
20
  import { SpiffyMetricsEventName, useAmplitude } from '../amplitudeContext';
18
21
 
22
+ const TRACKED_USER_EVENTS = [
23
+ UserEventCategory.PageVisit,
24
+ UserEventCategory.PdpVisit,
25
+ UserEventCategory.PlpVisit,
26
+ ];
19
27
  export interface SalesAgentChatAPI {
20
28
  logPageVisit: ({ pageVisitCategory }: { pageVisitCategory: PageVisitCategory }) => void;
21
29
  logUserEvent: (event: UserEvent) => void;
@@ -23,13 +31,23 @@ export interface SalesAgentChatAPI {
23
31
  suggestion: Suggestion,
24
32
  displayLocation: ChatElementDisplayLocationV3,
25
33
  ) => void;
26
- onTypedMessageSubmitted: ({ query, userTyped }: { query: string; userTyped: boolean }) => void;
27
- onFormResponseSubmitted: (formResponse: FormSubmittedAttributes) => void;
34
+ onTypedMessageSubmitted: ({
35
+ query,
36
+ userTyped,
37
+ displayLocation,
38
+ }: {
39
+ query: string;
40
+ userTyped: boolean;
41
+ displayLocation: ChatElementDisplayLocationV3;
42
+ }) => void;
43
+ onFormResponseSubmitted: (form: FormSubmittedAttributes) => void;
28
44
  }
29
45
 
30
46
  export const useSalesAgentChatAPI = () => {
31
47
  // TODO: Each of these functions will trigger both the necessary amplitude events and initiate the
32
48
  // necessary actions to trigger the backend API
49
+ const context = useAtomValue(analyticsContextAtom);
50
+ const hasParsedVariantInfo = useAtomValue(hasParsedVariantInfoAtom);
33
51
  const queueUserEvent = useSetAtom(queueUserEventAtom);
34
52
  const setReplyEventCategory = useSetAtom(replyEventCategoryAtom);
35
53
  const setFormSubmit = useSetAtom(formSubmitAtom);
@@ -37,8 +55,17 @@ export const useSalesAgentChatAPI = () => {
37
55
 
38
56
  const logPageVisit = useCallback(
39
57
  ({ pageVisitCategory }: { pageVisitCategory: PageVisitCategory }) => {
58
+ const eventId = uuid();
59
+ trackEvent({
60
+ eventName: EnviveMetricsEventName.ChatRequest,
61
+ eventProps: {
62
+ ...context,
63
+ 'chat.request_id': eventId,
64
+ 'chat.request_type': 'page_visit',
65
+ },
66
+ });
40
67
  const event: UserEvent = {
41
- eventId: uuid(),
68
+ eventId,
42
69
  category: UserEventCategory.PageVisit,
43
70
  createdAt: new Date().toISOString(),
44
71
  attributes: {
@@ -48,14 +75,32 @@ export const useSalesAgentChatAPI = () => {
48
75
  };
49
76
  queueUserEvent(event);
50
77
  },
51
- [queueUserEvent],
78
+ [queueUserEvent, context, trackEvent],
52
79
  );
53
80
 
54
81
  const logUserEvent = useCallback(
55
82
  (event: UserEvent) => {
83
+ if (!hasParsedVariantInfo) {
84
+ Logger.logWarn(
85
+ '[envive-ai] hasParsedVariantInfo is false, not logging user event',
86
+ undefined,
87
+ { event },
88
+ );
89
+ return;
90
+ }
91
+ if (TRACKED_USER_EVENTS.includes(event.category)) {
92
+ trackEvent({
93
+ eventName: EnviveMetricsEventName.ChatRequest,
94
+ eventProps: {
95
+ ...context,
96
+ 'chat.request_id': event.eventId,
97
+ 'chat.request_type': 'page_visit',
98
+ },
99
+ });
100
+ }
56
101
  queueUserEvent(event);
57
102
  },
58
- [queueUserEvent],
103
+ [context, hasParsedVariantInfo, queueUserEvent, trackEvent],
59
104
  );
60
105
  const onSuggestionClicked = useCallback(
61
106
  (suggestion: Suggestion, displayLocation: ChatElementDisplayLocationV3) => {
@@ -74,8 +119,24 @@ export const useSalesAgentChatAPI = () => {
74
119
  },
75
120
  },
76
121
  });
122
+ const eventId = uuid();
123
+ trackEvent({
124
+ eventName: EnviveMetricsEventName.ChatRequest,
125
+ eventProps: {
126
+ ...context,
127
+ 'trigger.widget': displayLocation, // This is fairly finegrained, but that may be ok
128
+ 'chat.request_id': eventId,
129
+ 'chat.request_type': 'suggestion',
130
+ 'chat.request_text': suggestion.content,
131
+ 'chat.suggestion_id': suggestion.id,
132
+ 'chat.suggestion_category': suggestion.category,
133
+ 'chat.suggestion_created_at': suggestion.createdAt,
134
+ 'chat.suggestion_is_answer': suggestion.isAnswer,
135
+ 'chat.user_typed': false,
136
+ },
137
+ });
77
138
  const event: UserEvent = {
78
- eventId: uuid(),
139
+ eventId,
79
140
  category: UserEventCategory.SuggestionClicked,
80
141
  createdAt: new Date().toISOString(),
81
142
  attributes: {
@@ -85,10 +146,18 @@ export const useSalesAgentChatAPI = () => {
85
146
  };
86
147
  queueUserEvent(event);
87
148
  },
88
- [queueUserEvent, trackEvent],
149
+ [queueUserEvent, trackEvent, context],
89
150
  );
90
151
  const onTypedMessageSubmitted = useCallback(
91
- ({ query, userTyped }: { query: string; userTyped: boolean }) => {
152
+ ({
153
+ query,
154
+ userTyped,
155
+ displayLocation,
156
+ }: {
157
+ query: string;
158
+ userTyped: boolean;
159
+ displayLocation: ChatElementDisplayLocationV3;
160
+ }) => {
92
161
  const eventId = uuid();
93
162
  trackEvent({
94
163
  eventName: SpiffyMetricsEventName.ChatUserMessageInput,
@@ -105,6 +174,18 @@ export const useSalesAgentChatAPI = () => {
105
174
  },
106
175
  },
107
176
  });
177
+ trackEvent({
178
+ eventName: EnviveMetricsEventName.ChatRequest,
179
+ eventProps: {
180
+ ...context,
181
+ 'trigger.widget': displayLocation,
182
+ 'chat.request_type': 'text',
183
+ 'chat.request_text': query,
184
+ 'chat.request_id': eventId,
185
+ 'chat.request_created_at': new Date().toISOString(),
186
+ 'chat.user_typed': userTyped,
187
+ },
188
+ });
108
189
  const event: UserEvent = {
109
190
  eventId,
110
191
  category: UserEventCategory.QueryTyped,
@@ -116,21 +197,32 @@ export const useSalesAgentChatAPI = () => {
116
197
  };
117
198
  queueUserEvent(event);
118
199
  },
119
- [queueUserEvent, trackEvent],
200
+ [queueUserEvent, trackEvent, context],
120
201
  );
121
202
  const onFormResponseSubmitted = useCallback(
122
203
  (form: FormSubmittedAttributes) => {
123
204
  setReplyEventCategory(UserEventCategory.FormSubmitted);
124
205
  setFormSubmit(form);
206
+ const eventId = uuid();
207
+ trackEvent({
208
+ eventName: EnviveMetricsEventName.ChatRequest,
209
+ eventProps: {
210
+ ...context,
211
+ 'chat.request_id': eventId,
212
+ 'chat.request_type': 'form',
213
+ 'chat.form_response_id': form.formResponseId,
214
+ 'chat.form_type': form.formType,
215
+ },
216
+ });
125
217
  const event: UserEvent = {
126
- eventId: uuid(),
218
+ eventId,
127
219
  category: UserEventCategory.FormSubmitted,
128
220
  createdAt: new Date().toISOString(),
129
221
  attributes: form,
130
222
  };
131
223
  queueUserEvent(event);
132
224
  },
133
- [queueUserEvent, setReplyEventCategory, setFormSubmit],
225
+ [setReplyEventCategory, setFormSubmit, trackEvent, context, queueUserEvent],
134
226
  );
135
227
 
136
228
  return {
@@ -25,6 +25,8 @@ import { useMessageInterceptor } from 'src/interceptors/useMessageInterceptor';
25
25
  import { SpiffyMetricsEventName, useAmplitude } from 'src/contexts/amplitudeContext';
26
26
  import { useFeatureFlagService } from 'src/contexts/featureFlagServiceContext';
27
27
  import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
28
+ import { EnviveMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
29
+ import { analyticsContextAtom } from 'src/atoms/app/variant';
28
30
 
29
31
  import { StatusCodeError } from './statusCodeError';
30
32
 
@@ -59,6 +61,41 @@ const inputPropsToTrackingProps = (
59
61
  return {};
60
62
  };
61
63
 
64
+ const inputPropsToEnviveTrackingProps = (
65
+ payload: NextMessageRequest,
66
+ ): Record<string, unknown> | undefined => {
67
+ const [userEvent] = payload.userEvents || [];
68
+ if (userEvent.category === UserEventCategory.SuggestionClicked) {
69
+ return {
70
+ 'chat.request_type': 'suggestion',
71
+ 'chat.request_id': userEvent.eventId,
72
+ 'chat.request_text': userEvent.attributes.content,
73
+ 'chat.suggestion_id': userEvent.attributes.suggestionId,
74
+ 'chat.user_typed': false,
75
+ };
76
+ }
77
+ if (userEvent.category === UserEventCategory.QueryTyped) {
78
+ return {
79
+ 'chat.request_type': 'text',
80
+ 'chat.request_id': userEvent.eventId,
81
+ 'chat.request_text': userEvent.attributes.query,
82
+ 'chat.user_typed': userEvent.attributes.userTyped,
83
+ };
84
+ }
85
+ if (
86
+ userEvent.category === UserEventCategory.PageVisit ||
87
+ userEvent.category === UserEventCategory.PdpVisit ||
88
+ userEvent.category === UserEventCategory.PlpVisit
89
+ ) {
90
+ return {
91
+ 'chat.request_id': userEvent.eventId,
92
+ 'chat.request_type': 'page_visit',
93
+ };
94
+ }
95
+
96
+ return {};
97
+ };
98
+
62
99
  export const getQueryParam = (key: string): string | null => {
63
100
  const urlObj = new URL(window.location.href);
64
101
  return urlObj.searchParams.get(key);
@@ -137,14 +174,15 @@ const processStreamingResponse = async (
137
174
  messageInterceptor: { intercept: (response?: Response) => boolean | undefined },
138
175
  setMessages: (updater: (prev: Message[][]) => Message[][]) => void,
139
176
  setResponseStreaming: (responseStreaming: boolean) => void,
140
- ): Promise<void> => {
177
+ ): Promise<Record<string, number>> => {
141
178
  let lastMessage: Message | undefined;
142
179
  setResponseStreaming(true);
180
+ const responseAnalytics: Record<string, number> = {};
143
181
 
144
182
  for await (const response of stream) {
145
183
  try {
146
184
  if (messageInterceptor.intercept(response)) {
147
- return;
185
+ return {};
148
186
  }
149
187
 
150
188
  if (!response) {
@@ -155,6 +193,14 @@ const processStreamingResponse = async (
155
193
  if (!message) {
156
194
  throw new Error('Failed to transform API response to client message');
157
195
  }
196
+ if (message.type === MessageType.Product) {
197
+ responseAnalytics['chat.product_cards_returned'] =
198
+ (responseAnalytics['chat.product_cards_returned'] || 0) + 1;
199
+ }
200
+ if (message.type === MessageType.Review) {
201
+ responseAnalytics['chat.review_cards_returned'] =
202
+ (responseAnalytics['chat.review_cards_returned'] || 0) + 1;
203
+ }
158
204
 
159
205
  // No support for ChatSearch messages at the current time.
160
206
  // Perhaps we add support back in the future
@@ -173,6 +219,7 @@ const processStreamingResponse = async (
173
219
  }
174
220
  setResponseStreaming(false);
175
221
  }
222
+ return responseAnalytics;
176
223
  };
177
224
 
178
225
  export const useSalesAgentService: () => SalesAgentService = () => {
@@ -181,6 +228,7 @@ export const useSalesAgentService: () => SalesAgentService = () => {
181
228
  const setSuggestions = useSetAtom(suggestionsAtom);
182
229
  const setPendingResponse = useSetAtom(pendingResponseAtom);
183
230
  const setResponseStreaming = useSetAtom(responseStreamingAtom);
231
+ const analyticsContext = useAtomValue(analyticsContextAtom);
184
232
  const messageInterceptor = useMessageInterceptor();
185
233
  const { trackEvent } = useAmplitude();
186
234
 
@@ -220,12 +268,13 @@ export const useSalesAgentService: () => SalesAgentService = () => {
220
268
  const startTime = Date.now();
221
269
  let successfulResponse: boolean | undefined;
222
270
  setPendingResponse(true);
271
+ setSuggestions([]);
223
272
  const stream = CommerceApiClient.getNextResponseStreaming(payload);
224
-
273
+ let responseAnalytics: Record<string, unknown> | undefined;
225
274
  try {
226
275
  setRequestFailure(false);
227
276
 
228
- await processStreamingResponse(
277
+ responseAnalytics = await processStreamingResponse(
229
278
  stream,
230
279
  messageInterceptor,
231
280
  setMessages,
@@ -274,14 +323,27 @@ export const useSalesAgentService: () => SalesAgentService = () => {
274
323
  ...inputPropsToTrackingProps(payload),
275
324
  },
276
325
  });
326
+ trackEvent({
327
+ eventName: EnviveMetricsEventName.ChatResponse,
328
+ eventProps: {
329
+ ...analyticsContext,
330
+ 'chat.response_time_ms': responseTime.toString(),
331
+ ...inputPropsToEnviveTrackingProps(payload),
332
+ ...responseAnalytics,
333
+ },
334
+ });
277
335
  setPendingResponse(false);
278
336
  setResponseStreaming(false);
279
337
  // logPerfMetric(PerfMetricsEvents.FirstResponseCompleted);
280
338
  }
281
339
  },
282
340
  [
341
+ analyticsContext,
342
+ trackEvent,
343
+ setResponseStreaming,
283
344
  // logPerfMetric,
284
345
  setRequestFailure,
346
+ setSuggestions,
285
347
  messageInterceptor,
286
348
  setMessages,
287
349
  setPendingResponse,
@@ -13,7 +13,8 @@ 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 { WidgetConfigV3 } from './typesV3';
16
+ import { WidgetConfigV3, WidgetTypeV3 } from './typesV3';
17
+ import type { HardcopyResponse } from './hardcopyContext/hardcopyContext';
17
18
 
18
19
  type ShowFloatingButtonOptions =
19
20
  | 'always'
@@ -863,6 +864,7 @@ type PageVariantBase = {
863
864
  variantId: string;
864
865
  widgetMounting: WidgetMounting[];
865
866
  variantTests: PageVariantTest[];
867
+ floatingButtonOverride?: ShowFloatingButtonOptions;
866
868
  };
867
869
 
868
870
  type PLPPageVariant = PageVariantBase & {
@@ -892,13 +894,18 @@ type SearchPageVariant = PageVariantBase & {
892
894
  variantType: 'search';
893
895
  };
894
896
 
897
+ type FullPageSalesAgentVariant = PageVariantBase & {
898
+ variantType: 'full_page';
899
+ };
900
+
895
901
  export type PageVariantConfig =
896
902
  | PLPPageVariant
897
903
  | PDPPageVariant
898
904
  | VisitPageVariant
899
905
  | OtherPageVariant
900
906
  | HomePageVariant
901
- | SearchPageVariant;
907
+ | SearchPageVariant
908
+ | FullPageSalesAgentVariant;
902
909
 
903
910
  export type OrgPageConfig = {
904
911
  // pageVariants is a list of page variants that we support.
@@ -928,8 +935,10 @@ export interface EnviveConfig {
928
935
  variantUrlOverride?: string;
929
936
  variantInfoOverride?: any;
930
937
  show?: boolean;
938
+ enviveOn?: boolean;
931
939
  publicKey?: string;
932
940
  featureGates?: OrgConfigFeatureGate[];
941
+ hardcopyOverride?: Partial<Record<WidgetTypeV3, HardcopyResponse>>;
933
942
  }
934
943
 
935
944
  export type {
@@ -1,11 +1,11 @@
1
- import { AnimationSpeed, PromptCarouselRows } from '@envive-ai/react-toolkit-v3/PromptCarousel';
1
+ import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
2
2
  import { ImageGalleryImage, ImageGalleryLayout } from '@envive-ai/react-toolkit-v3/ImageGallery';
3
3
  import { PromptButtonVariant } from '@envive-ai/react-toolkit-v3/PromptButton';
4
- import { WidgetWrapperVariant } from '@envive-ai/react-toolkit-v3/WidgetWrapper';
5
- import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
4
+ import { AnimationSpeed, PromptCarouselRows } from '@envive-ai/react-toolkit-v3/PromptCarousel';
6
5
  import { DynamicLayout, WidgetKind } from '@envive-ai/react-toolkit-v3/SocialProof';
6
+ import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
7
7
  import { SparkleIconColor } from '@envive-ai/react-toolkit-v3/WelcomeMessage';
8
- import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
8
+ import { WidgetWrapperVariant } from '@envive-ai/react-toolkit-v3/WidgetWrapper';
9
9
  import { ColorNames } from '../application/models/colorsConfigV3';
10
10
  import { CustomerServiceType } from '../types/customerService';
11
11
  import type { SearchConfig } from './types';
@@ -184,6 +184,23 @@ type FloatingButtonConfig = {
184
184
  style: Style;
185
185
  };
186
186
 
187
+ export type ReferrerPopupTrigger = {
188
+ type: 'referrer';
189
+ referrers: string[];
190
+ };
191
+
192
+ export type AutomaticPopupTrigger = {
193
+ type: 'automatic';
194
+ };
195
+ export type PopupTrigger = ReferrerPopupTrigger | AutomaticPopupTrigger;
196
+ export type AutoPopupTrigger = {
197
+ delay: number;
198
+ trigger: PopupTrigger;
199
+ };
200
+ export interface AutoPopupConfig {
201
+ triggers: AutoPopupTrigger[];
202
+ }
203
+
187
204
  type FloatingChatConfig = {
188
205
  headerBackgroundColor?: string;
189
206
  headerMode: Mode;
@@ -196,6 +213,7 @@ type FloatingChatConfig = {
196
213
  welcomeMessageTitle: string;
197
214
  welcomeMessageText: string;
198
215
  chatFooterTextFieldPlaceholderText: string;
216
+ autoPopupConfig?: AutoPopupConfig;
199
217
  };
200
218
 
201
219
  type CustomerServiceIntegration = {
@@ -246,6 +264,7 @@ export enum WidgetTypeV3 {
246
264
  FloatingChatV3 = 'FloatingChatV3',
247
265
  FloatingButtonV3 = 'FloatingButtonV3',
248
266
  ProductCardV3 = 'ProductCardV3',
267
+ FullPageSalesAgentV3 = 'FullPageSalesAgentV3',
249
268
  }
250
269
 
251
270
  interface BaseWidgetConfig<T extends WidgetTypeV3> {
@@ -324,6 +343,11 @@ interface ProductCardWidgetV3Config extends BaseWidgetConfig<WidgetTypeV3.Produc
324
343
  imageSrc?: string;
325
344
  }
326
345
 
346
+ interface FullPageSalesAgentWidgetV3Config extends BaseWidgetConfig<WidgetTypeV3.FullPageSalesAgentV3> {
347
+ headerContainer?: string;
348
+ autoHeight?: boolean;
349
+ }
350
+
327
351
  type WidgetConfigV3 =
328
352
  | PromptCarouselWidgetV3Config
329
353
  | SocialProofWidgetV3Config
@@ -334,24 +358,26 @@ type WidgetConfigV3 =
334
358
  | ChatPreviewWidgetV3Config
335
359
  | PromptButtonCarouselWithImageWidgetV3Config
336
360
  | FloatingChatWidgetV3Config
337
- | ProductCardWidgetV3Config;
361
+ | ProductCardWidgetV3Config
362
+ | FullPageSalesAgentWidgetV3Config;
338
363
 
339
364
  export type {
340
- OrgUIConfigV3,
341
- WidgetConfigV3,
342
- LookAndFeelConfig,
343
- SocialProofWidgetV3Config,
344
- PromptCarouselWidgetV3Config,
345
365
  ChatPreviewComparisonWidgetV3Config,
346
366
  ChatPreviewLoadingWidgetV3Config,
347
367
  ChatPreviewWidgetV3Config,
348
- TitledPromptCarouselWidgetV3Config,
349
- TypingAnimationWidgetV3Config,
350
- PromptButtonCarouselWithImageWidgetV3Config,
368
+ CustomerServiceIntegration,
369
+ FloatingButtonConfig,
370
+ FloatingChatConfig,
351
371
  FloatingChatWidgetV3Config,
372
+ FullPageSalesAgentWidgetV3Config,
373
+ LookAndFeelConfig,
374
+ OrgUIConfigV3,
352
375
  ProductCardWidgetV3Config,
353
- FloatingChatConfig,
354
- FloatingButtonConfig,
355
- CustomerServiceIntegration,
376
+ PromptButtonCarouselWithImageWidgetV3Config,
377
+ PromptCarouselWidgetV3Config,
378
+ SocialProofWidgetV3Config,
356
379
  SPASettings,
380
+ TitledPromptCarouselWidgetV3Config,
381
+ TypingAnimationWidgetV3Config,
382
+ WidgetConfigV3,
357
383
  };
@@ -66,6 +66,23 @@ describe('AmplitudeService', () => {
66
66
  mockAdd.mockClear();
67
67
  mockDigest.mockResolvedValue(new Uint8Array(32).fill(0));
68
68
  mockDispatch.mockClear();
69
+
70
+ // Mock window.location for tests that need it
71
+ Object.defineProperty(global, 'window', {
72
+ value: {
73
+ location: {
74
+ href: 'https://test.example.com',
75
+ },
76
+ localStorage: {
77
+ getItem: vi.fn(),
78
+ },
79
+ performance: {
80
+ now: vi.fn(() => Date.now()),
81
+ },
82
+ },
83
+ writable: true,
84
+ configurable: true,
85
+ });
69
86
  });
70
87
 
71
88
  const createService = (overrides?: Partial<AmplitudeServiceConfig>) => {
@@ -76,6 +93,7 @@ describe('AmplitudeService', () => {
76
93
  env: 'test',
77
94
  orgId: 'test-org-id',
78
95
  show: true,
96
+ enviveOn: true,
79
97
  contextSource: 'app',
80
98
  orgShortName: 'test-org',
81
99
  featureFlagService,
@@ -108,6 +126,7 @@ describe('AmplitudeService', () => {
108
126
  env: 'test',
109
127
  orgId: 'test-org-id',
110
128
  show: true,
129
+ enviveOn: true,
111
130
  getLocalStorageItem: mockGetLocalStorageItem,
112
131
  contextSource: 'app',
113
132
  orgShortName: 'test-org',
@@ -125,6 +144,7 @@ describe('AmplitudeService', () => {
125
144
  env: 'test',
126
145
  orgId: 'test-org-id',
127
146
  show: true,
147
+ enviveOn: true,
128
148
  getLocalStorageItem: mockGetLocalStorageItem,
129
149
  contextSource: 'app',
130
150
  orgShortName: 'test-org',
@@ -142,6 +162,7 @@ describe('AmplitudeService', () => {
142
162
  env: 'test',
143
163
  orgId: 'test-org-id',
144
164
  show: true,
165
+ enviveOn: true,
145
166
  getLocalStorageItem: mockGetLocalStorageItem,
146
167
  contextSource: 'app',
147
168
  orgShortName: 'test-org',
@@ -209,6 +230,7 @@ describe('AmplitudeService', () => {
209
230
  eventProps: { message: 'test', count: 5 },
210
231
  });
211
232
 
233
+ expect(mockTrack).toHaveBeenCalled();
212
234
  const callArgs = mockTrack.mock.calls[0];
213
235
  const eventProps = callArgs[1];
214
236
 
@@ -238,6 +260,7 @@ describe('AmplitudeService', () => {
238
260
  eventGroups: { group1: 'value1', group2: 'value2' },
239
261
  });
240
262
 
263
+ expect(mockTrack).toHaveBeenCalled();
241
264
  const callArgs = mockTrack.mock.calls[0];
242
265
  expect(callArgs[2]).toHaveProperty('group1', 'value1');
243
266
  expect(callArgs[2]).toHaveProperty('group2', 'value2');
@@ -251,6 +274,7 @@ describe('AmplitudeService', () => {
251
274
  env: 'test',
252
275
  orgId: 'test-org-id',
253
276
  show: true,
277
+ enviveOn: true,
254
278
  getLocalStorageItem: mockGetLocalStorageItem,
255
279
  contextSource: 'app',
256
280
  orgShortName: 'test-org',
@@ -304,6 +328,15 @@ describe('AmplitudeService', () => {
304
328
  const dataLayer: any[] = [];
305
329
  Object.defineProperty(global, 'window', {
306
330
  value: {
331
+ location: {
332
+ href: 'https://test.example.com',
333
+ },
334
+ localStorage: {
335
+ getItem: vi.fn(),
336
+ },
337
+ performance: {
338
+ now: vi.fn(() => Date.now()),
339
+ },
307
340
  dataLayer,
308
341
  },
309
342
  writable: true,
@@ -391,6 +424,7 @@ describe('AmplitudeService', () => {
391
424
  env: 'test',
392
425
  orgId: 'test-org-id',
393
426
  show: true,
427
+ enviveOn: true,
394
428
  getLocalStorageItem: null,
395
429
  contextSource: 'app',
396
430
  orgShortName: 'test-org',
@@ -408,7 +442,13 @@ describe('AmplitudeService', () => {
408
442
  };
409
443
  Object.defineProperty(global, 'window', {
410
444
  value: {
445
+ location: {
446
+ href: 'https://test.example.com',
447
+ },
411
448
  localStorage: mockLocalStorage,
449
+ performance: {
450
+ now: vi.fn(() => Date.now()),
451
+ },
412
452
  },
413
453
  writable: true,
414
454
  configurable: true,
@@ -418,7 +458,9 @@ describe('AmplitudeService', () => {
418
458
  eventName: SpiffyMetricsEventName.ChatUserMessageInput,
419
459
  });
420
460
 
461
+ // Check that getItem was called with ChatId (may be called with other keys too)
421
462
  expect(mockLocalStorage.getItem).toHaveBeenCalledWith(LocalStorageKeys.ChatId);
463
+ expect(mockTrack).toHaveBeenCalled();
422
464
  const callArgs = mockTrack.mock.calls[0];
423
465
  expect(callArgs[1]).toHaveProperty('chat_id', 'window-chat-id');
424
466
  });
@@ -448,6 +490,7 @@ describe('AmplitudeService', () => {
448
490
  eventName: SpiffyMetricsEventName.ChatUserMessageInput,
449
491
  });
450
492
 
493
+ expect(mockTrack).toHaveBeenCalled();
451
494
  const callArgs = mockTrack.mock.calls[0];
452
495
  const eventProps = callArgs[1];
453
496
  expect(eventProps).toHaveProperty('test', 'value');
@@ -456,6 +499,23 @@ describe('AmplitudeService', () => {
456
499
 
457
500
  describe('Event enrichment', () => {
458
501
  it('should enrich Page Viewed events', async () => {
502
+ // Ensure window.location is properly mocked
503
+ Object.defineProperty(global, 'window', {
504
+ value: {
505
+ location: {
506
+ href: 'https://test.example.com',
507
+ },
508
+ localStorage: {
509
+ getItem: vi.fn(),
510
+ },
511
+ performance: {
512
+ now: vi.fn(() => Date.now()),
513
+ },
514
+ },
515
+ writable: true,
516
+ configurable: true,
517
+ });
518
+
459
519
  createService({
460
520
  show: true,
461
521
  });
@@ -487,6 +547,23 @@ describe('AmplitudeService', () => {
487
547
  });
488
548
 
489
549
  it('should enrich Bundle Loaded events', async () => {
550
+ // Ensure window.location is properly mocked
551
+ Object.defineProperty(global, 'window', {
552
+ value: {
553
+ location: {
554
+ href: 'https://test.example.com',
555
+ },
556
+ localStorage: {
557
+ getItem: vi.fn(),
558
+ },
559
+ performance: {
560
+ now: vi.fn(() => Date.now()),
561
+ },
562
+ },
563
+ writable: true,
564
+ configurable: true,
565
+ });
566
+
490
567
  createService();
491
568
 
492
569
  const enrichmentCall = mockAdd.mock.calls[0];
@@ -541,6 +618,9 @@ describe('AmplitudeService', () => {
541
618
  it('should track multiple events with correct userId', async () => {
542
619
  const service = createService();
543
620
 
621
+ // Ensure the service is ready
622
+ expect(service.isReady).toBe(true);
623
+
544
624
  await service.trackEvent({
545
625
  eventName: SpiffyMetricsEventName.ChatUserMessageInput,
546
626
  eventProps: { event1: true },