@envive-ai/react-toolkit-v3 0.3.18 → 0.3.20

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 (256) hide show
  1. package/dist/AnimatedText/AnimatedText.d.cts +3 -3
  2. package/dist/AnimatedText/AnimatedText.d.ts +3 -3
  3. package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.cts +2 -2
  4. package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.ts +2 -2
  5. package/dist/CSSVariablesEditor/hooks/useGetCssVariablesOptions.cjs +1 -1
  6. package/dist/CSSVariablesEditor/hooks/useGetCssVariablesOptions.js +1 -1
  7. package/dist/CSSVariablesEditor/hooks/useGetCurrentMerchantColors.cjs +1 -1
  8. package/dist/CSSVariablesEditor/hooks/useGetCurrentMerchantColors.js +1 -1
  9. package/dist/CSSVariablesEditor/hooks/useGetDefaultCssVariables.cjs +1 -1
  10. package/dist/CSSVariablesEditor/hooks/useGetDefaultCssVariables.js +1 -1
  11. package/dist/CSSVariablesEditor/hooks/useHandleUpdateCssVars.cjs +1 -1
  12. package/dist/CSSVariablesEditor/hooks/useHandleUpdateCssVars.js +1 -1
  13. package/dist/Carousel/Carousel.d.ts +2 -2
  14. package/dist/Carousel/components/Badge.cjs +1 -1
  15. package/dist/Carousel/components/Badge.js +1 -1
  16. package/dist/Carousel/components/Container.cjs +1 -1
  17. package/dist/Carousel/components/Container.js +1 -1
  18. package/dist/ChatFooter/ChatFooter.cjs +1 -1
  19. package/dist/ChatFooter/ChatFooter.d.cts +2 -2
  20. package/dist/ChatFooter/ChatFooter.d.ts +2 -2
  21. package/dist/ChatFooter/ChatFooter.js +1 -1
  22. package/dist/ChatFooter/components/Layout.cjs +1 -1
  23. package/dist/ChatFooter/components/Layout.js +1 -1
  24. package/dist/ChatFooter/components/index.d.cts +5 -5
  25. package/dist/ChatFooter/components/index.d.ts +5 -5
  26. package/dist/ChatHeader/ChatHeader.d.cts +2 -2
  27. package/dist/ChatHeader/hooks/useGetLayoutProperties.cjs +1 -1
  28. package/dist/ChatHeader/hooks/useGetLayoutProperties.js +1 -1
  29. package/dist/ChatPreview/ChatPreview.d.cts +2 -2
  30. package/dist/ChatPreview/ChatPreview.d.ts +2 -2
  31. package/dist/ChatPreviewComparison/ChatPreviewComparison.d.cts +2 -2
  32. package/dist/ChatPreviewComparison/ChatPreviewComparison.d.ts +2 -2
  33. package/dist/ChatPreviewComparison/components/Layout.cjs +4 -4
  34. package/dist/ChatPreviewComparison/components/Layout.js +2 -2
  35. package/dist/ChatPreviewComparison/components/Message.cjs +2 -2
  36. package/dist/ChatPreviewLoading/ChatPreviewLoading.d.cts +2 -2
  37. package/dist/ChatPreviewLoading/ChatPreviewLoading.d.ts +2 -2
  38. package/dist/Container/Container.d.cts +176 -176
  39. package/dist/Container/Container.d.ts +176 -176
  40. package/dist/DesignTokens/DesignTokensComponent.d.cts +2 -2
  41. package/dist/DesignTokens/DesignTokensComponent.d.ts +2 -2
  42. package/dist/DesignTokens/components/FontFamily.cjs +1 -1
  43. package/dist/DesignTokens/components/FontFamily.js +1 -1
  44. package/dist/DesignTokens/components/FontSize.cjs +1 -1
  45. package/dist/DesignTokens/components/FontSize.js +1 -1
  46. package/dist/DesignTokens/components/FontWeight.cjs +1 -1
  47. package/dist/DesignTokens/components/FontWeight.js +1 -1
  48. package/dist/DesignTokens/components/LetterSpacing.cjs +1 -1
  49. package/dist/DesignTokens/components/LetterSpacing.js +1 -1
  50. package/dist/DesignTokens/components/LineHeight.cjs +1 -1
  51. package/dist/DesignTokens/components/LineHeight.js +1 -1
  52. package/dist/DesignTokens/components/Typography.cjs +1 -1
  53. package/dist/DesignTokens/components/Typography.js +1 -1
  54. package/dist/Disclaimer/components/Container.cjs +1 -1
  55. package/dist/Disclaimer/components/Container.js +1 -1
  56. package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.cts +2 -2
  57. package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.ts +2 -2
  58. package/dist/DocumentRetrievalCard/components/Image.cjs +1 -1
  59. package/dist/DocumentRetrievalCard/components/Image.js +1 -1
  60. package/dist/DocumentRetrievalCard/components/Layout.cjs +1 -1
  61. package/dist/DocumentRetrievalCard/components/Layout.js +1 -1
  62. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.cjs +1 -1
  63. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.js +1 -1
  64. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Label.cjs +1 -1
  65. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Label.js +1 -1
  66. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Layout.cjs +1 -1
  67. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Layout.js +1 -1
  68. package/dist/FloatingButton/FloatingButton.d.cts +2 -2
  69. package/dist/FloatingButton/FloatingButton.d.ts +2 -2
  70. package/dist/FloatingButton/components/Button.cjs +1 -1
  71. package/dist/FloatingButton/components/Button.js +1 -1
  72. package/dist/FloatingButton/components/Container.cjs +1 -1
  73. package/dist/FloatingButton/components/Container.js +1 -1
  74. package/dist/FloatingButton/components/Wrapper.cjs +1 -1
  75. package/dist/FloatingButton/components/Wrapper.js +1 -1
  76. package/dist/FloatingChat/FloatingChat.cjs +64 -25
  77. package/dist/FloatingChat/FloatingChat.d.cts +4 -3
  78. package/dist/FloatingChat/FloatingChat.d.ts +4 -3
  79. package/dist/FloatingChat/FloatingChat.js +66 -27
  80. package/dist/FloatingChat/components/AgentMessage.cjs +3 -9
  81. package/dist/FloatingChat/components/AgentMessage.js +3 -9
  82. package/dist/FloatingChat/components/ChatMessages.cjs +2 -2
  83. package/dist/FloatingChat/components/ChatMessages.js +2 -2
  84. package/dist/FloatingChat/components/Layout.cjs +3 -2
  85. package/dist/FloatingChat/components/Layout.js +3 -2
  86. package/dist/FloatingChat/components/ProductResultsModal.cjs +62 -0
  87. package/dist/FloatingChat/components/ProductResultsModal.js +60 -0
  88. package/dist/FloatingChat/components/ResultsGridView.cjs +86 -0
  89. package/dist/FloatingChat/components/ResultsGridView.js +82 -0
  90. package/dist/FloatingChat/components/SalesAgentBadgeContent.cjs +1 -1
  91. package/dist/FloatingChat/components/SalesAgentBadgeContent.js +1 -1
  92. package/dist/FloatingChat/components/SlideChatContent.cjs +47 -0
  93. package/dist/FloatingChat/components/SlideChatContent.js +46 -0
  94. package/dist/FloatingChat/components/index.cjs +6 -0
  95. package/dist/FloatingChat/components/index.js +6 -0
  96. package/dist/FloatingChat/hooks/useProductResultsView.cjs +49 -0
  97. package/dist/FloatingChat/hooks/useProductResultsView.js +48 -0
  98. package/dist/FloatingChat/hooks/useSnapSetup.cjs +2 -2
  99. package/dist/FloatingChat/types/types.d.cts +4 -0
  100. package/dist/FloatingChat/types/types.d.ts +4 -0
  101. package/dist/FloatingChat/utils/functions.cjs +40 -17
  102. package/dist/FloatingChat/utils/functions.js +38 -17
  103. package/dist/FloatingChat/utils/trackProductCardInteraction.cjs +18 -0
  104. package/dist/FloatingChat/utils/trackProductCardInteraction.js +17 -0
  105. package/dist/Form/Form.cjs +1 -1
  106. package/dist/Form/Form.js +1 -1
  107. package/dist/Form/components/Layout.cjs +1 -1
  108. package/dist/Form/components/Layout.js +1 -1
  109. package/dist/Form/components/SubmitButtonItem.cjs +1 -1
  110. package/dist/Form/components/SubmitButtonItem.js +1 -1
  111. package/dist/Form/components/TextFieldItem.cjs +1 -1
  112. package/dist/Form/components/TextFieldItem.js +1 -1
  113. package/dist/FullPageSalesAgent/FullPageSalesAgent.cjs +30 -8
  114. package/dist/FullPageSalesAgent/FullPageSalesAgent.d.cts +2 -2
  115. package/dist/FullPageSalesAgent/FullPageSalesAgent.d.ts +2 -2
  116. package/dist/FullPageSalesAgent/FullPageSalesAgent.js +30 -8
  117. package/dist/FullPageSalesAgent/components/Layout.cjs +3 -2
  118. package/dist/FullPageSalesAgent/components/Layout.js +3 -2
  119. package/dist/FullPageSalesAgent/hooks/useIsMobile.cjs +2 -2
  120. package/dist/Image/Image.cjs +1 -1
  121. package/dist/Image/Image.d.cts +2 -2
  122. package/dist/Image/Image.d.ts +2 -2
  123. package/dist/Image/Image.js +1 -1
  124. package/dist/ImageGallery/ImageGallery.d.cts +2 -2
  125. package/dist/ImageGallery/ImageGallery.d.ts +2 -2
  126. package/dist/ImageGallery/components/Layout.cjs +3 -3
  127. package/dist/ImageGallery/components/Layout.js +1 -1
  128. package/dist/ImageGallery/utils/functions.cjs +1 -1
  129. package/dist/ImageGallery/utils/functions.js +1 -1
  130. package/dist/MarkdownProcessor/MarkdownProcessor.d.ts +2 -2
  131. package/dist/Message/components/Layout.cjs +1 -1
  132. package/dist/Message/components/Layout.js +1 -1
  133. package/dist/OrderLookupCard/OrderLookupCard.cjs +1 -1
  134. package/dist/OrderLookupCard/OrderLookupCard.js +1 -1
  135. package/dist/OrderLookupCard/components/Layout.cjs +1 -1
  136. package/dist/OrderLookupCard/components/Layout.js +1 -1
  137. package/dist/OrderLookupCard/components/MoreProductsOverlay.cjs +1 -1
  138. package/dist/OrderLookupCard/components/MoreProductsOverlay.js +1 -1
  139. package/dist/OrderLookupCard/components/ProductImageGridItem.cjs +1 -1
  140. package/dist/OrderLookupCard/components/ProductImageGridItem.js +1 -1
  141. package/dist/OrderLookupCard/components/ProductImageItem.cjs +1 -1
  142. package/dist/OrderLookupCard/components/ProductImageItem.js +1 -1
  143. package/dist/OrderLookupCard/components/ProductImagesGrid.cjs +1 -1
  144. package/dist/OrderLookupCard/components/ProductImagesGrid.js +1 -1
  145. package/dist/OrderLookupCard/components/StatusLabel.cjs +1 -1
  146. package/dist/OrderLookupCard/components/StatusLabel.js +1 -1
  147. package/dist/OrderLookupCard/components/TrackOrderLink.cjs +1 -1
  148. package/dist/OrderLookupCard/components/TrackOrderLink.js +1 -1
  149. package/dist/ProductCard/ProductCard.cjs +1 -1
  150. package/dist/ProductCard/ProductCard.d.cts +2 -2
  151. package/dist/ProductCard/ProductCard.d.ts +2 -2
  152. package/dist/ProductCard/ProductCard.js +1 -1
  153. package/dist/PromptButton/PromptButton.cjs +1 -1
  154. package/dist/PromptButton/PromptButton.d.ts +2 -2
  155. package/dist/PromptButton/PromptButton.js +1 -1
  156. package/dist/PromptButton/components/Layout.cjs +1 -1
  157. package/dist/PromptButton/components/Layout.js +1 -1
  158. package/dist/PromptButton/components/Loading.cjs +1 -1
  159. package/dist/PromptButton/components/Loading.js +1 -1
  160. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.cts +2 -2
  161. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.ts +2 -2
  162. package/dist/PromptButtonCarouselWithImage/components/Layout.cjs +3 -3
  163. package/dist/PromptButtonCarouselWithImage/components/Layout.js +1 -1
  164. package/dist/PromptButtonCarouselWithImage/components/PromptButtonsCarousel.cjs +2 -2
  165. package/dist/PromptButtonCarouselWithImage/components/Skeleton.cjs +1 -1
  166. package/dist/PromptButtonCarouselWithImage/components/Skeleton.js +1 -1
  167. package/dist/PromptCarousel/PromptCarousel.cjs +3 -3
  168. package/dist/PromptCarousel/PromptCarousel.d.cts +2 -2
  169. package/dist/PromptCarousel/PromptCarousel.d.ts +2 -2
  170. package/dist/PromptCarousel/PromptCarousel.js +1 -1
  171. package/dist/ReviewCard/ReviewCard.d.cts +2 -2
  172. package/dist/ReviewCard/ReviewCard.d.ts +2 -2
  173. package/dist/ReviewCard/components/Container.cjs +1 -1
  174. package/dist/ReviewCard/components/Container.js +1 -1
  175. package/dist/ReviewCard/components/Rating.cjs +1 -1
  176. package/dist/ReviewCard/components/Rating.js +1 -1
  177. package/dist/ReviewCard/components/ReadMoreButton.cjs +1 -1
  178. package/dist/ReviewCard/components/ReadMoreButton.js +1 -1
  179. package/dist/ReviewCard/components/index.d.cts +2 -2
  180. package/dist/ReviewCard/components/index.d.ts +4 -4
  181. package/dist/SalesAgentProductCard/SalesAgentProductCard.d.cts +2 -2
  182. package/dist/SalesAgentProductCard/SalesAgentProductCard.d.ts +2 -2
  183. package/dist/SalesAgentProductCard/components/Container.cjs +1 -1
  184. package/dist/SalesAgentProductCard/components/Container.js +1 -1
  185. package/dist/SalesAgentProductCard/components/ProductImage.cjs +1 -1
  186. package/dist/SalesAgentProductCard/components/ProductImage.js +1 -1
  187. package/dist/SalesAgentProductCard/components/ProductName.cjs +1 -1
  188. package/dist/SalesAgentProductCard/components/ProductName.js +1 -1
  189. package/dist/SalesAgentProductCard/components/index.d.cts +8 -8
  190. package/dist/SalesAgentProductCard/components/index.d.ts +6 -6
  191. package/dist/SocialProof/SocialProof.cjs +2 -2
  192. package/dist/SocialProof/SocialProof.d.cts +2 -2
  193. package/dist/SocialProof/components/Headline.cjs +1 -1
  194. package/dist/SocialProof/components/Headline.js +1 -1
  195. package/dist/SocialProof/components/LayoutFourHorizontal.cjs +3 -3
  196. package/dist/SocialProof/components/LayoutFourHorizontal.js +1 -1
  197. package/dist/SocialProof/components/LayoutSingle.cjs +2 -2
  198. package/dist/SparkleAnimation/SparkleAnimation.d.cts +2 -2
  199. package/dist/SparkleAnimation/SparkleAnimation.d.ts +2 -2
  200. package/dist/Stack/Stack.d.cts +2 -2
  201. package/dist/TextField/TextField.cjs +1 -1
  202. package/dist/TextField/TextField.js +1 -1
  203. package/dist/TextField/components/Input.cjs +1 -1
  204. package/dist/TextField/components/Input.js +1 -1
  205. package/dist/TextField/components/Layout.cjs +1 -1
  206. package/dist/TextField/components/Layout.js +1 -1
  207. package/dist/TextField/components/SendIcon.cjs +1 -1
  208. package/dist/TextField/components/SendIcon.js +1 -1
  209. package/dist/Title/Title.cjs +1 -1
  210. package/dist/Title/Title.js +1 -1
  211. package/dist/Title/components/Layout.cjs +1 -1
  212. package/dist/Title/components/Layout.js +1 -1
  213. package/dist/TitledPromptCarousel/TitledPromptCarousel.cjs +1 -1
  214. package/dist/TitledPromptCarousel/TitledPromptCarousel.d.cts +2 -2
  215. package/dist/TitledPromptCarousel/TitledPromptCarousel.d.ts +2 -2
  216. package/dist/TitledPromptCarousel/TitledPromptCarousel.js +1 -1
  217. package/dist/Tokens/index.cjs +2 -2
  218. package/dist/Tokens/index.js +2 -2
  219. package/dist/TypingAnimation/TypingAnimation.d.cts +2 -2
  220. package/dist/TypingAnimation/TypingAnimation.d.ts +2 -2
  221. package/dist/Typography/Typography.d.cts +4 -4
  222. package/dist/Typography/Typography.d.ts +4 -4
  223. package/dist/WelcomeMessage/components/Container.cjs +1 -1
  224. package/dist/WelcomeMessage/components/Container.js +1 -1
  225. package/dist/WidgetTextField/WidgetTextField.d.cts +2 -2
  226. package/dist/WidgetTextField/WidgetTextField.d.ts +2 -2
  227. package/dist/WidgetTextField/components/Container.cjs +1 -1
  228. package/dist/WidgetTextField/components/Container.js +1 -1
  229. package/dist/WidgetTextField/components/Skeleton.cjs +1 -1
  230. package/dist/WidgetTextField/components/Skeleton.js +1 -1
  231. package/dist/WidgetWrapper/WidgetWrapper.cjs +1 -1
  232. package/dist/WidgetWrapper/WidgetWrapper.d.cts +2 -2
  233. package/dist/WidgetWrapper/WidgetWrapper.d.ts +2 -2
  234. package/dist/WidgetWrapper/WidgetWrapper.js +1 -1
  235. package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.cts +2 -2
  236. package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.ts +2 -2
  237. package/dist/styles.css +1 -1
  238. package/dist/utils/resolveTheme.cjs +1 -1
  239. package/dist/utils/resolveTheme.js +1 -1
  240. package/dist/utils/useCheckIsMobile.d.cts +9 -0
  241. package/dist/utils/useCheckIsMobile.d.ts +9 -0
  242. package/package.json +5 -1
  243. package/src/components/FloatingChat/FloatingChat.tsx +92 -36
  244. package/src/components/FloatingChat/components/AgentMessage.tsx +2 -12
  245. package/src/components/FloatingChat/components/Layout.tsx +7 -1
  246. package/src/components/FloatingChat/components/ProductResultsModal.tsx +87 -0
  247. package/src/components/FloatingChat/components/ResultsGridView.tsx +117 -0
  248. package/src/components/FloatingChat/components/SlideChatContent.tsx +80 -0
  249. package/src/components/FloatingChat/components/index.ts +6 -0
  250. package/src/components/FloatingChat/hooks/useProductResultsView.ts +73 -0
  251. package/src/components/FloatingChat/types/types.ts +4 -0
  252. package/src/components/FloatingChat/utils/functions.ts +56 -19
  253. package/src/components/FloatingChat/utils/trackProductCardInteraction.ts +26 -0
  254. package/src/components/FullPageSalesAgent/FullPageSalesAgent.tsx +54 -5
  255. package/src/components/FullPageSalesAgent/components/Layout.tsx +7 -1
  256. package/src/logging/logger.ts +33 -8
@@ -0,0 +1,87 @@
1
+ import classNames from 'classnames';
2
+ import { Theme } from '../../../../tokens/theme/theme';
3
+ import { ColorNames } from '../../../models/colorsConfig';
4
+ import { SalesAgentProductCardProps } from '../../SalesAgentProductCard/types/types';
5
+ import { ChatHeader } from '../../ChatHeader';
6
+ import { ChatHeaderOptions, ChatHeaderVariant } from '../../ChatHeader/types';
7
+ import { ResultsGridView } from './ResultsGridView';
8
+ import type { ResultsViewData } from '../hooks/useProductResultsView';
9
+
10
+ const overlayWrapperClasses = classNames(
11
+ 'envive-tw-min-h-0 envive-tw-flex envive-tw-h-full envive-tw-w-full',
12
+ 'envive-tw-items-center envive-tw-justify-center envive-tw-p-4',
13
+ );
14
+ const modalClasses = classNames(
15
+ 'envive-tw-rounded-[16px] envive-tw-flex envive-tw-max-h-[90vh] envive-tw-w-full envive-tw-max-w-[900px]',
16
+ 'envive-tw-flex-col envive-tw-overflow-hidden envive-tw-bg-white envive-tw-shadow-lg',
17
+ );
18
+ const headerWrapperClasses = classNames(
19
+ 'envive-tw-rounded-t-[16px] envive-tw-flex-shrink-0 envive-tw-overflow-hidden',
20
+ );
21
+ const scrollContentClasses = classNames(
22
+ 'envive-tw-flex-1 envive-tw-overflow-y-auto envive-tw-overflow-x-hidden',
23
+ );
24
+ const footerWrapperClasses = classNames('envive-tw-w-full envive-tw-flex-shrink-0');
25
+
26
+ export interface ProductResultsModalProps {
27
+ theme: Theme;
28
+ resultsViewData: ResultsViewData;
29
+ onBackToChat: () => void;
30
+ onProductCardClick?: (product: SalesAgentProductCardProps) => void;
31
+ onClose: () => void;
32
+ logoDark?: string;
33
+ logoLight?: string;
34
+ headerBgColor?: ColorNames;
35
+ headerMode?: ChatHeaderVariant;
36
+ footer: React.ReactNode;
37
+ }
38
+
39
+ /**
40
+ * Desktop-only modal for "Explore All Results" view.
41
+ */
42
+ export const ProductResultsModal = ({
43
+ theme,
44
+ resultsViewData,
45
+ onBackToChat,
46
+ onProductCardClick,
47
+ logoDark,
48
+ logoLight,
49
+ headerBgColor,
50
+ headerMode,
51
+ footer,
52
+ }: ProductResultsModalProps) => {
53
+ const header = (
54
+ <ChatHeader
55
+ logoDark={logoDark}
56
+ logoLight={logoLight}
57
+ selectedOption={ChatHeaderOptions.ASSISTANT}
58
+ onToggle={() => {}}
59
+ onClose={onBackToChat}
60
+ showLogo
61
+ showCXToggle={false}
62
+ centralizeCXToggle
63
+ theme={theme}
64
+ variant={headerMode ?? ChatHeaderVariant.LIGHT}
65
+ headerBgColor={headerBgColor}
66
+ />
67
+ );
68
+
69
+ return (
70
+ <div className={overlayWrapperClasses}>
71
+ <div className={modalClasses}>
72
+ <div className={headerWrapperClasses}>{header}</div>
73
+ <div className={scrollContentClasses}>
74
+ <ResultsGridView
75
+ theme={theme}
76
+ searchQuery={resultsViewData.searchQuery}
77
+ products={resultsViewData.products}
78
+ onBackToChat={onBackToChat}
79
+ onProductCardClick={onProductCardClick}
80
+ gridCols={4}
81
+ />
82
+ </div>
83
+ {footer && <div className={footerWrapperClasses}>{footer}</div>}
84
+ </div>
85
+ </div>
86
+ );
87
+ };
@@ -0,0 +1,117 @@
1
+ import classNames from 'classnames';
2
+ import { Theme } from '../../../../tokens/theme/theme';
3
+ import { SalesAgentProductCard } from '../../SalesAgentProductCard/SalesAgentProductCard';
4
+ import {
5
+ SalesAgentProductCardProps,
6
+ SalesAgentProductCardVariant,
7
+ } from '../../SalesAgentProductCard/types/types';
8
+ import { resolveTheme } from '../../utils/resolveTheme';
9
+ import { Stack } from '../../Stack';
10
+ import { Typography, TypographyColor, TypographyVariant } from '../../Typography';
11
+
12
+ export const NUM_COLUMNS_MOBILE = 2;
13
+ export const NUM_COLUMNS_FPSA = 3;
14
+ export const NUM_COLUMNS_DESKTOP_MODAL = 4;
15
+
16
+ export type ResultsGridColumnCount =
17
+ | typeof NUM_COLUMNS_MOBILE
18
+ | typeof NUM_COLUMNS_FPSA
19
+ | typeof NUM_COLUMNS_DESKTOP_MODAL;
20
+
21
+ const gridClassNameByColumnCount: Record<ResultsGridColumnCount, string> = {
22
+ [NUM_COLUMNS_MOBILE]: 'envive-tw-grid-cols-2 envive-tw-gap-4',
23
+ [NUM_COLUMNS_FPSA]: 'envive-tw-grid-cols-3 envive-tw-gap-2',
24
+ [NUM_COLUMNS_DESKTOP_MODAL]: 'envive-tw-grid-cols-4 envive-tw-gap-2',
25
+ };
26
+
27
+ export interface ResultsGridViewProps {
28
+ theme: Theme;
29
+ searchQuery: string;
30
+ products: SalesAgentProductCardProps[];
31
+ onBackToChat: () => void;
32
+ onProductCardClick?: (product: SalesAgentProductCardProps) => void;
33
+ gridCols?: ResultsGridColumnCount;
34
+ }
35
+
36
+ export const ResultsGridView = ({
37
+ theme,
38
+ searchQuery,
39
+ products,
40
+ onBackToChat,
41
+ onProductCardClick,
42
+ gridCols = NUM_COLUMNS_MOBILE,
43
+ }: ResultsGridViewProps) => {
44
+ const finalTheme = resolveTheme(theme);
45
+
46
+ const forceShowCurrentPriceSpace = products.some(
47
+ product => product.currentPrice && product.previousPrice !== product.currentPrice,
48
+ );
49
+
50
+ const gridClassName = gridClassNameByColumnCount[gridCols];
51
+
52
+ const searchQueryHeadingClassName = classNames('envive-tw-mb-2', {
53
+ 'envive-tw-text-center': gridCols !== NUM_COLUMNS_MOBILE,
54
+ });
55
+
56
+ return (
57
+ <Stack
58
+ direction="column"
59
+ gap="4"
60
+ className="envive-tw-pb-4 envive-tw-pl-4 envive-tw-pr-4"
61
+ >
62
+ <div className="envive-tw-top-0 envive-tw-sticky envive-tw-z-10 envive-tw-mb-2 envive-tw-bg-background-light envive-tw-pb-2 envive-tw-pt-2">
63
+ <button
64
+ type="button"
65
+ onClick={onBackToChat}
66
+ className="envive-tw-flex envive-tw-items-center envive-tw-gap-1 envive-tw-rounded-global-custom envive-tw-border envive-tw-border-border-medium envive-tw-bg-background-light envive-tw-p-2 envive-tw-shadow-sm hover:envive-tw-border-border-dark"
67
+ aria-label="Back to Chat"
68
+ >
69
+ <span
70
+ className="envive-tw-text-[--envive-colors-text-primary]"
71
+ aria-hidden
72
+ >
73
+
74
+ </span>
75
+ <Typography
76
+ variant={TypographyVariant.B3_MD}
77
+ color={TypographyColor.TEXT_PRIMARY}
78
+ >
79
+ Back to Chat
80
+ </Typography>
81
+ </button>
82
+ </div>
83
+
84
+ {searchQuery && (
85
+ <Typography
86
+ variant={TypographyVariant.B3_MD}
87
+ color={TypographyColor.TEXT_PRIMARY}
88
+ className={searchQueryHeadingClassName}
89
+ >
90
+ Results for &quot;{searchQuery}&quot;
91
+ </Typography>
92
+ )}
93
+
94
+ {/* Product grid */}
95
+ <div className={classNames('envive-tw-grid', gridClassName)}>
96
+ {products.map((product, index) => (
97
+ <SalesAgentProductCard
98
+ key={product.id ?? `product-${index}`}
99
+ variant={SalesAgentProductCardVariant.LARGE}
100
+ productName={product.productName}
101
+ currentPrice={product.currentPrice}
102
+ previousPrice={product.previousPrice}
103
+ pricePrefix={product.pricePrefix ?? '$'}
104
+ forceShowCurrentPriceSpace={forceShowCurrentPriceSpace}
105
+ rate={product.rate}
106
+ numberOfReviews={product.numberOfReviews}
107
+ image={product.image}
108
+ url={product.url}
109
+ target="_blank"
110
+ theme={finalTheme}
111
+ onClick={() => onProductCardClick?.(product)}
112
+ />
113
+ ))}
114
+ </div>
115
+ </Stack>
116
+ );
117
+ };
@@ -0,0 +1,80 @@
1
+ import { AnimatePresence, motion } from 'framer-motion';
2
+ import { Theme } from '../../../../tokens/theme/theme';
3
+ import { SalesAgentProductCardProps } from '../../SalesAgentProductCard/types/types';
4
+ import {
5
+ NUM_COLUMNS_MOBILE,
6
+ type ResultsGridColumnCount,
7
+ ResultsGridView,
8
+ } from './ResultsGridView';
9
+ import type { ResultsViewData } from '../hooks/useProductResultsView';
10
+
11
+ type SlideChatContentProps = {
12
+ isResultsView: boolean;
13
+ isResultsViewRef: React.MutableRefObject<boolean>;
14
+ resultsViewData: ResultsViewData | null;
15
+ onBackToChat: () => void;
16
+ onProductCardClick: (product: SalesAgentProductCardProps) => void;
17
+ theme: Theme;
18
+ chatMessages: React.ReactNode;
19
+ scrollToBottom: () => void;
20
+ /** Column count for ResultsGridView; defaults to mobile (2). */
21
+ resultsGridColumns?: ResultsGridColumnCount;
22
+ };
23
+
24
+ const slideVariants = {
25
+ // Chat → Grid: chat exits left (-100%), grid enters from right (100%)
26
+ // Grid → Chat: grid exits right (100%), chat enters from left (-100%)
27
+ enter: (isResults: boolean) => ({ x: isResults ? '100%' : '-100%' }),
28
+ center: { x: 0 },
29
+ exit: (isResults: boolean) => ({ x: isResults ? '100%' : '-100%' }),
30
+ };
31
+
32
+ export const SlideChatContent = ({
33
+ isResultsView,
34
+ isResultsViewRef,
35
+ resultsViewData,
36
+ onBackToChat,
37
+ onProductCardClick,
38
+ theme,
39
+ chatMessages,
40
+ scrollToBottom,
41
+ resultsGridColumns = NUM_COLUMNS_MOBILE,
42
+ }: SlideChatContentProps) => {
43
+ const resultsViewContent = resultsViewData && (
44
+ <ResultsGridView
45
+ theme={theme}
46
+ searchQuery={resultsViewData.searchQuery}
47
+ products={resultsViewData.products}
48
+ onBackToChat={onBackToChat}
49
+ onProductCardClick={onProductCardClick}
50
+ gridCols={resultsGridColumns}
51
+ />
52
+ );
53
+
54
+ return (
55
+ <div className="envive-tw-relative envive-tw-min-h-[200px] envive-tw-w-full envive-tw-overflow-hidden">
56
+ <AnimatePresence
57
+ initial={false}
58
+ mode="wait"
59
+ >
60
+ <motion.div
61
+ key={isResultsView ? 'results' : 'chat'}
62
+ custom={isResultsView}
63
+ variants={slideVariants}
64
+ initial="enter"
65
+ animate="center"
66
+ exit="exit"
67
+ transition={{ duration: 0.3, ease: 'easeInOut' }}
68
+ onAnimationComplete={() => {
69
+ if (!isResultsViewRef.current) {
70
+ scrollToBottom();
71
+ }
72
+ }}
73
+ className="envive-tw-w-full"
74
+ >
75
+ {isResultsView ? resultsViewContent : chatMessages}
76
+ </motion.div>
77
+ </AnimatePresence>
78
+ </div>
79
+ );
80
+ };
@@ -1,4 +1,7 @@
1
1
  import { Layout } from './Layout';
2
+ import { ResultsGridView } from './ResultsGridView';
3
+ import { ProductResultsModal } from './ProductResultsModal';
4
+ import { SlideChatContent } from './SlideChatContent';
2
5
  import { UserMessage } from './UserMessage';
3
6
  import { AgentMessage } from './AgentMessage';
4
7
  import { ChatMessages } from './ChatMessages';
@@ -11,6 +14,9 @@ import { MessageDivider } from './MessageDivider';
11
14
 
12
15
  export const FloatingChatComponents = {
13
16
  Layout,
17
+ ResultsGridView,
18
+ ProductResultsModal,
19
+ SlideChatContent,
14
20
  UserMessage,
15
21
  AgentMessage,
16
22
  ChatMessages,
@@ -0,0 +1,73 @@
1
+ import { Message, MessageType } from '@envive-ai/react-hooks/application/models';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import {
4
+ getCleanProducts,
5
+ getCleanProductsForResultsGrid,
6
+ getSearchQueryFromMessageBlock,
7
+ } from '../utils/functions';
8
+
9
+ export type ResultsViewData = {
10
+ products: ReturnType<typeof getCleanProducts>;
11
+ searchQuery: string;
12
+ };
13
+
14
+ type UseResultsViewProps = {
15
+ scrollToBottom: () => void;
16
+ };
17
+
18
+ export const useProductResultsView = ({ scrollToBottom }: UseResultsViewProps) => {
19
+ const [resultsViewData, setResultsViewData] = useState<ResultsViewData | null>(null);
20
+ const isResultsView = resultsViewData !== null;
21
+
22
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
23
+ const isResultsViewRef = useRef(isResultsView);
24
+ isResultsViewRef.current = isResultsView;
25
+
26
+ const handleBackToChat = () => {
27
+ setResultsViewData(null);
28
+ };
29
+
30
+ const handleExploreAllResults = useCallback(
31
+ (firstProductMessageId: string, messages: Message[][]) => {
32
+ const blockIndex = messages.findIndex(block =>
33
+ block.some(msg => msg.type === MessageType.Product && msg.id === firstProductMessageId),
34
+ );
35
+ if (blockIndex < 0) return;
36
+
37
+ const messageBlock = messages[blockIndex];
38
+ const productMessages = messageBlock.filter(msg => msg.type === MessageType.Product);
39
+ const products = getCleanProductsForResultsGrid(productMessages);
40
+ if (products.length === 0) return;
41
+
42
+ const searchQuery = getSearchQueryFromMessageBlock(messages, blockIndex);
43
+ setResultsViewData({ products, searchQuery });
44
+ },
45
+ [],
46
+ );
47
+
48
+ // Scroll to top when navigating to results view
49
+ useEffect(() => {
50
+ if (isResultsView && scrollContainerRef.current) {
51
+ scrollContainerRef.current.scrollTo({ top: 0, behavior: 'smooth' });
52
+ }
53
+ }, [isResultsView]);
54
+
55
+ // Scroll to bottom when navigating back to chat
56
+ const prevIsResultsViewRef = useRef(isResultsView);
57
+ useEffect(() => {
58
+ if (prevIsResultsViewRef.current && !isResultsView) {
59
+ scrollToBottom();
60
+ }
61
+ prevIsResultsViewRef.current = isResultsView;
62
+ }, [isResultsView, scrollToBottom]);
63
+
64
+ return {
65
+ resultsViewData,
66
+ setResultsViewData,
67
+ isResultsView,
68
+ scrollContainerRef,
69
+ isResultsViewRef,
70
+ handleBackToChat,
71
+ handleExploreAllResults,
72
+ };
73
+ };
@@ -69,4 +69,8 @@ export interface FloatingChatProps {
69
69
  * Debug bar to display in the chat.
70
70
  */
71
71
  debugBar?: React.ReactNode;
72
+ /**
73
+ * Updates whether the product results modal is open (desktop full-width grid view).
74
+ */
75
+ setIsResultsModalOpen?: (isOpen: boolean) => void;
72
76
  }
@@ -34,27 +34,42 @@ export const checkIfHideAgentName = (messages: Message[]) => {
34
34
  );
35
35
  };
36
36
 
37
+ const mapProductMessageToCardProps = (msg: Message): SalesAgentProductCardProps | null => {
38
+ if (msg.type !== MessageType.Product) return null;
39
+ return {
40
+ id: msg.id,
41
+ productName: msg.metadata.title,
42
+ currentPrice: msg.metadata.salePrice,
43
+ previousPrice: msg.metadata.originalPrice,
44
+ pricePrefix: '$',
45
+ rate: msg.metadata.averageRating,
46
+ numberOfReviews: msg.metadata.numberReviews,
47
+ url: msg.metadata.url,
48
+ image: {
49
+ src: msg.metadata.imageUrl,
50
+ alt: `${msg.metadata.title} image`,
51
+ },
52
+ };
53
+ };
54
+
55
+ /** Product cards for carousels; excludes grid-only rows from the API (`isForGrid`). */
37
56
  export const getCleanProducts = (messages: Message[]) => {
38
- const products: SalesAgentProductCardProps[] = messages.map(msg => {
39
- if (msg.type === MessageType.Product && !msg.metadata.isForGrid) {
40
- return {
41
- productName: msg.metadata.title,
42
- currentPrice: msg.metadata.salePrice,
43
- previousPrice: msg.metadata.originalPrice,
44
- pricePrefix: '$',
45
- rate: msg.metadata.averageRating,
46
- numberOfReviews: msg.metadata.numberReviews,
47
- url: msg.metadata.url,
48
- image: {
49
- src: msg.metadata.imageUrl,
50
- alt: `${msg.metadata.title} image`,
51
- },
52
- };
53
- }
54
- return null;
55
- });
57
+ const cleanProducts = messages
58
+ .map(msg =>
59
+ msg.type === MessageType.Product && !msg.metadata.isForGrid
60
+ ? mapProductMessageToCardProps(msg)
61
+ : null,
62
+ )
63
+ .filter((p): p is SalesAgentProductCardProps => p !== null);
64
+ return cleanProducts;
65
+ };
56
66
 
57
- return products.filter(product => product !== null);
67
+ /** All product rows in a turn, including `isForGrid`, for the full results grid. */
68
+ export const getCleanProductsForResultsGrid = (messages: Message[]) => {
69
+ const productsForGrid = messages
70
+ .map(msg => (msg.type === MessageType.Product ? mapProductMessageToCardProps(msg) : null))
71
+ .filter((p): p is SalesAgentProductCardProps => p !== null);
72
+ return productsForGrid;
58
73
  };
59
74
 
60
75
  export const getCleanReviews = (messages: Message[]) => {
@@ -121,6 +136,28 @@ export const getCleanOrders = (messages: Message[]) => {
121
136
 
122
137
  export const MOST_RELEVANT_DISPLAY_LIMIT = 8;
123
138
 
139
+ /**
140
+ * Extracts the search query from a message block (or previous blocks) that led to product results.
141
+ * Looks for QueryTyped (metadata.content) or SuggestionClicked (metadata.suggestionContent).
142
+ */
143
+ export const getSearchQueryFromMessageBlock = (
144
+ messages: Message[][],
145
+ productBlockIndex: number,
146
+ ): string => {
147
+ for (let i = productBlockIndex; i >= 0; i -= 1) {
148
+ const block = messages[i];
149
+ const queryTyped = block.find(msg => msg.type === MessageType.QueryTyped);
150
+ if (queryTyped && 'content' in queryTyped.metadata) {
151
+ return (queryTyped.metadata as { content?: string }).content ?? '';
152
+ }
153
+ const suggestionClicked = block.find(msg => msg.type === MessageType.SuggestionClicked);
154
+ if (suggestionClicked && 'suggestionContent' in suggestionClicked.metadata) {
155
+ return (suggestionClicked.metadata as { suggestionContent?: string }).suggestionContent ?? '';
156
+ }
157
+ }
158
+ return '';
159
+ };
160
+
124
161
  export const getProductCarouselDisplayInfo = <T>(
125
162
  products: T[],
126
163
  numberOfProducts?: number,
@@ -0,0 +1,26 @@
1
+ import { EnviveMetricsEventName } from '@envive-ai/react-hooks/contexts/amplitudeContext';
2
+ import {
3
+ type WidgetInteraction,
4
+ WidgetInteractionComponent,
5
+ WidgetInteractionType,
6
+ } from '@envive-ai/react-hooks/hooks/WidgetInteraction/types';
7
+ import type { SalesAgentProductCardProps } from '../../SalesAgentProductCard/types/types';
8
+
9
+ export const trackProductCardClicked = (
10
+ trackWidgetInteraction: (props: WidgetInteraction) => void,
11
+ widget: WidgetInteractionComponent,
12
+ product: SalesAgentProductCardProps,
13
+ ) => {
14
+ trackWidgetInteraction({
15
+ eventName: EnviveMetricsEventName.WidgetInteraction,
16
+ trigger: {
17
+ widget,
18
+ widget_interaction: WidgetInteractionType.PRODUCT_CARD_CLICKED,
19
+ widget_interaction_data: {
20
+ product_card_clicked: {
21
+ product_id: product.id,
22
+ },
23
+ },
24
+ },
25
+ });
26
+ };
@@ -13,7 +13,10 @@ import { ChatFooter } from '../ChatFooter';
13
13
  import { Disclaimer } from '../Disclaimer';
14
14
  import { FloatingChatComponents } from '../FloatingChat/components';
15
15
  import { useChatSuggestions } from '../FloatingChat/hooks/useChatSuggestions';
16
+ import { useProductResultsView } from '../FloatingChat/hooks/useProductResultsView';
16
17
  import { useScrollToBottom } from '../FloatingChat/hooks/useScrollToBottom';
18
+ import { trackProductCardClicked } from '../FloatingChat/utils/trackProductCardInteraction';
19
+ import { SalesAgentProductCardProps } from '../SalesAgentProductCard/types/types';
17
20
  import { PromptButtonVariant } from '../PromptButton';
18
21
  import { PromptCarousel, PromptCarouselRows, usePromptCarouselAnalytics } from '../PromptCarousel';
19
22
  import { Theme } from '../Tokens';
@@ -24,6 +27,11 @@ import { useGetFooterStyles } from './hooks/useGetFooterStyles';
24
27
  import { useGetMessagesStyles } from './hooks/useGetMessagesStyles';
25
28
  import { useGetScrollContentStyles } from './hooks/useGetScrollContentStyles';
26
29
  import { useIsMobile } from './hooks/useIsMobile';
30
+ import {
31
+ NUM_COLUMNS_FPSA,
32
+ NUM_COLUMNS_MOBILE,
33
+ ResultsGridColumnCount,
34
+ } from '../FloatingChat/components/ResultsGridView';
27
35
 
28
36
  interface FullPageSalesAgentProps {
29
37
  theme?: Theme;
@@ -107,6 +115,15 @@ export const FullPageSalesAgent = ({
107
115
  ],
108
116
  });
109
117
 
118
+ const {
119
+ resultsViewData,
120
+ isResultsView,
121
+ scrollContainerRef,
122
+ isResultsViewRef,
123
+ handleBackToChat,
124
+ handleExploreAllResults,
125
+ } = useProductResultsView({ scrollToBottom });
126
+
110
127
  const handleScrollToBottom = () => {
111
128
  scrollToBottom();
112
129
  trackWidgetInteraction({
@@ -128,6 +145,14 @@ export const FullPageSalesAgent = ({
128
145
  });
129
146
  };
130
147
 
148
+ const handleProductCardClick = (product: SalesAgentProductCardProps) => {
149
+ trackProductCardClicked(
150
+ trackWidgetInteraction,
151
+ WidgetInteractionComponent.FULL_PAGE_SALES_AGENT,
152
+ product,
153
+ );
154
+ };
155
+
131
156
  const welcomeMessage = (
132
157
  <div className={messageContainerClasses}>
133
158
  <WelcomeMessage
@@ -165,6 +190,7 @@ export const FullPageSalesAgent = ({
165
190
 
166
191
  setAnswerSuggestions([]);
167
192
  setGeneralSuggestions([]);
193
+ handleBackToChat();
168
194
  }}
169
195
  onChange={setQuery}
170
196
  onSubmit={() => {
@@ -175,6 +201,7 @@ export const FullPageSalesAgent = ({
175
201
  });
176
202
  setAnswerSuggestions([]);
177
203
  setGeneralSuggestions([]);
204
+ handleBackToChat();
178
205
  }}
179
206
  onFocus={handleInputQueryFocus}
180
207
  parentWidget={WidgetInteractionComponent.FULL_PAGE_SALES_AGENT}
@@ -197,6 +224,25 @@ export const FullPageSalesAgent = ({
197
224
  showVerifiedBuyer={showVerifiedBuyer}
198
225
  onFormResponseSubmitted={onFormResponseSubmitted}
199
226
  parentWidget={WidgetInteractionComponent.FULL_PAGE_SALES_AGENT}
227
+ onExploreAllResults={firstProductMessageId =>
228
+ handleExploreAllResults(firstProductMessageId, messages)
229
+ }
230
+ />
231
+ );
232
+
233
+ const middleContent = (
234
+ <FloatingChatComponents.SlideChatContent
235
+ isResultsView={isResultsView}
236
+ isResultsViewRef={isResultsViewRef}
237
+ resultsViewData={resultsViewData}
238
+ onBackToChat={handleBackToChat}
239
+ onProductCardClick={handleProductCardClick}
240
+ theme={resolvedTheme}
241
+ chatMessages={chatMessages}
242
+ scrollToBottom={scrollToBottom}
243
+ resultsGridColumns={
244
+ (isMobile ? NUM_COLUMNS_MOBILE : NUM_COLUMNS_FPSA) as ResultsGridColumnCount
245
+ }
200
246
  />
201
247
  );
202
248
 
@@ -232,18 +278,21 @@ export const FullPageSalesAgent = ({
232
278
  return (
233
279
  <FullPageSAComponents.Layout
234
280
  theme={resolvedTheme}
235
- welcomeMessage={welcomeMessage}
281
+ welcomeMessage={isResultsView ? null : welcomeMessage}
236
282
  footer={footer}
237
- chatMessages={chatMessages}
238
- answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent : undefined}
283
+ chatMessages={middleContent}
284
+ answerSuggestions={
285
+ showAnswerSuggestions && !isResultsView ? answerSuggestionsComponent : undefined
286
+ }
239
287
  scrollToBottomButton={
240
- showScrollButton ? (
288
+ !isResultsView && showScrollButton ? (
241
289
  <FloatingChatComponents.ScrollToBottomButton onClick={handleScrollToBottom} />
242
290
  ) : undefined
243
291
  }
244
- disclaimer={disclaimer}
292
+ disclaimer={isResultsView ? null : disclaimer}
245
293
  headerContainer={headerContainer}
246
294
  autoHeight={autoHeight}
295
+ scrollContainerRef={scrollContainerRef}
247
296
  />
248
297
  );
249
298
  };
@@ -17,6 +17,8 @@ export interface LayoutProps {
17
17
  disclaimer?: React.ReactNode;
18
18
  headerContainer?: string;
19
19
  autoHeight?: boolean;
20
+ /** Scrollable content ref (e.g. scroll to top when opening product results grid) */
21
+ scrollContainerRef?: React.RefObject<HTMLDivElement | null>;
20
22
  }
21
23
 
22
24
  export const Layout = ({
@@ -29,6 +31,7 @@ export const Layout = ({
29
31
  disclaimer,
30
32
  headerContainer,
31
33
  autoHeight,
34
+ scrollContainerRef,
32
35
  }: LayoutProps) => {
33
36
  const hasWelcomeMessage = isValidElement(welcomeMessage);
34
37
  const hasAnswerSuggestions = isValidElement(answerSuggestions);
@@ -45,7 +48,10 @@ export const Layout = ({
45
48
  className={containerClasses}
46
49
  style={containerStyles}
47
50
  >
48
- <div className={contentClasses}>
51
+ <div
52
+ ref={scrollContainerRef}
53
+ className={contentClasses}
54
+ >
49
55
  <Stack
50
56
  direction="column"
51
57
  justify="between"