@envive-ai/react-widgets-v3 0.3.6 → 0.3.7

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 (83) hide show
  1. package/dist/CXIntegration/hooks/useUnifiedCXButton.cjs +2 -0
  2. package/dist/CXIntegration/hooks/useUnifiedCXButton.js +2 -0
  3. package/dist/hocs/withBaseWidget/withBaseWidget.d.cts +2 -2
  4. package/dist/hocs/withBaseWidget/withBaseWidget.d.ts +2 -2
  5. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.cjs +7 -24
  6. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.cts +3 -3
  7. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.d.ts +3 -3
  8. package/dist/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.js +9 -26
  9. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.cts +3 -3
  10. package/dist/widgets/ChatPreviewLoadingWidget/ChatPreviewLoadingWidget.d.ts +3 -3
  11. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.cjs +18 -24
  12. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.d.ts +3 -3
  13. package/dist/widgets/ChatPreviewWidget/ChatPreviewWidget.js +21 -27
  14. package/dist/widgets/FloatingChatWidget/FloatingChatOverlay.cjs +7 -6
  15. package/dist/widgets/FloatingChatWidget/FloatingChatOverlay.js +7 -6
  16. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.cjs +40 -11
  17. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.cts +9 -3
  18. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.d.ts +8 -2
  19. package/dist/widgets/FloatingChatWidget/FloatingChatWidget.js +41 -12
  20. package/dist/widgets/FloatingChatWidget/hooks/useFloatingButtonVisibility.cjs +19 -0
  21. package/dist/widgets/FloatingChatWidget/hooks/useFloatingButtonVisibility.js +18 -0
  22. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.cjs +19 -26
  23. package/dist/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.js +21 -28
  24. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.cjs +17 -14
  25. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.d.cts +2 -2
  26. package/dist/widgets/PromptCarouselWidget/PromptCarouselWidget.js +19 -16
  27. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.cts +2 -2
  28. package/dist/widgets/SocialProofFlowWidget/SocialProofFlowWidget.d.ts +2 -2
  29. package/dist/widgets/SocialProofWidget/SocialProofWidget.cjs +42 -36
  30. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.cts +3 -3
  31. package/dist/widgets/SocialProofWidget/SocialProofWidget.d.ts +3 -3
  32. package/dist/widgets/SocialProofWidget/SocialProofWidget.js +45 -39
  33. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.cjs +19 -15
  34. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.cts +2 -2
  35. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.d.ts +2 -2
  36. package/dist/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.js +21 -17
  37. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.cts +2 -2
  38. package/dist/widgets/TypingAnimationFlowWidget/TypingAnimationFlowWidget.d.ts +2 -2
  39. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.cjs +17 -13
  40. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.d.cts +3 -3
  41. package/dist/widgets/TypingAnimationWidget/TypingAnimationWidget.js +19 -15
  42. package/dist/widgets/dist/SearchResults/SearchResultsWidget.d.cts +2 -2
  43. package/dist/{packages/widgets → widgets}/dist/SearchResults/SearchResultsWidget.d.ts +2 -2
  44. package/dist/widgets/dist/SuggestionBar/SuggestionBar.d.cts +2 -2
  45. package/dist/{packages/widgets → widgets}/dist/SuggestionBar/SuggestionBar.d.ts +2 -2
  46. package/dist/widgets-v2/SearchResults/index.d.ts +3 -3
  47. package/dist/widgets-v2/SearchZeroState/index.d.ts +4 -4
  48. package/dist/widgets-v2/SuggestionBar/index.d.ts +3 -3
  49. package/dist/widgets-v2/SuggestionButtonContainer/index.d.ts +2 -2
  50. package/package.json +1 -1
  51. package/src/CXIntegration/hooks/useUnifiedCXButton.ts +8 -0
  52. package/src/stories/SalesAgentTest/SalesAgentTest.tsx +2 -2
  53. package/src/widgets/ChatPreviewComparisonWidget/ChatPreviewComparisonWidget.tsx +8 -30
  54. package/src/widgets/ChatPreviewWidget/ChatPreviewWidget.tsx +26 -33
  55. package/src/widgets/FloatingChatWidget/FloatingChatOverlay.tsx +22 -13
  56. package/src/widgets/FloatingChatWidget/FloatingChatWidget.tsx +89 -22
  57. package/src/widgets/FloatingChatWidget/hooks/useFloatingButtonVisibility.ts +43 -0
  58. package/src/widgets/PromptButtonCarouselWithImageWidget/PromptButtonCarouselWithImageWidget.tsx +25 -34
  59. package/src/widgets/PromptCarouselWidget/PromptCarouselWidget.tsx +23 -20
  60. package/src/widgets/SocialProofWidget/SocialProofWidget.tsx +55 -45
  61. package/src/widgets/TitledPromptCarouselWidget/TitledPromptCarouselWidget.tsx +23 -20
  62. package/src/widgets/TypingAnimationWidget/TypingAnimationWidget.tsx +22 -19
  63. package/dist/node_modules/uuid/dist/native.js +0 -6
  64. package/dist/node_modules/uuid/dist/rng.js +0 -13
  65. package/dist/node_modules/uuid/dist/stringify.js +0 -9
  66. package/dist/node_modules/uuid/dist/v4.js +0 -27
  67. package/dist/node_modules/uuid/dist-node/native.cjs +0 -8
  68. package/dist/node_modules/uuid/dist-node/rng.cjs +0 -16
  69. package/dist/node_modules/uuid/dist-node/stringify.cjs +0 -10
  70. package/dist/node_modules/uuid/dist-node/v4.cjs +0 -27
  71. /package/dist/{packages/widgets → widgets}/dist/SearchResults/SearchResults.d.ts +0 -0
  72. /package/dist/{packages/widgets → widgets}/dist/SearchResults/index.d.ts +0 -0
  73. /package/dist/{packages/widgets → widgets}/dist/SearchResults/types.d.ts +0 -0
  74. /package/dist/{packages/widgets → widgets}/dist/SearchZeroState/SearchZeroState.d.ts +0 -0
  75. /package/dist/{packages/widgets → widgets}/dist/SearchZeroState/SearchZeroStateWidget.d.ts +0 -0
  76. /package/dist/{packages/widgets → widgets}/dist/SearchZeroState/index.d.ts +0 -0
  77. /package/dist/{packages/widgets → widgets}/dist/SearchZeroState/types.d.ts +0 -0
  78. /package/dist/{packages/widgets → widgets}/dist/SuggestionBar/index.d.ts +0 -0
  79. /package/dist/{packages/widgets → widgets}/dist/SuggestionBar/types.d.ts +0 -0
  80. /package/dist/{packages/widgets → widgets}/dist/SuggestionButtonContainer/SuggestionButtonContainer.d.ts +0 -0
  81. /package/dist/{packages/widgets → widgets}/dist/SuggestionButtonContainer/types.d.ts +0 -0
  82. /package/dist/{packages/widgets → widgets}/dist/config/BaseWidgetConfig.d.ts +0 -0
  83. /package/dist/{packages/widgets → widgets}/dist/config/WidgetType.d.ts +0 -0
@@ -34,5 +34,13 @@ export const useUnifiedCXButton = ({
34
34
  if (!showUnifiedButton) return undefined;
35
35
  }
36
36
 
37
+ if (!selectedCustomerService.isSwitchEnabled() && !window?._spiffy?.selectedCustomizeOption) {
38
+ return undefined;
39
+ }
40
+
41
+ if (window?._spiffy?.selectedCustomizeOption && provider === CustomerServiceType.unsupported) {
42
+ return undefined;
43
+ }
44
+
37
45
  return selectedCustomerService;
38
46
  };
@@ -56,7 +56,7 @@ export const SalesAgentTest = () => {
56
56
  onChange={e => setQuery(e.target.value)}
57
57
  onKeyDown={e => {
58
58
  if (e.key === 'Enter') {
59
- onTypedMessageSubmitted({ query });
59
+ onTypedMessageSubmitted({ query, userTyped: true });
60
60
  setQuery('');
61
61
  }
62
62
  }}
@@ -65,7 +65,7 @@ export const SalesAgentTest = () => {
65
65
  <button
66
66
  type="button"
67
67
  onClick={() => {
68
- onTypedMessageSubmitted({ query });
68
+ onTypedMessageSubmitted({ query, userTyped: true });
69
69
  setQuery('');
70
70
  }}
71
71
  >
@@ -2,8 +2,8 @@ import {
2
2
  ChatPreviewComparisonWidgetV3Config,
3
3
  WidgetTypeV3,
4
4
  } from '@envive-ai/react-hooks/contexts/typesV3';
5
- import { useAtomValue, useSetAtom } from 'jotai';
6
- import { handleReplyAtom } from '@envive-ai/react-hooks/atoms/chat/replies';
5
+ import { useAtomValue } from 'jotai';
6
+ import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
7
7
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
8
8
  import { chatAtom, lastAssistantMessageAtom } from '@envive-ai/react-hooks/atoms/chat';
9
9
 
@@ -11,12 +11,8 @@ import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
11
11
  import { useCallback, useMemo } from 'react';
12
12
  import {
13
13
  ChatElementDisplayLocationV3,
14
- Message,
15
- MessageRole,
16
- MessageType,
17
14
  VariantTypeEnum,
18
15
  } from '@envive-ai/react-hooks/application/models';
19
- import { v4 as uuid } from 'uuid';
20
16
  import { ChatPreviewComparison } from '@envive-ai/react-toolkit-v3/ChatPreviewComparison';
21
17
  import { ChatPreviewComparisonProps } from '@envive-ai/react-toolkit-v3/ChatPreviewComparison/types/types';
22
18
  import { variantInfoAtom } from '@envive-ai/react-hooks/atoms/app';
@@ -26,7 +22,7 @@ import { getMessageText, getRecentProductImageUrls } from '../utils/functions';
26
22
  import { ChatPreviewLoadingWidgetWithBaseWidget } from '../ChatPreviewLoadingWidget/ChatPreviewLoadingWidget';
27
23
 
28
24
  const ChatPreviewComparisonWidgetHandler = (props: BaseWidgetProps) => {
29
- const handleReply = useSetAtom(handleReplyAtom);
25
+ const { onTypedMessageSubmitted } = useSalesAgent();
30
26
  const { openChat } = useChatToggle();
31
27
 
32
28
  const lastAssistantMessage = useAtomValue(lastAssistantMessageAtom);
@@ -78,33 +74,15 @@ const ChatPreviewComparisonWidgetHandler = (props: BaseWidgetProps) => {
78
74
 
79
75
  const handlePromptButtonClick = useCallback(
80
76
  (text: string) => {
81
- const newMessage: Message = {
82
- id: uuid(),
83
- role: MessageRole.User,
84
- type: MessageType.QueryTyped,
85
- createdAt: new Date().toISOString(),
86
- metadata: { content: text },
87
- };
88
- handleReply({ message: newMessage, userTyped: false });
77
+ onTypedMessageSubmitted({ query: text, userTyped: false });
89
78
  openChat(ChatElementDisplayLocationV3.CHAT_PREVIEW_COMPARISON_PROMPT_BUTTON);
90
79
  },
91
- [handleReply, openChat],
80
+ [onTypedMessageSubmitted, openChat],
92
81
  );
93
82
 
94
- const handleTextFieldClick = useCallback(
95
- (text: string) => {
96
- const newMessage: Message = {
97
- id: uuid(),
98
- role: MessageRole.User,
99
- type: MessageType.QueryTyped,
100
- createdAt: new Date().toISOString(),
101
- metadata: { content: text },
102
- };
103
- handleReply({ message: newMessage, userTyped: false });
104
- openChat(ChatElementDisplayLocationV3.CHAT_PREVIEW_COMPARISON_TEXT_FIELD);
105
- },
106
- [handleReply, openChat],
107
- );
83
+ const handleTextFieldClick = useCallback(() => {
84
+ openChat(ChatElementDisplayLocationV3.CHAT_PREVIEW_COMPARISON_TEXT_FIELD);
85
+ }, [openChat]);
108
86
 
109
87
  if (isLoading) {
110
88
  return (
@@ -1,18 +1,17 @@
1
1
  import { ChatPreviewWidgetV3Config, WidgetTypeV3 } from '@envive-ai/react-hooks/contexts/typesV3';
2
- import { useAtomValue, useSetAtom } from 'jotai';
3
- import { handleReplyAtom } from '@envive-ai/react-hooks/atoms/chat/replies';
2
+ import { useAtomValue } from 'jotai';
3
+ import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
4
4
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
5
5
  import { chatAtom, lastAssistantMessageAtom } from '@envive-ai/react-hooks/atoms/chat';
6
6
 
7
7
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
8
- import { useCallback, useMemo } from 'react';
8
+
9
+ import { ChatElementDisplayLocationV3 } from '@envive-ai/react-hooks/application/models';
10
+ import { useCallback, useEffect, useMemo } from 'react';
9
11
  import {
10
- ChatElementDisplayLocationV3,
11
- Message,
12
- MessageRole,
13
- MessageType,
14
- } from '@envive-ai/react-hooks/application/models';
15
- import { v4 as uuid } from 'uuid';
12
+ SpiffyMetricsEventName,
13
+ useAmplitude,
14
+ } from '@envive-ai/react-hooks/contexts/amplitudeContext';
16
15
  import { ChatPreviewProps } from '@envive-ai/react-toolkit-v3/ChatPreview/types/types';
17
16
  import { ChatPreview } from '@envive-ai/react-toolkit-v3/ChatPreview';
18
17
  import { withBaseWidget } from '../../hocs/withBaseWidget/withBaseWidget';
@@ -21,7 +20,7 @@ import { getMessageText } from '../utils/functions';
21
20
  import { ChatPreviewLoadingWidgetWithBaseWidget } from '../ChatPreviewLoadingWidget/ChatPreviewLoadingWidget';
22
21
 
23
22
  const ChatPreviewWidgetHandler = (props: BaseWidgetProps) => {
24
- const handleReply = useSetAtom(handleReplyAtom);
23
+ const { onTypedMessageSubmitted } = useSalesAgent();
25
24
  const { openChat } = useChatToggle();
26
25
 
27
26
  const lastAssistantMessage = useAtomValue(lastAssistantMessageAtom);
@@ -50,39 +49,33 @@ const ChatPreviewWidgetHandler = (props: BaseWidgetProps) => {
50
49
  'messageText' | 'promptButtonTexts' | 'textFieldPlaceholderText' | 'titleLabel'
51
50
  >;
52
51
 
52
+ const { trackEvent } = useAmplitude();
53
+
54
+ useEffect(() => {
55
+ trackEvent({
56
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
57
+ eventProps: {
58
+ widget_config_id: widgetConfigId,
59
+ widget_type: WidgetTypeV3.ChatPreviewV3,
60
+ },
61
+ });
62
+ }, [trackEvent, widgetConfigId]);
63
+
53
64
  const logoSrc = uiConfig?.lookAndFeel?.widgetLogoSrc;
54
65
 
55
66
  const hideLogo = uiConfig?.lookAndFeel?.hideWidgetLogo;
56
67
 
57
68
  const handlePromptButtonClick = useCallback(
58
69
  (text: string) => {
59
- const newMessage: Message = {
60
- id: uuid(),
61
- role: MessageRole.User,
62
- type: MessageType.QueryTyped,
63
- createdAt: new Date().toISOString(),
64
- metadata: { content: text },
65
- };
66
- handleReply({ message: newMessage, userTyped: false });
70
+ onTypedMessageSubmitted({ query: text, userTyped: false });
67
71
  openChat(ChatElementDisplayLocationV3.CHAT_PREVIEW_PROMPT_BUTTON);
68
72
  },
69
- [handleReply, openChat],
73
+ [onTypedMessageSubmitted, openChat],
70
74
  );
71
75
 
72
- const handleTextFieldClick = useCallback(
73
- (text: string) => {
74
- const newMessage: Message = {
75
- id: uuid(),
76
- role: MessageRole.User,
77
- type: MessageType.QueryTyped,
78
- createdAt: new Date().toISOString(),
79
- metadata: { content: text },
80
- };
81
- handleReply({ message: newMessage, userTyped: false });
82
- openChat(ChatElementDisplayLocationV3.CHAT_PREVIEW_TEXT_FIELD);
83
- },
84
- [handleReply, openChat],
85
- );
76
+ const handleTextFieldClick = useCallback(() => {
77
+ openChat(ChatElementDisplayLocationV3.CHAT_PREVIEW_TEXT_FIELD);
78
+ }, [openChat]);
86
79
 
87
80
  if (isLoading) {
88
81
  return (
@@ -9,6 +9,7 @@ export interface FloatingChatOverlayProps {
9
9
  onClose: () => void;
10
10
  className?: string;
11
11
  dataTestId?: string;
12
+ previewMode?: boolean;
12
13
  }
13
14
 
14
15
  export const FloatingChatOverlay = ({
@@ -17,13 +18,14 @@ export const FloatingChatOverlay = ({
17
18
  onClose,
18
19
  className,
19
20
  dataTestId,
21
+ previewMode = false,
20
22
  }: FloatingChatOverlayProps) => {
21
23
  const overlayClasses = classNames(
22
24
  'envive-floating-chat-overlay',
23
- 'envive-tw-fixed',
25
+ previewMode ? 'envive-tw-absolute' : 'envive-tw-fixed',
24
26
  'envive-tw-top-0',
25
27
  'envive-tw-left-0',
26
- 'envive-tw-h-screen',
28
+ previewMode ? 'envive-tw-h-full' : 'envive-tw-h-screen',
27
29
  'envive-tw-w-full',
28
30
  'envive-tw-z-[2147483647]',
29
31
  className,
@@ -67,14 +69,16 @@ export const FloatingChatOverlay = ({
67
69
  transition={{ duration: 0.3 }}
68
70
  data-testid={dataTestId}
69
71
  >
70
- <motion.div
71
- className={backdropClasses}
72
- initial={{ opacity: 0 }}
73
- animate={{ opacity: 0.5 }}
74
- exit={{ opacity: 0 }}
75
- transition={{ duration: 0.3 }}
76
- aria-label="Close chat"
77
- />
72
+ {!previewMode && (
73
+ <motion.div
74
+ className={backdropClasses}
75
+ initial={{ opacity: 0 }}
76
+ animate={{ opacity: 0.5 }}
77
+ exit={{ opacity: 0 }}
78
+ transition={{ duration: 0.3 }}
79
+ aria-label="Close chat"
80
+ />
81
+ )}
78
82
  <div
79
83
  className={overlayContentClasses}
80
84
  onClick={onClose}
@@ -88,11 +92,11 @@ export const FloatingChatOverlay = ({
88
92
  aria-label="Close floating chat"
89
93
  >
90
94
  <motion.div
91
- initial={{ opacity: 0, x: 512 }}
95
+ initial={{ opacity: 0, x: previewMode ? 0 : 512 }}
92
96
  animate={{ opacity: 1, x: 0 }}
93
- exit={{ opacity: 0, x: 512 }}
97
+ exit={{ opacity: 0, x: previewMode ? 0 : 512 }}
94
98
  transition={{ duration: 0.3 }}
95
- style={{ height: '100dvh', cursor: 'default' }}
99
+ style={{ height: previewMode ? '100%' : '100dvh', cursor: 'default' }}
96
100
  className="envive-tw-shadow-md"
97
101
  onClick={e => e.stopPropagation()}
98
102
  >
@@ -104,6 +108,11 @@ export const FloatingChatOverlay = ({
104
108
  </AnimatePresence>
105
109
  );
106
110
 
111
+ // In preview mode, render inline. Otherwise use portal to body
112
+ if (previewMode) {
113
+ return chatWrapper;
114
+ }
115
+
107
116
  // Render using portal to body to ensure it's on top of everything
108
117
  if (typeof document !== 'undefined') {
109
118
  return createPortal(chatWrapper, document.body);
@@ -1,9 +1,13 @@
1
- import { useMemo } from 'react';
1
+ import { useEffect, useMemo, useRef } from 'react';
2
2
  import {
3
3
  FloatingChatConfig,
4
4
  LookAndFeelConfig,
5
5
  WidgetTypeV3,
6
6
  } from '@envive-ai/react-hooks/contexts/typesV3';
7
+ import {
8
+ SpiffyMetricsEventName,
9
+ useAmplitude,
10
+ } from '@envive-ai/react-hooks/contexts/amplitudeContext';
7
11
  import { FloatingChat } from '@envive-ai/react-toolkit-v3/FloatingChat';
8
12
  import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
9
13
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
@@ -23,8 +27,15 @@ import useGetWidgetStatus from '../hooks/useGetWidgetStatus';
23
27
  import { useUnifiedCXButton } from '../../CXIntegration/hooks/useUnifiedCXButton';
24
28
  import { FLOATING_BUTTON_ID } from './constants';
25
29
  import { CustomerServiceType } from '../../CXIntegration/types';
30
+ import { useFloatingButtonVisibility } from './hooks/useFloatingButtonVisibility';
31
+
32
+ interface FloatingChatWidgetHandlerProps extends BaseWidgetProps {
33
+ previewButtonOnly?: boolean;
34
+ previewChatAlwaysOpen?: boolean;
35
+ }
26
36
 
27
- const FloatingChatWidgetHandler = (props: BaseWidgetProps) => {
37
+ const FloatingChatWidgetHandler = (props: FloatingChatWidgetHandlerProps) => {
38
+ const { previewButtonOnly, previewChatAlwaysOpen } = props;
28
39
  const salesAgentData = useSalesAgent();
29
40
 
30
41
  const { userHasInteractedValue } = useGetWidgetStatus();
@@ -46,9 +57,23 @@ const FloatingChatWidgetHandler = (props: BaseWidgetProps) => {
46
57
  });
47
58
 
48
59
  const { isSwitchEnabled, toggle } = unifiedCXButton ?? {};
60
+
61
+ const { floatingButton } = uiConfig ?? {};
62
+
49
63
  // TODO: Get hardcopy content
50
64
  const { isOpen, openChat, closeChat } = useChatToggle();
51
65
 
66
+ const { shouldShowFloatingButton } = useFloatingButtonVisibility({
67
+ floatingButtonShowConfig: floatingButton?.showOption as FloatingButtonShow,
68
+ isChatOpen: isOpen,
69
+ userHasInteracted: userHasInteractedValue,
70
+ });
71
+
72
+ // Override isOpen for preview modes
73
+ const effectiveIsOpen = previewChatAlwaysOpen ? true : isOpen;
74
+ const effectiveShouldRenderFloatingButton = !isOpen && shouldShowFloatingButton;
75
+ const buttonShouldRender = previewButtonOnly ? true : effectiveShouldRenderFloatingButton;
76
+
52
77
  const theme = useMemo(() => {
53
78
  if (isUiConfigLoading || !uiConfig) {
54
79
  return Theme.STANDARD;
@@ -57,52 +82,94 @@ const FloatingChatWidgetHandler = (props: BaseWidgetProps) => {
57
82
  return (uiConfig?.lookAndFeel?.theme as Theme) ?? Theme.GLOBAL_CUSTOM;
58
83
  }, [isUiConfigLoading, uiConfig]);
59
84
 
60
- const { floatingButton } = uiConfig ?? {};
85
+ const { trackEvent } = useAmplitude();
86
+ const hasTrackedEvent = useRef(false);
87
+ // When we add support for initially hidden floating buttons, we will need to update this to handle that case
88
+ useEffect(() => {
89
+ if (buttonShouldRender && !hasTrackedEvent.current) {
90
+ trackEvent({
91
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
92
+ eventProps: {
93
+ widget_config_id: 'floating-button',
94
+ widget_type: WidgetTypeV3.FloatingButtonV3,
95
+ },
96
+ });
97
+ hasTrackedEvent.current = true;
98
+ }
99
+ }, [trackEvent, buttonShouldRender]);
61
100
 
62
101
  return (
63
102
  <>
64
- <FloatingChatOverlay
65
- isOpened={isOpen}
66
- onClose={() => closeChat(ChatElementDisplayLocationV3.FLOATING_CHAT_OVERLAY)}
67
- >
68
- <FloatingChat
69
- theme={theme}
70
- salesAgentData={salesAgentData}
71
- floatingChatConfig={uiConfig?.floatingChat ?? ({} as FloatingChatConfig)}
72
- lookAndFeelConfig={uiConfig?.lookAndFeel ?? ({} as LookAndFeelConfig)}
73
- isCXButtonSwitchEnabled={!!isSwitchEnabled?.()}
74
- onToggleCXButton={toggle}
75
- onClose={() => closeChat(ChatElementDisplayLocationV3.FLOATING_CHAT_CLOSE_BUTTON)}
76
- />
77
- </FloatingChatOverlay>
103
+ {/* Render chat when always open preview OR when normally open */}
104
+ {(previewChatAlwaysOpen || effectiveIsOpen) && !previewButtonOnly && (
105
+ <FloatingChatOverlay
106
+ isOpened={effectiveIsOpen}
107
+ onClose={
108
+ previewChatAlwaysOpen
109
+ ? () => {}
110
+ : () => closeChat(ChatElementDisplayLocationV3.FLOATING_CHAT_OVERLAY)
111
+ }
112
+ previewMode={!!previewChatAlwaysOpen}
113
+ >
114
+ <FloatingChat
115
+ theme={theme}
116
+ salesAgentData={salesAgentData}
117
+ floatingChatConfig={uiConfig?.floatingChat ?? ({} as FloatingChatConfig)}
118
+ lookAndFeelConfig={uiConfig?.lookAndFeel ?? ({} as LookAndFeelConfig)}
119
+ isCXButtonSwitchEnabled={!!isSwitchEnabled?.()}
120
+ isFloatingChatOpen={effectiveIsOpen}
121
+ onToggleCXButton={toggle}
122
+ onClose={
123
+ previewChatAlwaysOpen
124
+ ? () => {}
125
+ : () => closeChat(ChatElementDisplayLocationV3.FLOATING_CHAT_CLOSE_BUTTON)
126
+ }
127
+ />
128
+ </FloatingChatOverlay>
129
+ )}
78
130
 
79
- {!isOpen && (
131
+ {/* Render button when preview button only OR when chat is closed */}
132
+ {buttonShouldRender && (
80
133
  <FloatingButton
81
134
  id={FLOATING_BUTTON_ID}
82
135
  variant={floatingButton?.style as FloatingButtonVariant}
83
136
  mode={floatingButton?.mode as FloatingButtonMode}
84
137
  backgroundColor={floatingButton?.backgroundColor as FloatingButtonBackgroundColor}
85
- onClick={() => openChat(ChatElementDisplayLocationV3.FLOATING_BUTTON)}
138
+ onClick={
139
+ previewButtonOnly
140
+ ? () => {}
141
+ : () => openChat(ChatElementDisplayLocationV3.FLOATING_BUTTON)
142
+ }
86
143
  customIcon={floatingButton?.iconSVGSrc}
87
144
  show={floatingButton?.showOption as FloatingButtonShow}
88
145
  location={floatingButton?.position as FloatingButtonLocation}
89
146
  hasInteractionHappened={userHasInteractedValue}
90
147
  ariaLabel="Open chat"
148
+ previewMode={!!previewButtonOnly}
91
149
  />
92
150
  )}
93
151
  </>
94
152
  );
95
153
  };
96
154
 
97
- const FloatingChatWidgetWithBaseWidget = withBaseWidget<BaseWidgetProps>(FloatingChatWidgetHandler);
155
+ const FloatingChatWidgetWithBaseWidget =
156
+ withBaseWidget<FloatingChatWidgetHandlerProps>(FloatingChatWidgetHandler);
98
157
 
99
- export interface FloatingChatWidgetProps {}
158
+ export interface FloatingChatWidgetProps {
159
+ previewButtonOnly?: boolean;
160
+ previewChatAlwaysOpen?: boolean;
161
+ }
100
162
 
101
- export const FloatingChatWidget = () => {
163
+ export const FloatingChatWidget = ({
164
+ previewButtonOnly,
165
+ previewChatAlwaysOpen,
166
+ }: FloatingChatWidgetProps = {}) => {
102
167
  return (
103
168
  <FloatingChatWidgetWithBaseWidget
104
169
  widgetType={WidgetTypeV3.FloatingChatV3}
105
170
  widgetConfigId="fake-widget-config-id"
171
+ previewButtonOnly={previewButtonOnly}
172
+ previewChatAlwaysOpen={previewChatAlwaysOpen}
106
173
  />
107
174
  );
108
175
  };
@@ -0,0 +1,43 @@
1
+ import { useMemo } from 'react';
2
+ import { useAtomValue } from 'jotai';
3
+ import { featureFlagServiceAtom } from '@envive-ai/react-hooks/atoms/org';
4
+ import { FeatureGates } from '@envive-ai/react-hooks/application/models';
5
+ import { FloatingButtonShow } from '@envive-ai/react-toolkit-v3/FloatingButton';
6
+
7
+ export interface UseFloatingButtonVisibilityProps {
8
+ floatingButtonShowConfig?: FloatingButtonShow;
9
+ isChatOpen: boolean;
10
+ userHasInteracted: boolean;
11
+ }
12
+
13
+ export interface UseFloatingButtonVisibilityReturn {
14
+ shouldShowFloatingButton: boolean;
15
+ isSalesAgentEnabled: boolean;
16
+ }
17
+
18
+ export const useFloatingButtonVisibility = ({
19
+ floatingButtonShowConfig = FloatingButtonShow.ALWAYS,
20
+ isChatOpen,
21
+ userHasInteracted,
22
+ }: UseFloatingButtonVisibilityProps): UseFloatingButtonVisibilityReturn => {
23
+ const featureFlagService = useAtomValue(featureFlagServiceAtom);
24
+
25
+ const isSalesAgentEnabled = useMemo(
26
+ () =>
27
+ featureFlagService?.featureFlagService?.isFeatureGateEnabled(
28
+ FeatureGates.IsSalesAgentEnabled,
29
+ ),
30
+ [featureFlagService],
31
+ );
32
+
33
+ const shouldShowFloatingButton =
34
+ (isSalesAgentEnabled || isSalesAgentEnabled === undefined) &&
35
+ !isChatOpen &&
36
+ (floatingButtonShowConfig === FloatingButtonShow.ALWAYS ||
37
+ (floatingButtonShowConfig === FloatingButtonShow.POST_INTERACTION && userHasInteracted));
38
+
39
+ return {
40
+ shouldShowFloatingButton,
41
+ isSalesAgentEnabled,
42
+ };
43
+ };
@@ -2,19 +2,16 @@ import {
2
2
  PromptButtonCarouselWithImageWidgetV3Config,
3
3
  WidgetTypeV3,
4
4
  } from '@envive-ai/react-hooks/contexts/typesV3';
5
- import { useSetAtom } from 'jotai';
6
- import { handleReplyAtom } from '@envive-ai/react-hooks/atoms/chat/replies';
5
+ import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
7
6
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
8
7
 
9
8
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
10
- import { useCallback } from 'react';
9
+ import { useCallback, useEffect } from 'react';
11
10
  import {
12
- ChatElementDisplayLocationV3,
13
- Message,
14
- MessageRole,
15
- MessageType,
16
- } from '@envive-ai/react-hooks/application/models';
17
- import { v4 as uuid } from 'uuid';
11
+ SpiffyMetricsEventName,
12
+ useAmplitude,
13
+ } from '@envive-ai/react-hooks/contexts/amplitudeContext';
14
+ import { ChatElementDisplayLocationV3 } from '@envive-ai/react-hooks/application/models';
18
15
  import {
19
16
  PromptButtonCarouselWithImage,
20
17
  PromptButtonCarouselWithImageProps,
@@ -23,10 +20,10 @@ import { BaseWidgetProps } from '../../hocs/withBaseWidget/types';
23
20
  import { withBaseWidget } from '../../hocs/withBaseWidget/withBaseWidget';
24
21
 
25
22
  const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
26
- const handleReply = useSetAtom(handleReplyAtom);
23
+ const { onTypedMessageSubmitted } = useSalesAgent();
27
24
  const { openChat } = useChatToggle();
28
25
 
29
- const { hardcopyContent, widgetConfig, isLoading } = props;
26
+ const { hardcopyContent, widgetConfig, isLoading, widgetConfigId } = props;
30
27
 
31
28
  const promptButtonCarouselWithImageWidgetConfig =
32
29
  widgetConfig as PromptButtonCarouselWithImageWidgetV3Config;
@@ -42,35 +39,29 @@ const PromptButtonCarouselWithImageWidgetHandler = (props: BaseWidgetProps) => {
42
39
  'title' | 'promptButtonsTexts' | 'textFieldPlaceholder'
43
40
  >;
44
41
 
42
+ const { trackEvent } = useAmplitude();
43
+
44
+ useEffect(() => {
45
+ trackEvent({
46
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
47
+ eventProps: {
48
+ widget_config_id: widgetConfigId,
49
+ widget_type: WidgetTypeV3.PromptButtonCarouselWithImageV3,
50
+ },
51
+ });
52
+ }, [trackEvent, widgetConfigId]);
53
+
45
54
  const handlePromptButtonClick = useCallback(
46
55
  (text: string) => {
47
- const newMessage: Message = {
48
- id: uuid(),
49
- role: MessageRole.User,
50
- type: MessageType.QueryTyped,
51
- createdAt: new Date().toISOString(),
52
- metadata: { content: text },
53
- };
54
- handleReply({ message: newMessage, userTyped: false });
56
+ onTypedMessageSubmitted({ query: text, userTyped: false });
55
57
  openChat(ChatElementDisplayLocationV3.PROMPT_BUTTON_CAROUSEL_WITH_IMAGE_PROMPT_BUTTON);
56
58
  },
57
- [handleReply, openChat],
59
+ [onTypedMessageSubmitted, openChat],
58
60
  );
59
61
 
60
- const handleTextFieldClick = useCallback(
61
- (text: string) => {
62
- const newMessage: Message = {
63
- id: uuid(),
64
- role: MessageRole.User,
65
- type: MessageType.QueryTyped,
66
- createdAt: new Date().toISOString(),
67
- metadata: { content: text },
68
- };
69
- handleReply({ message: newMessage, userTyped: false });
70
- openChat(ChatElementDisplayLocationV3.PROMPT_BUTTON_CAROUSEL_WITH_IMAGE_TEXT_FIELD);
71
- },
72
- [handleReply, openChat],
73
- );
62
+ const handleTextFieldClick = useCallback(() => {
63
+ openChat(ChatElementDisplayLocationV3.PROMPT_BUTTON_CAROUSEL_WITH_IMAGE_TEXT_FIELD);
64
+ }, [openChat]);
74
65
 
75
66
  return (
76
67
  <PromptButtonCarouselWithImage
@@ -3,16 +3,14 @@ import {
3
3
  WidgetTypeV3,
4
4
  } from '@envive-ai/react-hooks/contexts/typesV3';
5
5
  import { PromptButtonVariant } from '@envive-ai/react-toolkit-v3/PromptButton/types';
6
- import { useCallback } from 'react';
7
- import { v4 as uuid } from 'uuid';
6
+ import { ChatElementDisplayLocationV3 } from '@envive-ai/react-hooks/application/models';
7
+ import { useSalesAgent } from '@envive-ai/react-hooks/contexts/salesAgentContext';
8
+ import { useCallback, useEffect } from 'react';
8
9
  import {
9
- ChatElementDisplayLocationV3,
10
- Message,
11
- MessageRole,
12
- MessageType,
13
- } from '@envive-ai/react-hooks/application/models';
14
- import { useSetAtom } from 'jotai';
15
- import { handleReplyAtom } from '@envive-ai/react-hooks/atoms/chat/replies';
10
+ SpiffyMetricsEventName,
11
+ useAmplitude,
12
+ } from '@envive-ai/react-hooks/contexts/amplitudeContext';
13
+
16
14
  import { useChatToggle } from '@envive-ai/react-hooks/hooks/ChatToggle';
17
15
  import { AnimationSpeed } from '@envive-ai/react-toolkit-v3/PromptCarousel/types/types';
18
16
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
@@ -28,10 +26,10 @@ const mockButtonTexts = [
28
26
  ];
29
27
 
30
28
  const PromptCarouselWidgetHandler = (props: BaseWidgetProps) => {
31
- const handleReply = useSetAtom(handleReplyAtom);
29
+ const { onTypedMessageSubmitted } = useSalesAgent();
32
30
  const { openChat } = useChatToggle();
33
31
 
34
- const { hardcopyContent, widgetConfig, isLoading } = props;
32
+ const { hardcopyContent, widgetConfig, isLoading, widgetConfigId } = props;
35
33
 
36
34
  const promptButtonTexts = (hardcopyContent?.values?.promptButtonTexts as string[]) || [];
37
35
  const buttonTexts = isLoading ? mockButtonTexts : promptButtonTexts;
@@ -46,19 +44,24 @@ const PromptCarouselWidgetHandler = (props: BaseWidgetProps) => {
46
44
  ? AnimationSpeed.FAST
47
45
  : promptCarouselWidgetConfig?.animationSpeed;
48
46
 
47
+ const { trackEvent } = useAmplitude();
48
+
49
+ useEffect(() => {
50
+ trackEvent({
51
+ eventName: SpiffyMetricsEventName.ChatComponentVisible,
52
+ eventProps: {
53
+ widget_config_id: widgetConfigId,
54
+ widget_type: WidgetTypeV3.PromptCarouselV3,
55
+ },
56
+ });
57
+ }, [trackEvent, widgetConfigId]);
58
+
49
59
  const handleButtonClick = useCallback(
50
60
  (text: string) => {
51
- const newMessage: Message = {
52
- id: uuid(),
53
- role: MessageRole.User,
54
- type: MessageType.QueryTyped,
55
- createdAt: new Date().toISOString(),
56
- metadata: { content: text },
57
- };
58
- handleReply({ message: newMessage, userTyped: false });
61
+ onTypedMessageSubmitted({ query: text, userTyped: false });
59
62
  openChat(ChatElementDisplayLocationV3.PROMPT_CAROUSEL);
60
63
  },
61
- [handleReply, openChat],
64
+ [onTypedMessageSubmitted, openChat],
62
65
  );
63
66
 
64
67
  return (