@envive-ai/react-widgets-v3 0.3.21 → 0.3.23

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 (91) hide show
  1. package/dist/debug/chatEmbed.cjs +1 -1
  2. package/dist/debug/chatEmbed.js +1 -1
  3. package/dist/debug/reportIssue.cjs +1 -1
  4. package/dist/debug/reportIssue.js +1 -1
  5. package/dist/hocs/withBaseWidget/types.d.cts +5 -6
  6. package/dist/hocs/withBaseWidget/types.d.ts +5 -6
  7. package/dist/hocs/withBaseWidget/withBaseWidget.cjs +37 -43
  8. package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +2 -2
  9. package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +2 -2
  10. package/dist/hocs/withBaseWidget/withBaseWidget.js +39 -45
  11. package/dist/packages/widgets/dist/SearchResults/SearchResultsWidget.d.ts +2 -2
  12. package/dist/packages/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.ts +2 -2
  13. package/dist/packages/widgets/dist/SuggestionBar/SuggestionBar.d.ts +2 -2
  14. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.cjs +34 -8
  15. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +3 -3
  16. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts +3 -3
  17. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.js +35 -9
  18. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.cjs +13 -5
  19. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +3 -3
  20. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +3 -3
  21. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.js +13 -5
  22. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.cjs +32 -15
  23. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.cts +3 -3
  24. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +3 -3
  25. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.js +35 -18
  26. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +36 -32
  27. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +2 -2
  28. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +37 -33
  29. package/dist/widgets/FloatingChatWidget/hooks/useAutoPopup.cjs +2 -2
  30. package/dist/widgets/FloatingChatWidget/hooks/useAutoPopup.js +2 -2
  31. package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.cjs +7 -1
  32. package/dist/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.js +7 -1
  33. package/dist/widgets/ProductCardWidget/ProductCardWidget.cjs +31 -15
  34. package/dist/widgets/ProductCardWidget/ProductCardWidget.d.cts +2 -2
  35. package/dist/widgets/ProductCardWidget/ProductCardWidget.d.ts +2 -2
  36. package/dist/widgets/ProductCardWidget/ProductCardWidget.js +33 -17
  37. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.cjs +33 -16
  38. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.cts +3 -3
  39. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.d.ts +3 -3
  40. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.js +36 -19
  41. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.cjs +3 -12
  42. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +2 -2
  43. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.ts +2 -2
  44. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.js +5 -14
  45. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +2 -2
  46. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +2 -2
  47. package/dist/widgets/SocialProofWidget/SocialProofWidget.cjs +34 -17
  48. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +3 -3
  49. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +3 -3
  50. package/dist/widgets/SocialProofWidget/SocialProofWidget.js +37 -20
  51. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.cjs +2 -11
  52. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +2 -2
  53. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +2 -2
  54. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.js +4 -13
  55. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +2 -2
  56. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +2 -2
  57. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.cjs +31 -14
  58. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +3 -3
  59. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.ts +3 -3
  60. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.js +34 -17
  61. package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +2 -2
  62. package/dist/widgets/dist/SearchZeroState/SearchZeroStateWidget.d.cts +2 -2
  63. package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +2 -2
  64. package/dist/widgets/hooks/useGetWidgetStatus.cjs +1 -1
  65. package/dist/widgets/hooks/useGetWidgetStatus.js +1 -1
  66. package/dist/widgets/utils/functions.cjs +1 -1
  67. package/dist/widgets/utils/functions.js +1 -1
  68. package/package.json +1 -1
  69. package/src/hocs/withBaseWidget/__tests__/withBaseWidget.test.tsx +68 -302
  70. package/src/hocs/withBaseWidget/types.ts +6 -6
  71. package/src/hocs/withBaseWidget/withBaseWidget.tsx +46 -61
  72. package/src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.tsx +33 -0
  73. package/src/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.tsx +9 -1
  74. package/src/widgets/ChatPreviewWidget/ChatPreviewWidget.tsx +34 -13
  75. package/src/widgets/ChatPreviewWidget/__tests__/ChatPreviewWidget.test.tsx +11 -5
  76. package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +56 -38
  77. package/src/widgets/FloatingChatWidget/__tests__/FloatingChatWidget.test.tsx +11 -5
  78. package/src/widgets/FullPageSalesAgentWidget/FullPageSalesAgentWidget.tsx +8 -0
  79. package/src/widgets/ProductCardWidget/ProductCardWidget.tsx +37 -15
  80. package/src/widgets/ProductCardWidget/__tests__/ProductCardWidget.test.tsx +12 -8
  81. package/src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.tsx +35 -13
  82. package/src/widgets/PromptButtonCarouselWithImageWidget/__tests__/PromptButtonCarouselWithImageWidget.test.tsx +19 -8
  83. package/src/widgets/PromptCarouselWidget/PromptCarouselWidget.tsx +1 -12
  84. package/src/widgets/PromptCarouselWidget/__tests__/PromptCarouselWidget.test.tsx +21 -8
  85. package/src/widgets/SocialProofWidget/SocialProofWidget.tsx +34 -12
  86. package/src/widgets/SocialProofWidget/__tests__/SocialProofWidget.test.tsx +19 -8
  87. package/src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.tsx +1 -12
  88. package/src/widgets/TitledPromptCarouselWidget/__tests__/TitledPromptCarouselWidget.test.tsx +19 -8
  89. package/src/widgets/TypingAnimationWidget/TypingAnimationWidget.tsx +37 -14
  90. package/src/widgets/TypingAnimationWidget/__tests__/TypingAnimationWidget.test.tsx +19 -8
  91. package/src/widgets/__tests__/trackEventCanary.test.ts +1 -1
@@ -1,13 +1,16 @@
1
- import { ChatElementDisplayLocationV3 } from '@envive-ai/react-hooks/application/models';
1
+ import {
2
+ ChatElementDisplayLocationV3,
3
+ FeatureGates,
4
+ } from '@envive-ai/react-hooks/application/models';
2
5
  import {
3
6
  EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
7
  useAmplitude,
6
8
  } from '@envive-ai/react-hooks/contexts/amplitudeContext';
7
9
  import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
8
10
  import { ProductCardWidgetV3Config, WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
9
11
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
10
12
  import { useWidgetInteraction } from '@envive-ai/react-hooks/hooks/WidgetInteraction';
13
+ import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
11
14
  import {
12
15
  WidgetInteractionComponent,
13
16
  WidgetInteractionType,
@@ -16,7 +19,7 @@ import { ProductCard } from '@envive-ai/react-toolkit-v3/ProductCard';
16
19
  import { PromptButtonVariant } from '@envive-ai/react-toolkit-v3/PromptButton/types';
17
20
  import { usePromptCarouselAnalytics } from '@envive-ai/react-toolkit-v3/PromptCarousel';
18
21
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
19
- import { useCallback, useEffect } from 'react';
22
+ import { useCallback } from 'react';
20
23
  import { BaseWidgetProps, withBaseWidget } from '../../hocs/withBaseWidget';
21
24
  import { RawValues, getStringIdForText } from '../utils/functions';
22
25
 
@@ -34,7 +37,7 @@ const mockAnimatedText = ['Loading...'];
34
37
  const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
35
38
  const { onTypedMessageSubmitted } = useSalesAgent();
36
39
  const { openChat } = useChatToggle();
37
- const { hardcopyContent, widgetConfig, isLoading } = props;
40
+ const { hardcopyContent, widgetConfig, uiConfig, isLoading } = props;
38
41
 
39
42
  const hardCopyHeadline = hardcopyContent?.values?.headline as string | undefined;
40
43
  const headline = isLoading ? mockHeadline : hardCopyHeadline || '';
@@ -56,6 +59,10 @@ const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
56
59
  const italicizeHeadline = productCardWidgetConfig?.italicizeHeadline;
57
60
  const fallbackColor = productCardWidgetConfig?.fallbackColor;
58
61
  const imageSrc = productCardWidgetConfig?.imageSrc || '';
62
+ const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
63
+ const { featureFlagService } = useFeatureFlagService();
64
+ const isVoiceInputEnabled =
65
+ featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
59
66
 
60
67
  const { trackEvent } = useAmplitude();
61
68
  const { trackWidgetInteraction } = useWidgetInteraction();
@@ -72,17 +79,6 @@ const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
72
79
  return getStringIdForText(rawValues, text);
73
80
  });
74
81
 
75
- const { widgetConfigId } = props;
76
- useEffect(() => {
77
- trackEvent({
78
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
79
- eventProps: {
80
- widget_config_id: widgetConfigId,
81
- widget_type: WidgetTypeV3.ProductCardV3,
82
- },
83
- });
84
- }, [trackEvent, widgetConfigId]);
85
-
86
82
  const handleSelect = useCallback(
87
83
  (text: string) => {
88
84
  const rawValues = (hardcopyContent as { rawValues?: RawValues } | undefined)?.rawValues;
@@ -117,6 +113,29 @@ const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
117
113
  });
118
114
  }, [openChat]);
119
115
 
116
+ const handleTranscriptionStarted = useCallback(() => {
117
+ trackWidgetInteraction({
118
+ eventName: EnviveMetricsEventName.WidgetInteraction,
119
+ trigger: {
120
+ widget: WidgetInteractionComponent.IMAGE_PROMPT_CARD,
121
+ widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_STARTED,
122
+ },
123
+ });
124
+ }, []);
125
+
126
+ const handleTranscriptionCompleted = useCallback((transcript: string) => {
127
+ trackWidgetInteraction({
128
+ eventName: EnviveMetricsEventName.WidgetInteraction,
129
+ trigger: {
130
+ widget: WidgetInteractionComponent.IMAGE_PROMPT_CARD,
131
+ widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_COMPLETED,
132
+ widget_interaction_data: {
133
+ transcription: transcript,
134
+ },
135
+ },
136
+ });
137
+ }, []);
138
+
120
139
  return (
121
140
  <ProductCard
122
141
  theme={Theme.GLOBAL_CUSTOM}
@@ -129,6 +148,7 @@ const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
129
148
  promptButtonType={promptButtonType}
130
149
  carouselId={carouselId}
131
150
  placeholder={placeholder}
151
+ voiceInputEnabled={isVoiceInputEnabled}
132
152
  textTypingDuration={800}
133
153
  textTransition={2000}
134
154
  onSelect={handleSelect}
@@ -139,6 +159,8 @@ const ProductCardWidgetHandler = (props: BaseWidgetProps) => {
139
159
  onTouchStart={onTouchStart}
140
160
  onTouchEnd={onTouchEnd}
141
161
  onInputClick={handleInputClick}
162
+ onTranscriptionStarted={handleTranscriptionStarted}
163
+ onTranscriptionCompleted={handleTranscriptionCompleted}
142
164
  />
143
165
  );
144
166
  };
@@ -1,14 +1,12 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import {
3
- EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
- } from '@envive-ai/react-hooks/contexts/amplitudeContext';
2
+ import { EnviveMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
6
3
  import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
7
4
  import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
8
5
  import { ProductCardWidget } from '../ProductCardWidget';
9
6
 
10
7
  const mockTrackEvent = vi.fn();
11
8
  const mockGetHardcopy = vi.fn();
9
+ const mockUseTrackComponentVisibleEvent = vi.fn();
12
10
 
13
11
  vi.mock('@envive-ai/react-toolkit-v3/ProductCard', () => ({
14
12
  ProductCard: ({ prompts, onSelect }: { prompts: string[]; onSelect: (text: string) => void }) => (
@@ -40,6 +38,10 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
40
38
  },
41
39
  }));
42
40
 
41
+ vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
42
+ useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
43
+ }));
44
+
43
45
  vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
44
46
  useHardcopy: () => ({
45
47
  getHardcopy: mockGetHardcopy,
@@ -106,13 +108,15 @@ describe('ProductCardWidget analytics', () => {
106
108
 
107
109
  await waitFor(
108
110
  () => {
109
- expect(mockTrackEvent).toHaveBeenCalledWith({
110
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
111
- eventProps: {
111
+ expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
112
+ expect.any(Object),
113
+ {
112
114
  widget_config_id: 'test-config-1',
113
115
  widget_type: WidgetTypeV3.ProductCardV3,
114
116
  },
115
- });
117
+ '0px',
118
+ true,
119
+ );
116
120
  },
117
121
  { timeout: 3000 },
118
122
  );
@@ -4,16 +4,17 @@ import {
4
4
  WidgetTypeV3,
5
5
  } from '@envive-ai/react-hooks/contexts/typesV3';
6
6
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
7
+ import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
7
8
 
8
9
  import {
9
10
  ChatElementDisplayLocationV3,
11
+ FeatureGates,
10
12
  VariantTypeEnum,
11
13
  } from '@envive-ai/react-hooks/application/models';
12
14
  import { variantInfoAtom } from '@envive-ai/react-hooks/atoms/app';
13
15
  import { lastAssistantMessageAtom } from '@envive-ai/react-hooks/atoms/chat';
14
16
  import {
15
17
  EnviveMetricsEventName,
16
- SpiffyMetricsEventName,
17
18
  useAmplitude,
18
19
  } from '@envive-ai/react-hooks/contexts/amplitudeContext';
19
20
  import { useWidgetInteraction } from '@envive-ai/react-hooks/hooks/WidgetInteraction';
@@ -28,7 +29,7 @@ import {
28
29
  import { usePromptCarouselAnalytics } from '@envive-ai/react-toolkit-v3/PromptCarousel';
29
30
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
30
31
  import { useAtomValue } from 'jotai';
31
- import { useCallback, useEffect, useMemo } from 'react';
32
+ import { useCallback, useMemo } from 'react';
32
33
  import { BaseWidgetProps } from '../../hocs/withBaseWidget/types';
33
34
  import { withBaseWidget } from '../../hocs/withBaseWidget/withBaseWidget';
34
35
  import { RawValues, getRecentProductImageUrls, getStringIdForText } from '../utils/functions';
@@ -47,7 +48,7 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
47
48
  onTouchEnd,
48
49
  } = usePromptCarouselAnalytics(WidgetInteractionComponent.SINGLE_IMAGE_PROMPT, text => text);
49
50
 
50
- const { hardcopyContent, widgetConfig, isLoading, widgetConfigId } = props;
51
+ const { hardcopyContent, widgetConfig, uiConfig, isLoading } = props;
51
52
 
52
53
  const promptButtonCarouselWithImageWidgetConfig =
53
54
  widgetConfig as PromptButtonCarouselWithImageWidgetV3Config;
@@ -65,16 +66,6 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
65
66
 
66
67
  const { trackEvent } = useAmplitude();
67
68
 
68
- useEffect(() => {
69
- trackEvent({
70
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
71
- eventProps: {
72
- widget_config_id: widgetConfigId,
73
- widget_type: WidgetTypeV3.PromptButtonCarouselWithImageV3,
74
- },
75
- });
76
- }, [trackEvent, widgetConfigId]);
77
-
78
69
  const handlePromptButtonClick = useCallback(
79
70
  (text: string) => {
80
71
  const rawValues = (hardcopyContent as { rawValues?: RawValues } | undefined)?.rawValues;
@@ -110,6 +101,29 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
110
101
  });
111
102
  }, [openChat]);
112
103
 
104
+ const handleTranscriptionStarted = useCallback(() => {
105
+ trackWidgetInteraction({
106
+ eventName: EnviveMetricsEventName.WidgetInteraction,
107
+ trigger: {
108
+ widget: WidgetInteractionComponent.SINGLE_IMAGE_PROMPT,
109
+ widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_STARTED,
110
+ },
111
+ });
112
+ }, []);
113
+
114
+ const handleTranscriptionCompleted = useCallback((transcript: string) => {
115
+ trackWidgetInteraction({
116
+ eventName: EnviveMetricsEventName.WidgetInteraction,
117
+ trigger: {
118
+ widget: WidgetInteractionComponent.SINGLE_IMAGE_PROMPT,
119
+ widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_COMPLETED,
120
+ widget_interaction_data: {
121
+ transcription: transcript,
122
+ },
123
+ },
124
+ });
125
+ }, []);
126
+
113
127
  const variantInfo = useAtomValue(variantInfoAtom);
114
128
  const productId = variantInfo.variant === VariantTypeEnum.Pdp ? variantInfo.productId : undefined;
115
129
 
@@ -131,6 +145,11 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
131
145
  ? 'Product Image From Page'
132
146
  : promptButtonCarouselWithImageWidgetConfig?.image?.alt;
133
147
 
148
+ const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
149
+ const { featureFlagService } = useFeatureFlagService();
150
+ const isVoiceInputEnabled =
151
+ featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
152
+
134
153
  return (
135
154
  <PromptButtonCarouselWithImage
136
155
  id={id}
@@ -143,6 +162,7 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
143
162
  alt={finalImageAlt}
144
163
  textFieldPlaceholder={hardCopyContent?.textFieldPlaceholder}
145
164
  hideTextField={promptButtonCarouselWithImageWidgetConfig?.hideTextField}
165
+ voiceInputEnabled={isVoiceInputEnabled}
146
166
  handlePromptButtonClick={handlePromptButtonClick}
147
167
  handlePromptButtonDrag={onDrag}
148
168
  handlePromptButtonHover={onHover}
@@ -151,6 +171,8 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
151
171
  handlePromptButtonTouchStart={onTouchStart}
152
172
  handlePromptButtonTouchEnd={onTouchEnd}
153
173
  handleTextFieldClick={handleTextFieldClick}
174
+ onTranscriptionStarted={handleTranscriptionStarted}
175
+ onTranscriptionCompleted={handleTranscriptionCompleted}
154
176
  />
155
177
  );
156
178
  };
@@ -1,14 +1,12 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import {
3
- EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
- } from '@envive-ai/react-hooks/contexts/amplitudeContext';
2
+ import { EnviveMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
6
3
  import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
7
4
  import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
8
5
  import { PromptButtonCarouselWithImageWidget } from '../PromptButtonCarouselWithImageWidget';
9
6
 
10
7
  const mockTrackEvent = vi.fn();
11
8
  const mockGetHardcopy = vi.fn();
9
+ const mockUseTrackComponentVisibleEvent = vi.fn();
12
10
 
13
11
  vi.mock('@envive-ai/react-toolkit-v3/PromptButtonCarouselWithImage', () => ({
14
12
  PromptButtonCarouselWithImage: ({
@@ -46,6 +44,17 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
46
44
  },
47
45
  }));
48
46
 
47
+ vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
48
+ useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
49
+ }));
50
+
51
+ vi.mock('@envive-ai/react-hooks/hooks/WidgetInteraction', () => ({
52
+ useWidgetInteraction: () => ({
53
+ trackWidgetInteraction: vi.fn(),
54
+ getInteractionId: vi.fn(),
55
+ }),
56
+ }));
57
+
49
58
  vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
50
59
  useHardcopy: () => ({
51
60
  getHardcopy: mockGetHardcopy,
@@ -141,13 +150,15 @@ describe('PromptButtonCarouselWithImageWidget analytics', () => {
141
150
 
142
151
  await waitFor(
143
152
  () => {
144
- expect(mockTrackEvent).toHaveBeenCalledWith({
145
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
146
- eventProps: {
153
+ expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
154
+ expect.any(Object),
155
+ {
147
156
  widget_config_id: 'test-config-1',
148
157
  widget_type: WidgetTypeV3.PromptButtonCarouselWithImageV3,
149
158
  },
150
- });
159
+ '0px',
160
+ true,
161
+ );
151
162
  },
152
163
  { timeout: 3000 },
153
164
  );
@@ -1,7 +1,6 @@
1
1
  import { ChatElementDisplayLocationV3 } from '@envive-ai/react-hooks/application/models';
2
2
  import {
3
3
  EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
4
  useAmplitude,
6
5
  } from '@envive-ai/react-hooks/contexts/amplitudeContext';
7
6
  import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
@@ -10,7 +9,7 @@ import {
10
9
  WidgetTypeV3,
11
10
  } from '@envive-ai/react-hooks/contexts/typesV3';
12
11
  import { PromptButtonVariant } from '@envive-ai/react-toolkit-v3/PromptButton/types';
13
- import { useCallback, useEffect } from 'react';
12
+ import { useCallback } from 'react';
14
13
 
15
14
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
16
15
  import { WidgetInteractionComponent } from '@envive-ai/react-hooks/hooks/WidgetInteraction/types';
@@ -65,16 +64,6 @@ const PromptCarouselWidgetHandler = (props: BaseWidgetProps) => {
65
64
 
66
65
  const { trackEvent } = useAmplitude();
67
66
 
68
- useEffect(() => {
69
- trackEvent({
70
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
71
- eventProps: {
72
- widget_config_id: widgetConfigId,
73
- widget_type: WidgetTypeV3.PromptCarouselV3,
74
- },
75
- });
76
- }, [trackEvent, widgetConfigId]);
77
-
78
67
  const handleButtonClick = useCallback(
79
68
  (text: string) => {
80
69
  const rawValues = (hardcopyContent as { rawValues?: RawValues } | undefined)?.rawValues;
@@ -1,14 +1,12 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import {
3
- EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
- } from '@envive-ai/react-hooks/contexts/amplitudeContext';
2
+ import { EnviveMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
6
3
  import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
7
4
  import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
8
5
  import { PromptCarouselWidget } from '../PromptCarouselWidget';
9
6
 
10
7
  const mockTrackEvent = vi.fn();
11
8
  const mockGetHardcopy = vi.fn();
9
+ const mockUseTrackComponentVisibleEvent = vi.fn();
12
10
 
13
11
  vi.mock('@envive-ai/react-toolkit-v3/PromptCarousel', () => ({
14
12
  PromptCarousel: ({
@@ -30,6 +28,15 @@ vi.mock('@envive-ai/react-toolkit-v3/PromptCarousel', () => ({
30
28
  ))}
31
29
  </div>
32
30
  ),
31
+ usePromptCarouselAnalytics: () => ({
32
+ onClick: vi.fn(),
33
+ onDrag: vi.fn(),
34
+ onHover: vi.fn(),
35
+ onMouseDown: vi.fn(),
36
+ onMouseUp: vi.fn(),
37
+ onTouchStart: vi.fn(),
38
+ onTouchEnd: vi.fn(),
39
+ }),
33
40
  }));
34
41
 
35
42
  vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
@@ -46,6 +53,10 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
46
53
  },
47
54
  }));
48
55
 
56
+ vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
57
+ useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
58
+ }));
59
+
49
60
  vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
50
61
  useHardcopy: () => ({
51
62
  getHardcopy: mockGetHardcopy,
@@ -112,13 +123,15 @@ describe('PromptCarouselWidget analytics', () => {
112
123
 
113
124
  await waitFor(
114
125
  () => {
115
- expect(mockTrackEvent).toHaveBeenCalledWith({
116
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
117
- eventProps: {
126
+ expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
127
+ expect.any(Object),
128
+ {
118
129
  widget_config_id: 'test-config-1',
119
130
  widget_type: WidgetTypeV3.PromptCarouselV3,
120
131
  },
121
- });
132
+ '0px',
133
+ true,
134
+ );
122
135
  },
123
136
  { timeout: 3000 },
124
137
  );
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useMemo } from 'react';
1
+ import { useCallback, useMemo } from 'react';
2
2
  import { SocialProofWidgetV3Config, WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
3
3
  import { useAtomValue, useSetAtom } from 'jotai';
4
4
  import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
@@ -13,12 +13,12 @@ import {
13
13
 
14
14
  import {
15
15
  ChatElementDisplayLocationV3,
16
+ FeatureGates,
16
17
  VariantTypeEnum,
17
18
  } from '@envive-ai/react-hooks/application/models';
18
19
  import { chatPreviewLoadingDataAtom } from '@envive-ai/react-hooks/atoms/widget';
19
20
  import {
20
21
  EnviveMetricsEventName,
21
- SpiffyMetricsEventName,
22
22
  useAmplitude,
23
23
  } from '@envive-ai/react-hooks/contexts/amplitudeContext';
24
24
  import { useWidgetInteraction } from '@envive-ai/react-hooks/hooks/WidgetInteraction';
@@ -30,6 +30,7 @@ import { useProductImageUrl } from '@envive-ai/react-hooks/hooks/ProductImageUrl
30
30
  import { ChatPreviewLoading } from '@envive-ai/react-toolkit-v3/ChatPreviewLoading';
31
31
  import { usePromptCarouselAnalytics } from '@envive-ai/react-toolkit-v3/PromptCarousel';
32
32
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
33
+ import { useFeatureFlagService } from '@envive-ai/react-hooks/contexts/featureFlagServiceContext';
33
34
 
34
35
  import { BaseWidgetProps } from '../../hocs/withBaseWidget/types';
35
36
  import { withBaseWidget } from '../../hocs/withBaseWidget/withBaseWidget';
@@ -98,6 +99,11 @@ const SocialProofWidgetHandler = (props: BaseWidgetProps) => {
98
99
 
99
100
  const hideLogo = uiConfig?.lookAndFeel?.hideWidgetLogo;
100
101
 
102
+ const voiceInputEnabled = uiConfig?.lookAndFeel?.voiceInputEnabled;
103
+ const { featureFlagService } = useFeatureFlagService();
104
+ const isVoiceInputEnabled =
105
+ featureFlagService?.isFeatureGateEnabled(FeatureGates.IsVoiceInputEnabled) || voiceInputEnabled;
106
+
101
107
  const productId = variantInfo.variant === VariantTypeEnum.Pdp ? variantInfo.productId : undefined;
102
108
  const dynamicImageUrl = useProductImageUrl(productId);
103
109
 
@@ -108,16 +114,6 @@ const SocialProofWidgetHandler = (props: BaseWidgetProps) => {
108
114
 
109
115
  const { trackEvent } = useAmplitude();
110
116
 
111
- useEffect(() => {
112
- trackEvent({
113
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
114
- eventProps: {
115
- widget_config_id: widgetConfigId,
116
- widget_type: WidgetTypeV3.SocialProofV3,
117
- },
118
- });
119
- }, [trackEvent, widgetConfigId]);
120
-
121
117
  const handlePrimaryButtonClick = useCallback(
122
118
  (text: string) => {
123
119
  const rawValues = (hardcopyContent as { rawValues?: RawValues } | undefined)?.rawValues;
@@ -202,6 +198,29 @@ const SocialProofWidgetHandler = (props: BaseWidgetProps) => {
202
198
  });
203
199
  }, [openChat]);
204
200
 
201
+ const handleTranscriptionStarted = useCallback(() => {
202
+ trackWidgetInteraction({
203
+ eventName: EnviveMetricsEventName.WidgetInteraction,
204
+ trigger: {
205
+ widget: WidgetInteractionComponent.SOCIAL_PROOF,
206
+ widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_STARTED,
207
+ },
208
+ });
209
+ }, []);
210
+
211
+ const handleTranscriptionCompleted = useCallback((transcript: string) => {
212
+ trackWidgetInteraction({
213
+ eventName: EnviveMetricsEventName.WidgetInteraction,
214
+ trigger: {
215
+ widget: WidgetInteractionComponent.SOCIAL_PROOF,
216
+ widget_interaction: WidgetInteractionType.VOICE_TRANSCRIPTION_COMPLETED,
217
+ widget_interaction_data: {
218
+ transcription: transcript,
219
+ },
220
+ },
221
+ });
222
+ }, []);
223
+
205
224
  if (isLoading) {
206
225
  return <ChatPreviewLoading fullIsLoading />;
207
226
  }
@@ -244,6 +263,7 @@ const SocialProofWidgetHandler = (props: BaseWidgetProps) => {
244
263
  textFieldPlaceholderText: hardCopyContent?.textFieldPlaceholderText,
245
264
  images: images ?? [],
246
265
  logoSrc: logoSrc ?? undefined,
266
+ voiceInputEnabled: isVoiceInputEnabled,
247
267
  };
248
268
 
249
269
  const widgetEventProps: SocialProofProps['widgetEventProps'] = {
@@ -257,6 +277,8 @@ const SocialProofWidgetHandler = (props: BaseWidgetProps) => {
257
277
  handleSecondaryButtonTouchStart: onTouchStart,
258
278
  handleSecondaryButtonTouchEnd: onTouchEnd,
259
279
  handleTextFieldClick,
280
+ onTranscriptionStarted: handleTranscriptionStarted,
281
+ onTranscriptionCompleted: handleTranscriptionCompleted,
260
282
  };
261
283
 
262
284
  return (
@@ -1,14 +1,12 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import {
3
- EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
- } from '@envive-ai/react-hooks/contexts/amplitudeContext';
2
+ import { EnviveMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
6
3
  import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
7
4
  import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
8
5
  import { SocialProofWidget } from '../SocialProofWidget';
9
6
 
10
7
  const mockTrackEvent = vi.fn();
11
8
  const mockGetHardcopy = vi.fn();
9
+ const mockUseTrackComponentVisibleEvent = vi.fn();
12
10
 
13
11
  vi.mock('@envive-ai/react-toolkit-v3/SocialProof', async importOriginal => {
14
12
  const actual = await importOriginal();
@@ -52,6 +50,17 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
52
50
  },
53
51
  }));
54
52
 
53
+ vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
54
+ useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
55
+ }));
56
+
57
+ vi.mock('@envive-ai/react-hooks/hooks/WidgetInteraction', () => ({
58
+ useWidgetInteraction: () => ({
59
+ trackWidgetInteraction: vi.fn(),
60
+ getInteractionId: vi.fn(),
61
+ }),
62
+ }));
63
+
55
64
  vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
56
65
  useHardcopy: () => ({
57
66
  getHardcopy: mockGetHardcopy,
@@ -146,13 +155,15 @@ describe('SocialProofWidget analytics', () => {
146
155
 
147
156
  await waitFor(
148
157
  () => {
149
- expect(mockTrackEvent).toHaveBeenCalledWith({
150
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
151
- eventProps: {
158
+ expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
159
+ expect.any(Object),
160
+ {
152
161
  widget_config_id: 'test-config-1',
153
162
  widget_type: WidgetTypeV3.SocialProofV3,
154
163
  },
155
- });
164
+ '0px',
165
+ true,
166
+ );
156
167
  },
157
168
  { timeout: 3000 },
158
169
  );
@@ -1,7 +1,6 @@
1
1
  import { ChatElementDisplayLocationV3 } from '@envive-ai/react-hooks/application/models';
2
2
  import {
3
3
  EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
4
  useAmplitude,
6
5
  } from '@envive-ai/react-hooks/contexts/amplitudeContext';
7
6
  import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
@@ -10,7 +9,7 @@ import {
10
9
  WidgetTypeV3,
11
10
  } from '@envive-ai/react-hooks/contexts/typesV3';
12
11
  import { PromptButtonVariant } from '@envive-ai/react-toolkit-v3/PromptButton/types';
13
- import { useCallback, useEffect } from 'react';
12
+ import { useCallback } from 'react';
14
13
 
15
14
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
16
15
  import { WidgetInteractionComponent } from '@envive-ai/react-hooks/hooks/WidgetInteraction/types';
@@ -65,16 +64,6 @@ const TitledPromptCarouselWidgetHandler = (props: BaseWidgetProps) => {
65
64
 
66
65
  const { trackEvent } = useAmplitude();
67
66
 
68
- useEffect(() => {
69
- trackEvent({
70
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
71
- eventProps: {
72
- widget_config_id: widgetConfigId,
73
- widget_type: WidgetTypeV3.TitledPromptCarouselV3,
74
- },
75
- });
76
- }, [trackEvent, widgetConfigId]);
77
-
78
67
  const handleButtonClick = useCallback(
79
68
  (text: string) => {
80
69
  const rawValues = (hardcopyContent as { rawValues?: RawValues } | undefined)?.rawValues;
@@ -1,14 +1,12 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import {
3
- EnviveMetricsEventName,
4
- SpiffyMetricsEventName,
5
- } from '@envive-ai/react-hooks/contexts/amplitudeContext';
2
+ import { EnviveMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
6
3
  import { WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
7
4
  import { UserEvent, UserEventCategory } from '@spiffy-ai/commerce-api-client';
8
5
  import { TitledPromptCarouselWidget } from '../TitledPromptCarouselWidget';
9
6
 
10
7
  const mockTrackEvent = vi.fn();
11
8
  const mockGetHardcopy = vi.fn();
9
+ const mockUseTrackComponentVisibleEvent = vi.fn();
12
10
 
13
11
  vi.mock('@envive-ai/react-toolkit-v3/TitledPromptCarousel', () => ({
14
12
  TitledPromptCarousel: ({
@@ -46,6 +44,17 @@ vi.mock('@envive-ai/react-hooks/contexts/amplitudeContext', () => ({
46
44
  },
47
45
  }));
48
46
 
47
+ vi.mock('@envive-ai/react-hooks/hooks/TrackComponentVisibleEvent', () => ({
48
+ useTrackComponentVisibleEvent: (...args: unknown[]) => mockUseTrackComponentVisibleEvent(...args),
49
+ }));
50
+
51
+ vi.mock('@envive-ai/react-hooks/hooks/WidgetInteraction', () => ({
52
+ useWidgetInteraction: () => ({
53
+ trackWidgetInteraction: vi.fn(),
54
+ getInteractionId: vi.fn(),
55
+ }),
56
+ }));
57
+
49
58
  vi.mock('@envive-ai/react-hooks/contexts/hardcopyContext', () => ({
50
59
  useHardcopy: () => ({
51
60
  getHardcopy: mockGetHardcopy,
@@ -112,13 +121,15 @@ describe('TitledPromptCarouselWidget analytics', () => {
112
121
 
113
122
  await waitFor(
114
123
  () => {
115
- expect(mockTrackEvent).toHaveBeenCalledWith({
116
- eventName: SpiffyMetricsEventName.ChatComponentVisible,
117
- eventProps: {
124
+ expect(mockUseTrackComponentVisibleEvent).toHaveBeenCalledWith(
125
+ expect.any(Object),
126
+ {
118
127
  widget_config_id: 'test-config-1',
119
128
  widget_type: WidgetTypeV3.TitledPromptCarouselV3,
120
129
  },
121
- });
130
+ '0px',
131
+ true,
132
+ );
122
133
  },
123
134
  { timeout: 3000 },
124
135
  );