@envive-ai/react-toolkit-v3 0.3.17 → 0.3.19
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.
- package/dist/AnimatedText/AnimatedText.d.cts +3 -3
- package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.cts +2 -2
- package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.ts +2 -2
- package/dist/Carousel/Carousel.cjs +7 -6
- package/dist/Carousel/Carousel.d.cts +3 -2
- package/dist/Carousel/Carousel.d.ts +3 -2
- package/dist/Carousel/Carousel.js +7 -6
- package/dist/Carousel/components/Container.cjs +2 -2
- package/dist/Carousel/components/Container.js +2 -2
- package/dist/Carousel/types/types.d.cts +5 -0
- package/dist/Carousel/types/types.d.ts +5 -0
- package/dist/ChatFooter/ChatFooter.cjs +1 -1
- package/dist/ChatFooter/ChatFooter.d.cts +2 -2
- package/dist/ChatFooter/ChatFooter.d.ts +2 -2
- package/dist/ChatFooter/ChatFooter.js +1 -1
- package/dist/ChatFooter/components/Layout.cjs +2 -2
- package/dist/ChatFooter/components/Layout.js +2 -2
- package/dist/ChatFooter/components/index.d.cts +5 -5
- package/dist/ChatFooter/components/index.d.ts +5 -5
- package/dist/ChatHeader/ChatHeader.d.cts +2 -2
- package/dist/ChatHeader/ChatHeader.d.ts +2 -2
- package/dist/ChatHeader/components/Handle.cjs +2 -2
- package/dist/ChatHeader/components/Handle.js +2 -2
- package/dist/ChatHeader/components/Toggle.cjs +3 -3
- package/dist/ChatHeader/components/Toggle.js +3 -3
- package/dist/ChatPreview/ChatPreview.cjs +1 -1
- package/dist/ChatPreview/ChatPreview.d.cts +2 -2
- package/dist/ChatPreview/ChatPreview.d.ts +2 -2
- package/dist/ChatPreview/ChatPreview.js +1 -1
- package/dist/ChatPreviewComparison/ChatPreviewComparison.cjs +1 -1
- package/dist/ChatPreviewComparison/ChatPreviewComparison.d.cts +2 -2
- package/dist/ChatPreviewComparison/ChatPreviewComparison.d.ts +2 -2
- package/dist/ChatPreviewComparison/ChatPreviewComparison.js +1 -1
- package/dist/ChatPreviewComparison/components/Headline.cjs +2 -2
- package/dist/ChatPreviewComparison/components/Headline.js +2 -2
- package/dist/ChatPreviewComparison/components/Layout.cjs +4 -4
- package/dist/ChatPreviewComparison/components/Layout.js +4 -4
- package/dist/ChatPreviewComparison/components/Message.cjs +2 -2
- package/dist/ChatPreviewComparison/components/Message.js +2 -2
- package/dist/ChatPreviewLoading/ChatPreviewLoading.d.cts +2 -2
- package/dist/ChatPreviewLoading/ChatPreviewLoading.d.ts +2 -2
- package/dist/Container/Container.d.cts +14 -14
- package/dist/Container/Container.d.ts +177 -177
- package/dist/DesignTokens/DesignTokensComponent.d.cts +2 -2
- package/dist/DesignTokens/DesignTokensComponent.d.ts +2 -2
- package/dist/Disclaimer/components/Container.cjs +2 -2
- package/dist/Disclaimer/components/Container.js +2 -2
- package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.cts +2 -2
- package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.ts +2 -2
- package/dist/DocumentRetrievalCard/components/Layout.cjs +2 -2
- package/dist/DocumentRetrievalCard/components/Layout.js +2 -2
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.cjs +1 -1
- package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.js +1 -1
- package/dist/FloatingButton/FloatingButton.d.cts +2 -2
- package/dist/FloatingButton/FloatingButton.d.ts +2 -2
- package/dist/FloatingChat/FloatingChat.cjs +61 -16
- package/dist/FloatingChat/FloatingChat.d.cts +2 -2
- package/dist/FloatingChat/FloatingChat.d.ts +2 -2
- package/dist/FloatingChat/FloatingChat.js +63 -18
- package/dist/FloatingChat/components/AgentMessage.cjs +8 -3
- package/dist/FloatingChat/components/AgentMessage.js +8 -3
- package/dist/FloatingChat/components/ChatMessages.cjs +5 -4
- package/dist/FloatingChat/components/ChatMessages.js +5 -4
- package/dist/FloatingChat/components/Layout.cjs +5 -4
- package/dist/FloatingChat/components/Layout.js +5 -4
- package/dist/FloatingChat/components/ResultsGridView.cjs +72 -0
- package/dist/FloatingChat/components/ResultsGridView.js +71 -0
- package/dist/FloatingChat/components/SalesAgentBadgeContent.cjs +59 -0
- package/dist/FloatingChat/components/SalesAgentBadgeContent.js +56 -0
- package/dist/FloatingChat/components/SalesAgentProductCardsCarousel.cjs +14 -5
- package/dist/FloatingChat/components/SalesAgentProductCardsCarousel.js +14 -5
- package/dist/FloatingChat/components/SlideChatContent.cjs +46 -0
- package/dist/FloatingChat/components/SlideChatContent.js +45 -0
- package/dist/FloatingChat/components/index.cjs +4 -0
- package/dist/FloatingChat/components/index.js +4 -0
- package/dist/FloatingChat/hooks/useProductResultsView.cjs +36 -0
- package/dist/FloatingChat/hooks/useProductResultsView.js +35 -0
- package/dist/FloatingChat/utils/functions.cjs +28 -1
- package/dist/FloatingChat/utils/functions.js +25 -1
- package/dist/FullPageSalesAgent/FullPageSalesAgent.cjs +12 -7
- package/dist/FullPageSalesAgent/FullPageSalesAgent.d.cts +2 -2
- package/dist/FullPageSalesAgent/FullPageSalesAgent.d.ts +2 -2
- package/dist/FullPageSalesAgent/FullPageSalesAgent.js +12 -7
- package/dist/FullPageSalesAgent/components/Layout.cjs +1 -2
- package/dist/FullPageSalesAgent/components/Layout.js +1 -2
- package/dist/FullPageSalesAgent/hooks/useContainerResizerObserver.cjs +4 -1
- package/dist/FullPageSalesAgent/hooks/useContainerResizerObserver.js +4 -1
- package/dist/Image/Image.d.cts +2 -2
- package/dist/Image/Image.d.ts +2 -2
- package/dist/ImageGallery/ImageGallery.d.cts +2 -2
- package/dist/ImageGallery/ImageGallery.d.ts +2 -2
- package/dist/ImageGallery/components/Layout.cjs +1 -1
- package/dist/ImageGallery/components/Layout.js +1 -1
- package/dist/MarkdownProcessor/MarkdownProcessor.d.cts +2 -2
- package/dist/MarkdownProcessor/MarkdownProcessor.d.ts +2 -2
- package/dist/Message/components/LinkButton.cjs +1 -1
- package/dist/Message/components/LinkButton.js +1 -1
- package/dist/OrderLookupCard/OrderLookupCard.cjs +1 -1
- package/dist/OrderLookupCard/OrderLookupCard.js +1 -1
- package/dist/ProductCard/ProductCard.cjs +2 -2
- package/dist/ProductCard/ProductCard.d.cts +2 -2
- package/dist/ProductCard/ProductCard.d.ts +2 -2
- package/dist/ProductCard/ProductCard.js +2 -2
- package/dist/PromptButton/PromptButton.d.cts +2 -2
- package/dist/PromptButton/PromptButton.d.ts +2 -2
- package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.cts +2 -2
- package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.ts +2 -2
- package/dist/PromptButtonCarouselWithImage/components/PromptButtonsCarousel.cjs +1 -1
- package/dist/PromptButtonCarouselWithImage/components/PromptButtonsCarousel.js +1 -1
- package/dist/PromptCarousel/PromptCarousel.cjs +3 -3
- package/dist/PromptCarousel/PromptCarousel.d.cts +2 -2
- package/dist/PromptCarousel/PromptCarousel.d.ts +2 -2
- package/dist/PromptCarousel/PromptCarousel.js +3 -3
- package/dist/ReviewCard/ReviewCard.d.cts +2 -2
- package/dist/ReviewCard/ReviewCard.d.ts +2 -2
- package/dist/ReviewCard/components/Container.cjs +2 -2
- package/dist/ReviewCard/components/Container.js +2 -2
- package/dist/ReviewCard/components/ReadMoreButton.cjs +1 -1
- package/dist/ReviewCard/components/ReadMoreButton.js +1 -1
- package/dist/ReviewCard/components/index.d.cts +6 -6
- package/dist/ReviewCard/components/index.d.ts +6 -6
- package/dist/SalesAgentProductCard/SalesAgentProductCard.d.cts +2 -2
- package/dist/SalesAgentProductCard/SalesAgentProductCard.d.ts +2 -2
- package/dist/SalesAgentProductCard/components/Container.cjs +2 -2
- package/dist/SalesAgentProductCard/components/Container.js +2 -2
- package/dist/SalesAgentProductCard/components/index.d.cts +8 -8
- package/dist/SalesAgentProductCard/components/index.d.ts +8 -8
- package/dist/SocialProof/SocialProof.cjs +1 -1
- package/dist/SocialProof/SocialProof.d.ts +2 -2
- package/dist/SocialProof/SocialProof.js +1 -1
- package/dist/SocialProof/components/Headline.cjs +3 -3
- package/dist/SocialProof/components/Headline.js +3 -3
- package/dist/SocialProof/components/LayoutFourHorizontal.cjs +1 -1
- package/dist/SocialProof/components/LayoutFourHorizontal.js +1 -1
- package/dist/SocialProof/components/Subheadline.cjs +1 -1
- package/dist/SocialProof/components/Subheadline.js +1 -1
- package/dist/SparkleAnimation/SparkleAnimation.d.cts +2 -2
- package/dist/SparkleAnimation/SparkleAnimation.d.ts +2 -2
- package/dist/Stack/Stack.d.cts +2 -2
- package/dist/Stack/Stack.d.ts +2 -2
- package/dist/TitledPromptCarousel/TitledPromptCarousel.cjs +1 -1
- package/dist/TitledPromptCarousel/TitledPromptCarousel.d.cts +2 -2
- package/dist/TitledPromptCarousel/TitledPromptCarousel.d.ts +2 -2
- package/dist/TitledPromptCarousel/TitledPromptCarousel.js +1 -1
- package/dist/Tokens/index.cjs +1 -1
- package/dist/Tokens/index.js +1 -1
- package/dist/TypingAnimation/TypingAnimation.cjs +1 -1
- package/dist/TypingAnimation/TypingAnimation.d.cts +2 -2
- package/dist/TypingAnimation/TypingAnimation.d.ts +2 -2
- package/dist/TypingAnimation/TypingAnimation.js +1 -1
- package/dist/TypingAnimation/hooks/useGetTypographyVariant.cjs +1 -1
- package/dist/TypingAnimation/hooks/useGetTypographyVariant.js +1 -1
- package/dist/Typography/Typography.d.cts +4 -4
- package/dist/Typography/Typography.d.ts +4 -4
- package/dist/WelcomeMessage/components/Container.cjs +2 -2
- package/dist/WelcomeMessage/components/Container.js +2 -2
- package/dist/WidgetTextField/WidgetTextField.cjs +1 -1
- package/dist/WidgetTextField/WidgetTextField.d.cts +2 -2
- package/dist/WidgetTextField/WidgetTextField.js +1 -1
- package/dist/WidgetTextField/components/Container.cjs +2 -2
- package/dist/WidgetTextField/components/Container.js +2 -2
- package/dist/WidgetTextField/components/Icon.cjs +1 -1
- package/dist/WidgetTextField/components/Icon.js +1 -1
- package/dist/WidgetWrapper/WidgetWrapper.d.cts +2 -2
- package/dist/WidgetWrapper/WidgetWrapper.d.ts +2 -2
- package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.cts +2 -2
- package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.ts +2 -2
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/Carousel/Carousel.tsx +11 -8
- package/src/components/Carousel/types/types.ts +5 -0
- package/src/components/FloatingChat/FloatingChat.tsx +91 -19
- package/src/components/FloatingChat/components/AgentMessage.tsx +8 -0
- package/src/components/FloatingChat/components/ChatMessages.tsx +3 -0
- package/src/components/FloatingChat/components/Layout.tsx +7 -1
- package/src/components/FloatingChat/components/ModalSheet.tsx +1 -1
- package/src/components/FloatingChat/components/ResultsGridView.tsx +93 -0
- package/src/components/FloatingChat/components/SalesAgentBadgeContent.tsx +86 -0
- package/src/components/FloatingChat/components/SalesAgentProductCardsCarousel.tsx +18 -7
- package/src/components/FloatingChat/components/SlideChatContent.tsx +72 -0
- package/src/components/FloatingChat/components/index.ts +4 -1
- package/src/components/FloatingChat/hooks/useFilteredChatMessages.ts +1 -1
- package/src/components/FloatingChat/hooks/useProductResultsView.ts +49 -0
- package/src/components/FloatingChat/utils/functions.ts +41 -0
- package/src/components/FullPageSalesAgent/FullPageSalesAgent.tsx +10 -6
- package/src/components/FullPageSalesAgent/components/Layout.tsx +1 -2
- package/src/components/FullPageSalesAgent/hooks/useContainerResizerObserver.ts +1 -0
- package/src/logging/logger.ts +33 -8
- package/src/mocks/productCardMocks.ts +955 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ChatElementDisplayLocationV3,
|
|
5
|
+
MessageType,
|
|
6
|
+
} from '@envive-ai/react-hooks/application/models';
|
|
4
7
|
import { motion } from 'framer-motion';
|
|
5
8
|
import {
|
|
6
9
|
WidgetInteractionComponent,
|
|
@@ -18,8 +21,8 @@ import { PromptButtonVariant } from '../PromptButton';
|
|
|
18
21
|
import { PromptCarousel, PromptCarouselRows, usePromptCarouselAnalytics } from '../PromptCarousel';
|
|
19
22
|
import { WelcomeMessage } from '../WelcomeMessage/WelcomeMessage';
|
|
20
23
|
import { SparkleIconColor } from '../WelcomeMessage/types/types';
|
|
21
|
-
import { FloatingChatComponents, ModalSheet } from './components';
|
|
22
24
|
import { useChatSuggestions } from './hooks/useChatSuggestions';
|
|
25
|
+
import { useProductResultsView } from './hooks/useProductResultsView';
|
|
23
26
|
import { useScrollToBottom } from './hooks/useScrollToBottom';
|
|
24
27
|
import { useSnapSetup } from './hooks/useSnapSetup';
|
|
25
28
|
|
|
@@ -28,6 +31,9 @@ import { resolveTheme } from '../utils/resolveTheme';
|
|
|
28
31
|
import { useFilteredChatMessages } from './hooks/useFilteredChatMessages';
|
|
29
32
|
import { Unit } from './hooks/useSnapCalculator';
|
|
30
33
|
import { FloatingChatProps } from './types/types';
|
|
34
|
+
import { getCleanProducts, getSearchQueryFromMessageBlock } from './utils/functions';
|
|
35
|
+
import { FloatingChatComponents, ModalSheet } from './components';
|
|
36
|
+
import { SalesAgentProductCardProps } from '../SalesAgentProductCard/types/types';
|
|
31
37
|
|
|
32
38
|
export const FloatingChat = ({
|
|
33
39
|
id,
|
|
@@ -76,6 +82,19 @@ export const FloatingChat = ({
|
|
|
76
82
|
rightToggleLabel,
|
|
77
83
|
} = hardcopyContent?.values ?? {};
|
|
78
84
|
|
|
85
|
+
const disclaimerTextString = useMemo(() => {
|
|
86
|
+
if (disclaimerText && typeof disclaimerText === 'string') {
|
|
87
|
+
return disclaimerText;
|
|
88
|
+
}
|
|
89
|
+
if (disclaimerText && Array.isArray(disclaimerText)) {
|
|
90
|
+
const textString = disclaimerText[0];
|
|
91
|
+
if (textString && typeof textString === 'string') {
|
|
92
|
+
return textString;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return undefined;
|
|
96
|
+
}, [disclaimerText]);
|
|
97
|
+
|
|
79
98
|
const { agentName, chatHeaderLogoDarkSrc, chatHeaderLogoLightSrc } = lookAndFeelConfig;
|
|
80
99
|
|
|
81
100
|
const {
|
|
@@ -137,6 +156,15 @@ export const FloatingChat = ({
|
|
|
137
156
|
isOpen: isFloatingChatOpen,
|
|
138
157
|
});
|
|
139
158
|
|
|
159
|
+
const {
|
|
160
|
+
resultsViewData,
|
|
161
|
+
setResultsViewData,
|
|
162
|
+
isResultsView,
|
|
163
|
+
scrollContainerRef,
|
|
164
|
+
isResultsViewRef,
|
|
165
|
+
handleBackToChat,
|
|
166
|
+
} = useProductResultsView({ scrollToBottom });
|
|
167
|
+
|
|
140
168
|
useEffect(() => {
|
|
141
169
|
if (isFloatingChatOpen) {
|
|
142
170
|
trackWidgetInteraction({
|
|
@@ -178,6 +206,21 @@ export const FloatingChat = ({
|
|
|
178
206
|
modalSheetControl,
|
|
179
207
|
});
|
|
180
208
|
|
|
209
|
+
const handleProductCardClick = (product: SalesAgentProductCardProps) => {
|
|
210
|
+
trackWidgetInteraction({
|
|
211
|
+
eventName: EnviveMetricsEventName.WidgetInteraction,
|
|
212
|
+
trigger: {
|
|
213
|
+
widget: WidgetInteractionComponent.FLOATING_CHAT,
|
|
214
|
+
widget_interaction: WidgetInteractionType.PRODUCT_CARD_CLICKED,
|
|
215
|
+
widget_interaction_data: {
|
|
216
|
+
product_card_clicked: {
|
|
217
|
+
product_id: product.id,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
181
224
|
const header = (
|
|
182
225
|
<ChatHeader
|
|
183
226
|
logoDark={chatHeaderLogoDarkSrc}
|
|
@@ -250,6 +293,7 @@ export const FloatingChat = ({
|
|
|
250
293
|
});
|
|
251
294
|
setAnswerSuggestions([]);
|
|
252
295
|
setGeneralSuggestions([]);
|
|
296
|
+
handleBackToChat();
|
|
253
297
|
}}
|
|
254
298
|
textFieldPlaceholderText={chatFooterTextFieldPlaceholderText as string}
|
|
255
299
|
promptSuggestions={
|
|
@@ -265,6 +309,7 @@ export const FloatingChat = ({
|
|
|
265
309
|
|
|
266
310
|
setAnswerSuggestions([]);
|
|
267
311
|
setGeneralSuggestions([]);
|
|
312
|
+
handleBackToChat();
|
|
268
313
|
}}
|
|
269
314
|
disabled={isPendingResponse || isResponseStreaming}
|
|
270
315
|
disabledInput={!userQueryInputEnabled}
|
|
@@ -286,6 +331,21 @@ export const FloatingChat = ({
|
|
|
286
331
|
/>
|
|
287
332
|
);
|
|
288
333
|
|
|
334
|
+
const handleExploreAllResults = (firstProductMessageId: string) => {
|
|
335
|
+
const blockIndex = messages.findIndex(block =>
|
|
336
|
+
block.some(msg => msg.type === MessageType.Product && msg.id === firstProductMessageId),
|
|
337
|
+
);
|
|
338
|
+
if (blockIndex < 0) return;
|
|
339
|
+
|
|
340
|
+
const messageBlock = messages[blockIndex];
|
|
341
|
+
const productMessages = messageBlock.filter(msg => msg.type === MessageType.Product);
|
|
342
|
+
const products = getCleanProducts(productMessages);
|
|
343
|
+
if (products.length === 0) return;
|
|
344
|
+
|
|
345
|
+
const searchQuery = getSearchQueryFromMessageBlock(messages, blockIndex);
|
|
346
|
+
setResultsViewData({ products, searchQuery });
|
|
347
|
+
};
|
|
348
|
+
|
|
289
349
|
const chatMessages = (
|
|
290
350
|
<FloatingChatComponents.ChatMessages
|
|
291
351
|
theme={finalTheme}
|
|
@@ -301,6 +361,20 @@ export const FloatingChat = ({
|
|
|
301
361
|
showVerifiedBuyer={showVerifiedBuyer}
|
|
302
362
|
onFormResponseSubmitted={onFormResponseSubmitted}
|
|
303
363
|
parentWidget={WidgetInteractionComponent.FLOATING_CHAT}
|
|
364
|
+
onExploreAllResults={handleExploreAllResults}
|
|
365
|
+
/>
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const middleContent = (
|
|
369
|
+
<FloatingChatComponents.SlideChatContent
|
|
370
|
+
isResultsView={isResultsView}
|
|
371
|
+
isResultsViewRef={isResultsViewRef}
|
|
372
|
+
resultsViewData={resultsViewData}
|
|
373
|
+
onBackToChat={handleBackToChat}
|
|
374
|
+
onProductCardClick={handleProductCardClick}
|
|
375
|
+
theme={finalTheme}
|
|
376
|
+
chatMessages={chatMessages}
|
|
377
|
+
scrollToBottom={scrollToBottom}
|
|
304
378
|
/>
|
|
305
379
|
);
|
|
306
380
|
|
|
@@ -337,20 +411,19 @@ export const FloatingChat = ({
|
|
|
337
411
|
theme={finalTheme}
|
|
338
412
|
header={header}
|
|
339
413
|
footer={footer}
|
|
340
|
-
welcomeMessage={welcomeMessage}
|
|
341
|
-
chatMessages={
|
|
342
|
-
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent :
|
|
414
|
+
welcomeMessage={isResultsView ? null : welcomeMessage}
|
|
415
|
+
chatMessages={middleContent}
|
|
416
|
+
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent : null}
|
|
343
417
|
scrollToBottomButton={
|
|
344
|
-
showScrollButton ? (
|
|
418
|
+
!isResultsView && showScrollButton ? (
|
|
345
419
|
<FloatingChatComponents.ScrollToBottomButton onClick={handleScrollToBottom} />
|
|
346
|
-
) :
|
|
420
|
+
) : null
|
|
347
421
|
}
|
|
348
422
|
disclaimer={
|
|
349
|
-
|
|
350
|
-
<Disclaimer disclaimerMarkdown={disclaimerText} />
|
|
351
|
-
) : undefined
|
|
423
|
+
disclaimerTextString ? <Disclaimer disclaimerMarkdown={disclaimerTextString} /> : null
|
|
352
424
|
}
|
|
353
425
|
debugBar={debugBar}
|
|
426
|
+
scrollContainerRef={scrollContainerRef}
|
|
354
427
|
/>
|
|
355
428
|
);
|
|
356
429
|
|
|
@@ -363,20 +436,19 @@ export const FloatingChat = ({
|
|
|
363
436
|
theme={finalTheme}
|
|
364
437
|
header={mobileHeader}
|
|
365
438
|
footer={footer}
|
|
366
|
-
welcomeMessage={welcomeMessage}
|
|
367
|
-
chatMessages={
|
|
368
|
-
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent :
|
|
439
|
+
welcomeMessage={isResultsView ? null : welcomeMessage}
|
|
440
|
+
chatMessages={middleContent}
|
|
441
|
+
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent : null}
|
|
369
442
|
scrollToBottomButton={
|
|
370
|
-
showScrollButton ? (
|
|
443
|
+
!isResultsView && showScrollButton ? (
|
|
371
444
|
<FloatingChatComponents.ScrollToBottomButton onClick={handleScrollToBottom} />
|
|
372
|
-
) :
|
|
445
|
+
) : null
|
|
373
446
|
}
|
|
374
447
|
disclaimer={
|
|
375
|
-
|
|
376
|
-
<Disclaimer disclaimerMarkdown={disclaimerText} />
|
|
377
|
-
) : undefined
|
|
448
|
+
disclaimerTextString ? <Disclaimer disclaimerMarkdown={disclaimerTextString} /> : null
|
|
378
449
|
}
|
|
379
450
|
isFloatingFooterLayout={isFloatingLayout}
|
|
451
|
+
scrollContainerRef={scrollContainerRef}
|
|
380
452
|
/>
|
|
381
453
|
);
|
|
382
454
|
|
|
@@ -42,6 +42,7 @@ export interface AgentMessageProps {
|
|
|
42
42
|
showVerifiedBuyer?: boolean;
|
|
43
43
|
onFormResponseSubmitted?: (formResponse: FormSubmittedAttributes) => void;
|
|
44
44
|
parentWidget: WidgetInteractionComponent;
|
|
45
|
+
onExploreAllResults: (firstProductMessageId: string) => void;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export const AgentMessage = ({
|
|
@@ -56,6 +57,7 @@ export const AgentMessage = ({
|
|
|
56
57
|
showVerifiedBuyer = false,
|
|
57
58
|
onFormResponseSubmitted,
|
|
58
59
|
parentWidget,
|
|
60
|
+
onExploreAllResults,
|
|
59
61
|
}: AgentMessageProps) => {
|
|
60
62
|
const handleFormSubmittedAtomFallback = useSetAtom(handleFormSubmittedAtom);
|
|
61
63
|
const finalTheme = resolveTheme(theme);
|
|
@@ -167,6 +169,11 @@ export const AgentMessage = ({
|
|
|
167
169
|
return null;
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
const handleExploreAllResults = () => {
|
|
173
|
+
const firstProductMessageId = messages[0]?.id ?? '';
|
|
174
|
+
onExploreAllResults?.(firstProductMessageId);
|
|
175
|
+
};
|
|
176
|
+
|
|
170
177
|
return (
|
|
171
178
|
<SalesAgentProductCardsCarousel
|
|
172
179
|
hideNavigation={products.length === 1}
|
|
@@ -174,6 +181,7 @@ export const AgentMessage = ({
|
|
|
174
181
|
numberOfProducts={products.length}
|
|
175
182
|
theme={finalTheme}
|
|
176
183
|
onProductCardClick={handleProductCardClick}
|
|
184
|
+
onExploreAllResults={handleExploreAllResults}
|
|
177
185
|
/>
|
|
178
186
|
);
|
|
179
187
|
}
|
|
@@ -32,6 +32,7 @@ export interface ChatMessagesProps {
|
|
|
32
32
|
showVerifiedBuyer?: boolean;
|
|
33
33
|
onFormResponseSubmitted?: (formResponse: FormSubmittedAttributes) => void;
|
|
34
34
|
parentWidget: WidgetInteractionComponent;
|
|
35
|
+
onExploreAllResults: (firstProductMessageId: string) => void;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export const ChatMessages = forwardRef<HTMLDivElement, ChatMessagesProps>(
|
|
@@ -50,6 +51,7 @@ export const ChatMessages = forwardRef<HTMLDivElement, ChatMessagesProps>(
|
|
|
50
51
|
showVerifiedBuyer,
|
|
51
52
|
onFormResponseSubmitted,
|
|
52
53
|
parentWidget,
|
|
54
|
+
onExploreAllResults,
|
|
53
55
|
},
|
|
54
56
|
ref,
|
|
55
57
|
) => {
|
|
@@ -143,6 +145,7 @@ export const ChatMessages = forwardRef<HTMLDivElement, ChatMessagesProps>(
|
|
|
143
145
|
showVerifiedBuyer={showVerifiedBuyer}
|
|
144
146
|
onFormResponseSubmitted={onFormResponseSubmitted}
|
|
145
147
|
parentWidget={parentWidget}
|
|
148
|
+
onExploreAllResults={onExploreAllResults}
|
|
146
149
|
/>
|
|
147
150
|
);
|
|
148
151
|
}
|
|
@@ -20,6 +20,8 @@ type LayoutProps = {
|
|
|
20
20
|
disclaimer?: React.ReactNode;
|
|
21
21
|
isFloatingFooterLayout?: boolean;
|
|
22
22
|
debugBar?: React.ReactNode;
|
|
23
|
+
/** Ref for the scrollable content container (used for programmatic scroll on view changes) */
|
|
24
|
+
scrollContainerRef?: React.RefObject<HTMLDivElement | null>;
|
|
23
25
|
};
|
|
24
26
|
|
|
25
27
|
export const Layout = ({
|
|
@@ -37,6 +39,7 @@ export const Layout = ({
|
|
|
37
39
|
disclaimer,
|
|
38
40
|
isFloatingFooterLayout = false,
|
|
39
41
|
debugBar,
|
|
42
|
+
scrollContainerRef,
|
|
40
43
|
}: LayoutProps) => {
|
|
41
44
|
const finalTheme = resolveTheme(theme);
|
|
42
45
|
const hasWelcomeMessage = isValidElement(welcomeMessage);
|
|
@@ -58,7 +61,10 @@ export const Layout = ({
|
|
|
58
61
|
>
|
|
59
62
|
{header && <div className="envive-tw-flex-shrink-0 envive-tw-leading-[0]">{header}</div>}
|
|
60
63
|
|
|
61
|
-
<div
|
|
64
|
+
<div
|
|
65
|
+
ref={scrollContainerRef}
|
|
66
|
+
className="envive-tw-flex-1 envive-tw-overflow-y-auto envive-tw-overflow-x-hidden envive-tw-transition-all envive-tw-duration-300 envive-tw-ease-in-out"
|
|
67
|
+
>
|
|
62
68
|
<Stack
|
|
63
69
|
className="envive-tw-h-full"
|
|
64
70
|
direction="column"
|
|
@@ -24,7 +24,7 @@ interface ModalSheetProps {
|
|
|
24
24
|
animation?: ValueAnimationTransition;
|
|
25
25
|
onClose: () => void;
|
|
26
26
|
onSnap?: (snap: number) => void;
|
|
27
|
-
controlRef?: React.
|
|
27
|
+
controlRef?: React.RefObject<ModalSheetControl | undefined>;
|
|
28
28
|
disableDrag?: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Theme } from '../../../../tokens/theme/theme';
|
|
2
|
+
import { SalesAgentProductCard } from '../../SalesAgentProductCard/SalesAgentProductCard';
|
|
3
|
+
import {
|
|
4
|
+
SalesAgentProductCardProps,
|
|
5
|
+
SalesAgentProductCardVariant,
|
|
6
|
+
} from '../../SalesAgentProductCard/types/types';
|
|
7
|
+
import { resolveTheme } from '../../utils/resolveTheme';
|
|
8
|
+
import { Stack } from '../../Stack';
|
|
9
|
+
import { Typography, TypographyColor, TypographyVariant } from '../../Typography';
|
|
10
|
+
|
|
11
|
+
export interface ResultsGridViewProps {
|
|
12
|
+
theme: Theme;
|
|
13
|
+
searchQuery: string;
|
|
14
|
+
products: SalesAgentProductCardProps[];
|
|
15
|
+
onBackToChat: () => void;
|
|
16
|
+
onProductCardClick?: (product: SalesAgentProductCardProps) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const ResultsGridView = ({
|
|
20
|
+
theme,
|
|
21
|
+
searchQuery,
|
|
22
|
+
products,
|
|
23
|
+
onBackToChat,
|
|
24
|
+
onProductCardClick,
|
|
25
|
+
}: ResultsGridViewProps) => {
|
|
26
|
+
const finalTheme = resolveTheme(theme);
|
|
27
|
+
|
|
28
|
+
const forceShowCurrentPriceSpace = products.some(
|
|
29
|
+
product => product.currentPrice && product.previousPrice !== product.currentPrice,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Stack
|
|
34
|
+
direction="column"
|
|
35
|
+
gap="4"
|
|
36
|
+
className="envive-tw-pb-4 envive-tw-pl-4 envive-tw-pr-4"
|
|
37
|
+
>
|
|
38
|
+
<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">
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
onClick={onBackToChat}
|
|
42
|
+
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"
|
|
43
|
+
aria-label="Back to Chat"
|
|
44
|
+
>
|
|
45
|
+
<span
|
|
46
|
+
className="envive-tw-text-[--envive-colors-text-primary]"
|
|
47
|
+
aria-hidden
|
|
48
|
+
>
|
|
49
|
+
←
|
|
50
|
+
</span>
|
|
51
|
+
<Typography
|
|
52
|
+
variant={TypographyVariant.B3_MD}
|
|
53
|
+
color={TypographyColor.TEXT_PRIMARY}
|
|
54
|
+
>
|
|
55
|
+
Back to Chat
|
|
56
|
+
</Typography>
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{searchQuery && (
|
|
61
|
+
<Typography
|
|
62
|
+
variant={TypographyVariant.B3_MD}
|
|
63
|
+
color={TypographyColor.TEXT_PRIMARY}
|
|
64
|
+
className="envive-tw-mb-2"
|
|
65
|
+
>
|
|
66
|
+
Results for "{searchQuery}"
|
|
67
|
+
</Typography>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
{/* Product grid */}
|
|
71
|
+
<div className="envive-tw-grid envive-tw-grid-cols-2 envive-tw-gap-4">
|
|
72
|
+
{products.map((product, index) => (
|
|
73
|
+
<SalesAgentProductCard
|
|
74
|
+
key={product.id ?? `product-${index}`}
|
|
75
|
+
variant={SalesAgentProductCardVariant.LARGE}
|
|
76
|
+
productName={product.productName}
|
|
77
|
+
currentPrice={product.currentPrice}
|
|
78
|
+
previousPrice={product.previousPrice}
|
|
79
|
+
pricePrefix={product.pricePrefix ?? '$'}
|
|
80
|
+
forceShowCurrentPriceSpace={forceShowCurrentPriceSpace}
|
|
81
|
+
rate={product.rate}
|
|
82
|
+
numberOfReviews={product.numberOfReviews}
|
|
83
|
+
image={product.image}
|
|
84
|
+
url={product.url}
|
|
85
|
+
target="_blank"
|
|
86
|
+
theme={finalTheme}
|
|
87
|
+
onClick={() => onProductCardClick?.(product)}
|
|
88
|
+
/>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
</Stack>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { ArrowIcon } from '../../Carousel/components/ArrowIcon';
|
|
4
|
+
import { Stack } from '../../Stack/Stack';
|
|
5
|
+
import { Typography, TypographyColor, TypographyVariant } from '../../Typography';
|
|
6
|
+
import { MOST_RELEVANT_DISPLAY_LIMIT } from '../utils/functions';
|
|
7
|
+
|
|
8
|
+
export interface ExploreAllResultsBadgeProps {
|
|
9
|
+
totalProducts: number;
|
|
10
|
+
displayLimit?: number;
|
|
11
|
+
onExploreAllResults: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const SalesAgentBadgeContent = ({
|
|
15
|
+
totalProducts,
|
|
16
|
+
displayLimit = MOST_RELEVANT_DISPLAY_LIMIT,
|
|
17
|
+
onExploreAllResults,
|
|
18
|
+
}: ExploreAllResultsBadgeProps) => {
|
|
19
|
+
return (
|
|
20
|
+
<Stack
|
|
21
|
+
direction="row"
|
|
22
|
+
justify="between"
|
|
23
|
+
align="center"
|
|
24
|
+
gap="1"
|
|
25
|
+
className="envive-tw-w-full envive-tw-rounded-global-custom envive-tw-p-2"
|
|
26
|
+
>
|
|
27
|
+
<Typography
|
|
28
|
+
variant={TypographyVariant.B3_MD}
|
|
29
|
+
color={TypographyColor.TEXT_PRIMARY}
|
|
30
|
+
>
|
|
31
|
+
Most Relevant ({displayLimit})
|
|
32
|
+
</Typography>
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
onClick={onExploreAllResults}
|
|
36
|
+
className={classNames(
|
|
37
|
+
'envive-tw-flex envive-tw-items-center envive-tw-gap-1',
|
|
38
|
+
'envive-tw-bg-background-secondary envive-tw-p-2',
|
|
39
|
+
'envive-tw-rounded-global-custom envive-tw-border envive-tw-border-border-medium',
|
|
40
|
+
'hover:envive-tw-border-border-dark',
|
|
41
|
+
)}
|
|
42
|
+
aria-label={`Explore all ${totalProducts} results`}
|
|
43
|
+
>
|
|
44
|
+
<Typography
|
|
45
|
+
variant={TypographyVariant.B3_MD}
|
|
46
|
+
color={TypographyColor.TEXT_LINK}
|
|
47
|
+
>
|
|
48
|
+
Explore All Results ({totalProducts})
|
|
49
|
+
</Typography>
|
|
50
|
+
<ArrowIcon direction="right" />
|
|
51
|
+
</button>
|
|
52
|
+
</Stack>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export interface GetBadgeContentAndLabelParams {
|
|
57
|
+
totalProducts: number;
|
|
58
|
+
areProductsMoreThanDisplayLimit: boolean;
|
|
59
|
+
onExploreAllResults?: () => void;
|
|
60
|
+
displayLimit?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface BadgeContentAndLabel {
|
|
64
|
+
badgeContent: React.ReactNode;
|
|
65
|
+
badgeLabel: string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const getBadgeContentAndLabel = ({
|
|
69
|
+
totalProducts,
|
|
70
|
+
areProductsMoreThanDisplayLimit,
|
|
71
|
+
onExploreAllResults,
|
|
72
|
+
displayLimit = MOST_RELEVANT_DISPLAY_LIMIT,
|
|
73
|
+
}: GetBadgeContentAndLabelParams): BadgeContentAndLabel => {
|
|
74
|
+
const badgeContent = areProductsMoreThanDisplayLimit ? (
|
|
75
|
+
<SalesAgentBadgeContent
|
|
76
|
+
totalProducts={totalProducts}
|
|
77
|
+
displayLimit={displayLimit}
|
|
78
|
+
onExploreAllResults={onExploreAllResults}
|
|
79
|
+
/>
|
|
80
|
+
) : null;
|
|
81
|
+
|
|
82
|
+
const badgeLabel =
|
|
83
|
+
!areProductsMoreThanDisplayLimit && totalProducts > 1 ? `${totalProducts} Products` : null;
|
|
84
|
+
|
|
85
|
+
return { badgeContent, badgeLabel };
|
|
86
|
+
};
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
SalesAgentProductCardVariant,
|
|
7
7
|
} from '../../SalesAgentProductCard/types/types';
|
|
8
8
|
import { resolveTheme } from '../../utils/resolveTheme';
|
|
9
|
+
import { getProductCarouselDisplayInfo } from '../utils/functions';
|
|
10
|
+
import { getBadgeContentAndLabel } from './SalesAgentBadgeContent';
|
|
9
11
|
|
|
10
12
|
export interface SalesAgentProductCardsCarouselProps {
|
|
11
13
|
theme: Theme;
|
|
@@ -14,6 +16,8 @@ export interface SalesAgentProductCardsCarouselProps {
|
|
|
14
16
|
products: SalesAgentProductCardProps[];
|
|
15
17
|
variant?: SalesAgentProductCardVariant;
|
|
16
18
|
onProductCardClick?: (product: SalesAgentProductCardProps) => void;
|
|
19
|
+
/** Called when user clicks "Explore All Results" (only relevant when products.length > 8) */
|
|
20
|
+
onExploreAllResults?: () => void;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export const SalesAgentProductCardsCarousel = ({
|
|
@@ -23,23 +27,30 @@ export const SalesAgentProductCardsCarousel = ({
|
|
|
23
27
|
numberOfProducts,
|
|
24
28
|
variant = SalesAgentProductCardVariant.LARGE,
|
|
25
29
|
onProductCardClick,
|
|
30
|
+
onExploreAllResults,
|
|
26
31
|
}: SalesAgentProductCardsCarouselProps) => {
|
|
27
32
|
const finalTheme = resolveTheme(theme);
|
|
33
|
+
const { totalProducts, areProductsMoreThanDisplayLimit, displayedProducts } =
|
|
34
|
+
getProductCarouselDisplayInfo(products, numberOfProducts);
|
|
28
35
|
|
|
29
|
-
const forceShowCurrentPriceSpace =
|
|
36
|
+
const forceShowCurrentPriceSpace = displayedProducts.some(
|
|
30
37
|
product => product.currentPrice && product.previousPrice !== product.currentPrice,
|
|
31
38
|
);
|
|
32
39
|
|
|
40
|
+
const { badgeContent, badgeLabel } = getBadgeContentAndLabel({
|
|
41
|
+
totalProducts,
|
|
42
|
+
areProductsMoreThanDisplayLimit,
|
|
43
|
+
onExploreAllResults,
|
|
44
|
+
});
|
|
45
|
+
|
|
33
46
|
return (
|
|
34
47
|
<Carousel
|
|
35
48
|
hideNavigation={hideNavigation}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
elements={products.map(product => (
|
|
49
|
+
badgeContent={badgeContent}
|
|
50
|
+
badgeLabel={badgeLabel}
|
|
51
|
+
elements={displayedProducts.map((product, index) => (
|
|
41
52
|
<SalesAgentProductCard
|
|
42
|
-
key={product.id}
|
|
53
|
+
key={product.id ?? `product-${index}`}
|
|
43
54
|
variant={variant}
|
|
44
55
|
productName={product.productName}
|
|
45
56
|
currentPrice={product.currentPrice}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
2
|
+
import { Theme } from '../../../../tokens/theme/theme';
|
|
3
|
+
import { SalesAgentProductCardProps } from '../../SalesAgentProductCard/types/types';
|
|
4
|
+
import { ResultsGridView } from './ResultsGridView';
|
|
5
|
+
import type { ResultsViewData } from '../hooks/useProductResultsView';
|
|
6
|
+
|
|
7
|
+
type SlideChatContentProps = {
|
|
8
|
+
isResultsView: boolean;
|
|
9
|
+
isResultsViewRef: React.MutableRefObject<boolean>;
|
|
10
|
+
resultsViewData: ResultsViewData | null;
|
|
11
|
+
onBackToChat: () => void;
|
|
12
|
+
onProductCardClick: (product: SalesAgentProductCardProps) => void;
|
|
13
|
+
theme: Theme;
|
|
14
|
+
chatMessages: React.ReactNode;
|
|
15
|
+
scrollToBottom: () => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const slideVariants = {
|
|
19
|
+
// Chat → Grid: chat exits left (-100%), grid enters from right (100%)
|
|
20
|
+
// Grid → Chat: grid exits right (100%), chat enters from left (-100%)
|
|
21
|
+
enter: (isResults: boolean) => ({ x: isResults ? '100%' : '-100%' }),
|
|
22
|
+
center: { x: 0 },
|
|
23
|
+
exit: (isResults: boolean) => ({ x: isResults ? '100%' : '-100%' }),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const SlideChatContent = ({
|
|
27
|
+
isResultsView,
|
|
28
|
+
isResultsViewRef,
|
|
29
|
+
resultsViewData,
|
|
30
|
+
onBackToChat,
|
|
31
|
+
onProductCardClick,
|
|
32
|
+
theme,
|
|
33
|
+
chatMessages,
|
|
34
|
+
scrollToBottom,
|
|
35
|
+
}: SlideChatContentProps) => {
|
|
36
|
+
const resultsViewContent = resultsViewData && (
|
|
37
|
+
<ResultsGridView
|
|
38
|
+
theme={theme}
|
|
39
|
+
searchQuery={resultsViewData.searchQuery}
|
|
40
|
+
products={resultsViewData.products}
|
|
41
|
+
onBackToChat={onBackToChat}
|
|
42
|
+
onProductCardClick={onProductCardClick}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className="envive-tw-relative envive-tw-min-h-[200px] envive-tw-w-full envive-tw-overflow-hidden">
|
|
48
|
+
<AnimatePresence
|
|
49
|
+
initial={false}
|
|
50
|
+
mode="wait"
|
|
51
|
+
>
|
|
52
|
+
<motion.div
|
|
53
|
+
key={isResultsView ? 'results' : 'chat'}
|
|
54
|
+
custom={isResultsView}
|
|
55
|
+
variants={slideVariants}
|
|
56
|
+
initial="enter"
|
|
57
|
+
animate="center"
|
|
58
|
+
exit="exit"
|
|
59
|
+
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
|
60
|
+
onAnimationComplete={() => {
|
|
61
|
+
if (!isResultsViewRef.current) {
|
|
62
|
+
scrollToBottom();
|
|
63
|
+
}
|
|
64
|
+
}}
|
|
65
|
+
className="envive-tw-w-full"
|
|
66
|
+
>
|
|
67
|
+
{isResultsView ? resultsViewContent : chatMessages}
|
|
68
|
+
</motion.div>
|
|
69
|
+
</AnimatePresence>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Layout } from './Layout';
|
|
2
|
+
import { ResultsGridView } from './ResultsGridView';
|
|
3
|
+
import { SlideChatContent } from './SlideChatContent';
|
|
2
4
|
import { UserMessage } from './UserMessage';
|
|
3
5
|
import { AgentMessage } from './AgentMessage';
|
|
4
6
|
import { ChatMessages } from './ChatMessages';
|
|
@@ -11,6 +13,8 @@ import { MessageDivider } from './MessageDivider';
|
|
|
11
13
|
|
|
12
14
|
export const FloatingChatComponents = {
|
|
13
15
|
Layout,
|
|
16
|
+
ResultsGridView,
|
|
17
|
+
SlideChatContent,
|
|
14
18
|
UserMessage,
|
|
15
19
|
AgentMessage,
|
|
16
20
|
ChatMessages,
|
|
@@ -23,4 +27,3 @@ export const FloatingChatComponents = {
|
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
export { ModalSheet } from './ModalSheet';
|
|
26
|
-
export type { ModalSheetControl } from './ModalSheet';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RefObject, useMemo } from 'react';
|
|
2
2
|
import { Message } from '@envive-ai/react-hooks/application/models';
|
|
3
3
|
import { useMessageFilter } from '@envive-ai/react-hooks/hooks/MessageFilter';
|
|
4
|
-
import type { ModalSheetControl } from '../
|
|
4
|
+
import type { ModalSheetControl } from '../snapConstants';
|
|
5
5
|
|
|
6
6
|
export interface UseFilteredChatMessagesProps {
|
|
7
7
|
messages: Message[][];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { getCleanProducts } from '../utils/functions';
|
|
3
|
+
|
|
4
|
+
export type ResultsViewData = {
|
|
5
|
+
products: ReturnType<typeof getCleanProducts>;
|
|
6
|
+
searchQuery: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type UseResultsViewProps = {
|
|
10
|
+
scrollToBottom: () => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useProductResultsView = ({ scrollToBottom }: UseResultsViewProps) => {
|
|
14
|
+
const [resultsViewData, setResultsViewData] = useState<ResultsViewData | null>(null);
|
|
15
|
+
const isResultsView = resultsViewData !== null;
|
|
16
|
+
|
|
17
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
18
|
+
const isResultsViewRef = useRef(isResultsView);
|
|
19
|
+
isResultsViewRef.current = isResultsView;
|
|
20
|
+
|
|
21
|
+
const handleBackToChat = () => {
|
|
22
|
+
setResultsViewData(null);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Scroll to top when navigating to results view
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (isResultsView && scrollContainerRef.current) {
|
|
28
|
+
scrollContainerRef.current.scrollTo({ top: 0, behavior: 'smooth' });
|
|
29
|
+
}
|
|
30
|
+
}, [isResultsView]);
|
|
31
|
+
|
|
32
|
+
// Scroll to bottom when navigating back to chat
|
|
33
|
+
const prevIsResultsViewRef = useRef(isResultsView);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (prevIsResultsViewRef.current && !isResultsView) {
|
|
36
|
+
scrollToBottom();
|
|
37
|
+
}
|
|
38
|
+
prevIsResultsViewRef.current = isResultsView;
|
|
39
|
+
}, [isResultsView, scrollToBottom]);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
resultsViewData,
|
|
43
|
+
setResultsViewData,
|
|
44
|
+
isResultsView,
|
|
45
|
+
scrollContainerRef,
|
|
46
|
+
isResultsViewRef,
|
|
47
|
+
handleBackToChat,
|
|
48
|
+
};
|
|
49
|
+
};
|