@envive-ai/react-toolkit-v3 0.3.18 → 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/AnimatedText/AnimatedText.d.ts +3 -3
- package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.cts +2 -2
- package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.ts +2 -2
- package/dist/Carousel/Carousel.cjs +1 -1
- package/dist/Carousel/Carousel.d.cts +2 -2
- package/dist/Carousel/Carousel.d.ts +2 -2
- package/dist/Carousel/Carousel.js +1 -1
- package/dist/Carousel/components/Container.cjs +2 -2
- package/dist/Carousel/components/Container.js +2 -2
- 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/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.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 +175 -175
- package/dist/Container/Container.d.ts +175 -175
- 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/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 +56 -20
- package/dist/FloatingChat/FloatingChat.d.cts +2 -2
- package/dist/FloatingChat/FloatingChat.d.ts +2 -2
- package/dist/FloatingChat/FloatingChat.js +57 -21
- package/dist/FloatingChat/components/AgentMessage.cjs +1 -1
- package/dist/FloatingChat/components/AgentMessage.js +1 -1
- package/dist/FloatingChat/components/ChatMessages.cjs +2 -2
- package/dist/FloatingChat/components/ChatMessages.js +2 -2
- 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 +1 -1
- package/dist/FloatingChat/components/SalesAgentBadgeContent.js +1 -1
- 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 +16 -1
- package/dist/FloatingChat/utils/functions.js +15 -1
- package/dist/FullPageSalesAgent/FullPageSalesAgent.cjs +2 -2
- package/dist/FullPageSalesAgent/FullPageSalesAgent.d.cts +2 -2
- package/dist/FullPageSalesAgent/FullPageSalesAgent.d.ts +2 -2
- package/dist/FullPageSalesAgent/FullPageSalesAgent.js +2 -2
- 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 +4 -4
- 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 +6 -6
- 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/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.d.ts +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/FloatingChat/FloatingChat.tsx +80 -25
- package/src/components/FloatingChat/components/Layout.tsx +7 -1
- package/src/components/FloatingChat/components/ResultsGridView.tsx +93 -0
- package/src/components/FloatingChat/components/SlideChatContent.tsx +72 -0
- package/src/components/FloatingChat/components/index.ts +4 -0
- package/src/components/FloatingChat/hooks/useProductResultsView.ts +49 -0
- package/src/components/FloatingChat/utils/functions.ts +22 -0
- package/src/logging/logger.ts +33 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
ChatElementDisplayLocationV3,
|
|
@@ -21,8 +21,8 @@ import { PromptButtonVariant } from '../PromptButton';
|
|
|
21
21
|
import { PromptCarousel, PromptCarouselRows, usePromptCarouselAnalytics } from '../PromptCarousel';
|
|
22
22
|
import { WelcomeMessage } from '../WelcomeMessage/WelcomeMessage';
|
|
23
23
|
import { SparkleIconColor } from '../WelcomeMessage/types/types';
|
|
24
|
-
import { FloatingChatComponents, ModalSheet } from './components';
|
|
25
24
|
import { useChatSuggestions } from './hooks/useChatSuggestions';
|
|
25
|
+
import { useProductResultsView } from './hooks/useProductResultsView';
|
|
26
26
|
import { useScrollToBottom } from './hooks/useScrollToBottom';
|
|
27
27
|
import { useSnapSetup } from './hooks/useSnapSetup';
|
|
28
28
|
|
|
@@ -31,7 +31,9 @@ import { resolveTheme } from '../utils/resolveTheme';
|
|
|
31
31
|
import { useFilteredChatMessages } from './hooks/useFilteredChatMessages';
|
|
32
32
|
import { Unit } from './hooks/useSnapCalculator';
|
|
33
33
|
import { FloatingChatProps } from './types/types';
|
|
34
|
-
import { getCleanProducts } from './utils/functions';
|
|
34
|
+
import { getCleanProducts, getSearchQueryFromMessageBlock } from './utils/functions';
|
|
35
|
+
import { FloatingChatComponents, ModalSheet } from './components';
|
|
36
|
+
import { SalesAgentProductCardProps } from '../SalesAgentProductCard/types/types';
|
|
35
37
|
|
|
36
38
|
export const FloatingChat = ({
|
|
37
39
|
id,
|
|
@@ -80,6 +82,19 @@ export const FloatingChat = ({
|
|
|
80
82
|
rightToggleLabel,
|
|
81
83
|
} = hardcopyContent?.values ?? {};
|
|
82
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
|
+
|
|
83
98
|
const { agentName, chatHeaderLogoDarkSrc, chatHeaderLogoLightSrc } = lookAndFeelConfig;
|
|
84
99
|
|
|
85
100
|
const {
|
|
@@ -141,6 +156,15 @@ export const FloatingChat = ({
|
|
|
141
156
|
isOpen: isFloatingChatOpen,
|
|
142
157
|
});
|
|
143
158
|
|
|
159
|
+
const {
|
|
160
|
+
resultsViewData,
|
|
161
|
+
setResultsViewData,
|
|
162
|
+
isResultsView,
|
|
163
|
+
scrollContainerRef,
|
|
164
|
+
isResultsViewRef,
|
|
165
|
+
handleBackToChat,
|
|
166
|
+
} = useProductResultsView({ scrollToBottom });
|
|
167
|
+
|
|
144
168
|
useEffect(() => {
|
|
145
169
|
if (isFloatingChatOpen) {
|
|
146
170
|
trackWidgetInteraction({
|
|
@@ -182,6 +206,21 @@ export const FloatingChat = ({
|
|
|
182
206
|
modalSheetControl,
|
|
183
207
|
});
|
|
184
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
|
+
|
|
185
224
|
const header = (
|
|
186
225
|
<ChatHeader
|
|
187
226
|
logoDark={chatHeaderLogoDarkSrc}
|
|
@@ -254,6 +293,7 @@ export const FloatingChat = ({
|
|
|
254
293
|
});
|
|
255
294
|
setAnswerSuggestions([]);
|
|
256
295
|
setGeneralSuggestions([]);
|
|
296
|
+
handleBackToChat();
|
|
257
297
|
}}
|
|
258
298
|
textFieldPlaceholderText={chatFooterTextFieldPlaceholderText as string}
|
|
259
299
|
promptSuggestions={
|
|
@@ -269,6 +309,7 @@ export const FloatingChat = ({
|
|
|
269
309
|
|
|
270
310
|
setAnswerSuggestions([]);
|
|
271
311
|
setGeneralSuggestions([]);
|
|
312
|
+
handleBackToChat();
|
|
272
313
|
}}
|
|
273
314
|
disabled={isPendingResponse || isResponseStreaming}
|
|
274
315
|
disabledInput={!userQueryInputEnabled}
|
|
@@ -291,15 +332,18 @@ export const FloatingChat = ({
|
|
|
291
332
|
);
|
|
292
333
|
|
|
293
334
|
const handleExploreAllResults = (firstProductMessageId: string) => {
|
|
294
|
-
const
|
|
335
|
+
const blockIndex = messages.findIndex(block =>
|
|
295
336
|
block.some(msg => msg.type === MessageType.Product && msg.id === firstProductMessageId),
|
|
296
337
|
);
|
|
297
|
-
|
|
338
|
+
if (blockIndex < 0) return;
|
|
339
|
+
|
|
340
|
+
const messageBlock = messages[blockIndex];
|
|
341
|
+
const productMessages = messageBlock.filter(msg => msg.type === MessageType.Product);
|
|
298
342
|
const products = getCleanProducts(productMessages);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
});
|
|
343
|
+
if (products.length === 0) return;
|
|
344
|
+
|
|
345
|
+
const searchQuery = getSearchQueryFromMessageBlock(messages, blockIndex);
|
|
346
|
+
setResultsViewData({ products, searchQuery });
|
|
303
347
|
};
|
|
304
348
|
|
|
305
349
|
const chatMessages = (
|
|
@@ -321,6 +365,19 @@ export const FloatingChat = ({
|
|
|
321
365
|
/>
|
|
322
366
|
);
|
|
323
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}
|
|
378
|
+
/>
|
|
379
|
+
);
|
|
380
|
+
|
|
324
381
|
const answerSuggestionsComponent = (
|
|
325
382
|
<PromptCarousel
|
|
326
383
|
className="envive-tw-flex envive-tw-justify-end envive-tw-p-4 [&>div>div]:envive-tw-items-end"
|
|
@@ -354,20 +411,19 @@ export const FloatingChat = ({
|
|
|
354
411
|
theme={finalTheme}
|
|
355
412
|
header={header}
|
|
356
413
|
footer={footer}
|
|
357
|
-
welcomeMessage={welcomeMessage}
|
|
358
|
-
chatMessages={
|
|
359
|
-
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent :
|
|
414
|
+
welcomeMessage={isResultsView ? null : welcomeMessage}
|
|
415
|
+
chatMessages={middleContent}
|
|
416
|
+
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent : null}
|
|
360
417
|
scrollToBottomButton={
|
|
361
|
-
showScrollButton ? (
|
|
418
|
+
!isResultsView && showScrollButton ? (
|
|
362
419
|
<FloatingChatComponents.ScrollToBottomButton onClick={handleScrollToBottom} />
|
|
363
|
-
) :
|
|
420
|
+
) : null
|
|
364
421
|
}
|
|
365
422
|
disclaimer={
|
|
366
|
-
|
|
367
|
-
<Disclaimer disclaimerMarkdown={disclaimerText} />
|
|
368
|
-
) : undefined
|
|
423
|
+
disclaimerTextString ? <Disclaimer disclaimerMarkdown={disclaimerTextString} /> : null
|
|
369
424
|
}
|
|
370
425
|
debugBar={debugBar}
|
|
426
|
+
scrollContainerRef={scrollContainerRef}
|
|
371
427
|
/>
|
|
372
428
|
);
|
|
373
429
|
|
|
@@ -380,20 +436,19 @@ export const FloatingChat = ({
|
|
|
380
436
|
theme={finalTheme}
|
|
381
437
|
header={mobileHeader}
|
|
382
438
|
footer={footer}
|
|
383
|
-
welcomeMessage={welcomeMessage}
|
|
384
|
-
chatMessages={
|
|
385
|
-
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent :
|
|
439
|
+
welcomeMessage={isResultsView ? null : welcomeMessage}
|
|
440
|
+
chatMessages={middleContent}
|
|
441
|
+
answerSuggestions={showAnswerSuggestions ? answerSuggestionsComponent : null}
|
|
386
442
|
scrollToBottomButton={
|
|
387
|
-
showScrollButton ? (
|
|
443
|
+
!isResultsView && showScrollButton ? (
|
|
388
444
|
<FloatingChatComponents.ScrollToBottomButton onClick={handleScrollToBottom} />
|
|
389
|
-
) :
|
|
445
|
+
) : null
|
|
390
446
|
}
|
|
391
447
|
disclaimer={
|
|
392
|
-
|
|
393
|
-
<Disclaimer disclaimerMarkdown={disclaimerText} />
|
|
394
|
-
) : undefined
|
|
448
|
+
disclaimerTextString ? <Disclaimer disclaimerMarkdown={disclaimerTextString} /> : null
|
|
395
449
|
}
|
|
396
450
|
isFloatingFooterLayout={isFloatingLayout}
|
|
451
|
+
scrollContainerRef={scrollContainerRef}
|
|
397
452
|
/>
|
|
398
453
|
);
|
|
399
454
|
|
|
@@ -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"
|
|
@@ -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,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,
|
|
@@ -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
|
+
};
|
|
@@ -121,6 +121,28 @@ export const getCleanOrders = (messages: Message[]) => {
|
|
|
121
121
|
|
|
122
122
|
export const MOST_RELEVANT_DISPLAY_LIMIT = 8;
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Extracts the search query from a message block (or previous blocks) that led to product results.
|
|
126
|
+
* Looks for QueryTyped (metadata.content) or SuggestionClicked (metadata.suggestionContent).
|
|
127
|
+
*/
|
|
128
|
+
export const getSearchQueryFromMessageBlock = (
|
|
129
|
+
messages: Message[][],
|
|
130
|
+
productBlockIndex: number,
|
|
131
|
+
): string => {
|
|
132
|
+
for (let i = productBlockIndex; i >= 0; i -= 1) {
|
|
133
|
+
const block = messages[i];
|
|
134
|
+
const queryTyped = block.find(msg => msg.type === MessageType.QueryTyped);
|
|
135
|
+
if (queryTyped && 'content' in queryTyped.metadata) {
|
|
136
|
+
return (queryTyped.metadata as { content?: string }).content ?? '';
|
|
137
|
+
}
|
|
138
|
+
const suggestionClicked = block.find(msg => msg.type === MessageType.SuggestionClicked);
|
|
139
|
+
if (suggestionClicked && 'suggestionContent' in suggestionClicked.metadata) {
|
|
140
|
+
return (suggestionClicked.metadata as { suggestionContent?: string }).suggestionContent ?? '';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return '';
|
|
144
|
+
};
|
|
145
|
+
|
|
124
146
|
export const getProductCarouselDisplayInfo = <T>(
|
|
125
147
|
products: T[],
|
|
126
148
|
numberOfProducts?: number,
|
package/src/logging/logger.ts
CHANGED
|
@@ -1,20 +1,45 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
|
|
3
3
|
class Logger {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
private readonly caller: string;
|
|
5
|
+
|
|
6
|
+
/* Creates a new Logger instance.
|
|
7
|
+
* @param caller - The caller of the logger.
|
|
8
|
+
*/
|
|
9
|
+
constructor(caller: string) {
|
|
10
|
+
this.caller = caller;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static getTimestamp(): string {
|
|
14
|
+
return new Date().toISOString();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
logInfo(message: string, ...args: unknown[]): void {
|
|
18
|
+
console.info(
|
|
19
|
+
`INFO: [envive-ai] ${Logger.getTimestamp()} - ${this.caller} - ${message}`,
|
|
20
|
+
...args,
|
|
21
|
+
);
|
|
6
22
|
}
|
|
7
23
|
|
|
8
|
-
|
|
9
|
-
console.debug(
|
|
24
|
+
logDebug(message: string, ...args: unknown[]): void {
|
|
25
|
+
console.debug(
|
|
26
|
+
`DEBUG: [envive-ai] ${Logger.getTimestamp()} - ${this.caller} - ${message}`,
|
|
27
|
+
...args,
|
|
28
|
+
);
|
|
10
29
|
}
|
|
11
30
|
|
|
12
|
-
|
|
13
|
-
console.error(
|
|
31
|
+
logError(message: string, error: unknown | undefined, ...args: unknown[]): void {
|
|
32
|
+
console.error(
|
|
33
|
+
`ERROR: [envive-ai] ${Logger.getTimestamp()} - ${this.caller} - ${message} error=${error}`,
|
|
34
|
+
args,
|
|
35
|
+
);
|
|
14
36
|
}
|
|
15
37
|
|
|
16
|
-
|
|
17
|
-
console.warn(
|
|
38
|
+
logWarn(message: string, error: unknown | undefined, ...args: unknown[]): void {
|
|
39
|
+
console.warn(
|
|
40
|
+
`WARN: [envive-ai] ${Logger.getTimestamp()} - ${this.caller} - ${message} error=${error}`,
|
|
41
|
+
args,
|
|
42
|
+
);
|
|
18
43
|
}
|
|
19
44
|
}
|
|
20
45
|
|