@constructor-io/constructorio-ui-pia 2.8.0 → 2.9.0
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/constructorio-ui-pia-bundled.js +76 -73
- package/lib/cjs/components/CioPia/CioPia.js +27 -5
- package/lib/cjs/components/CioPia/PiaCustomCarousel.js +6 -2
- package/lib/cjs/components/ConversationHistory/ConversationHistory.js +3 -3
- package/lib/cjs/components/PiaConversation/PiaConversation.js +2 -2
- package/lib/cjs/components/PiaInlineAnswer/PiaInlineAnswer.js +3 -3
- package/lib/cjs/constants.js +2 -1
- package/lib/cjs/hooks/useCioClient.js +2 -2
- package/lib/cjs/hooks/useCioPia.js +12 -3
- package/lib/cjs/hooks/useConversation.js +32 -9
- package/lib/cjs/hooks/useTracking.js +71 -0
- package/lib/cjs/hooks/useViewportTracking.js +92 -0
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/CioPia/CioPia.js +26 -5
- package/lib/mjs/components/CioPia/PiaCustomCarousel.js +6 -2
- package/lib/mjs/components/ConversationHistory/ConversationHistory.js +3 -3
- package/lib/mjs/components/PiaConversation/PiaConversation.js +2 -2
- package/lib/mjs/components/PiaInlineAnswer/PiaInlineAnswer.js +3 -3
- package/lib/mjs/constants.js +1 -0
- package/lib/mjs/hooks/useCioClient.js +2 -2
- package/lib/mjs/hooks/useCioPia.js +13 -4
- package/lib/mjs/hooks/useConversation.js +24 -2
- package/lib/mjs/hooks/useTracking.js +107 -0
- package/lib/mjs/hooks/useViewportTracking.js +89 -0
- package/lib/mjs/version.js +1 -1
- package/lib/types/components/CioPia/CioPia.d.ts +2 -0
- package/lib/types/components/CioPia/PiaCustomCarousel.d.ts +4 -1
- package/lib/types/components/ConversationHistory/ConversationHistory.d.ts +5 -2
- package/lib/types/components/PiaConversation/PiaConversation.d.ts +5 -2
- package/lib/types/components/PiaInlineAnswer/PiaInlineAnswer.d.ts +6 -2
- package/lib/types/constants.d.ts +1 -0
- package/lib/types/hooks/useCioClient.d.ts +3 -7
- package/lib/types/hooks/useCioPia.d.ts +4 -0
- package/lib/types/hooks/useConversation.d.ts +5 -2
- package/lib/types/hooks/useTracking.d.ts +27 -0
- package/lib/types/hooks/useViewportTracking.d.ts +10 -0
- package/lib/types/utils/transformers.d.ts +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +6 -3
|
@@ -3,12 +3,12 @@ import Answer from '../Answer/Answer';
|
|
|
3
3
|
import Feedback from '../Feedback/Feedback';
|
|
4
4
|
import Disclaimer from '../CioPia/Disclaimer';
|
|
5
5
|
import PiaCustomCarousel from '../CioPia/PiaCustomCarousel';
|
|
6
|
-
export default function PiaInlineAnswer({ currentAnswer, currentItems, showFeedback, learnMoreUrl, disclaimerPosition = 'bottom', translations, callbacks, componentOverrides, }) {
|
|
6
|
+
export default function PiaInlineAnswer({ currentAnswer, currentItems, showFeedback, learnMoreUrl, disclaimerPosition = 'bottom', translations, callbacks, componentOverrides, onFeedback, onResultClick, question, qnaResultId, }) {
|
|
7
7
|
const disclaimer = (React.createElement(Disclaimer, { learnMoreUrl: learnMoreUrl, translations: translations, componentOverride: componentOverrides?.disclaimer }));
|
|
8
8
|
return (React.createElement("div", { className: 'cio-pia-answer-container' },
|
|
9
9
|
disclaimerPosition === 'top' && disclaimer,
|
|
10
10
|
React.createElement(Answer, { text: currentAnswer, componentOverride: componentOverrides?.answer }),
|
|
11
|
-
currentItems && (React.createElement(PiaCustomCarousel, { items: currentItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks })),
|
|
12
|
-
showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: callbacks?.onFeedback, componentOverride: componentOverrides?.feedback })),
|
|
11
|
+
currentItems && (React.createElement(PiaCustomCarousel, { items: currentItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks, onResultClick: onResultClick, question: question, qnaResultId: qnaResultId })),
|
|
12
|
+
showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: onFeedback || callbacks?.onFeedback, componentOverride: componentOverrides?.feedback })),
|
|
13
13
|
disclaimerPosition === 'bottom' && disclaimer));
|
|
14
14
|
}
|
package/lib/mjs/constants.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const DEMO_API_KEY = 'key_13TO6qN9H3BP3Wrn';
|
|
2
2
|
export const DEMO_ITEM_ID = '149100215';
|
|
3
|
+
export const DEMO_ITEM_NAME = 'Ensure Original Nutrition Shake';
|
|
3
4
|
export const DEMO_QUESTION = 'What is the nutritional content of this shake?';
|
|
4
5
|
export const DEMO_QUESTION_ALTERNATIVE_PRODUCTS = 'What alternative products are available for this item?';
|
|
5
6
|
export const MOCK_QUESTIONS = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
+
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
3
|
import version from '../version';
|
|
3
|
-
import MockConstructorIOClient from './mocks/MockConstructorIOClient';
|
|
4
4
|
const useCioClient = ({ apiKey, cioClient, options } = {}) => {
|
|
5
5
|
if (!apiKey && !cioClient) {
|
|
6
6
|
throw new Error('Api Key or Constructor Client required');
|
|
@@ -9,7 +9,7 @@ const useCioClient = ({ apiKey, cioClient, options } = {}) => {
|
|
|
9
9
|
if (cioClient)
|
|
10
10
|
return cioClient;
|
|
11
11
|
if (apiKey) {
|
|
12
|
-
return new
|
|
12
|
+
return new ConstructorIOClient({
|
|
13
13
|
apiKey,
|
|
14
14
|
sendTrackingEvents: true,
|
|
15
15
|
version: `cio-ui-pia-${version}`,
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
import MockConstructorIOClient from './mocks/MockConstructorIOClient';
|
|
2
3
|
import useAnswerResults from './useAnswerResults';
|
|
3
|
-
import useCioClient from './useCioClient';
|
|
4
4
|
import useSuggestedQuestions from './useSuggestedQuestions';
|
|
5
|
+
import version from '../version';
|
|
5
6
|
export default function useCioPia(props) {
|
|
6
7
|
const { apiKey, itemId, variationId, threadId: providedThreadId, cioClient: providedClient, suggestedQuestionsParameters, formatImageUrl, } = props;
|
|
7
8
|
const [generatedThreadId] = useState(() => crypto.randomUUID());
|
|
8
9
|
const threadId = providedThreadId || generatedThreadId;
|
|
9
|
-
const
|
|
10
|
-
|
|
10
|
+
const client = useMemo(() => {
|
|
11
|
+
if (providedClient)
|
|
12
|
+
return providedClient;
|
|
13
|
+
return new MockConstructorIOClient({
|
|
14
|
+
apiKey,
|
|
15
|
+
sendTrackingEvents: true,
|
|
16
|
+
version: `cio-ui-pia-${version}`,
|
|
17
|
+
});
|
|
18
|
+
}, [apiKey, providedClient]);
|
|
11
19
|
const suggestedQuestions = useSuggestedQuestions({
|
|
12
20
|
itemId,
|
|
13
21
|
variationId,
|
|
@@ -23,6 +31,7 @@ export default function useCioPia(props) {
|
|
|
23
31
|
formatImageUrl,
|
|
24
32
|
});
|
|
25
33
|
return {
|
|
34
|
+
cioClient: client,
|
|
26
35
|
threadId,
|
|
27
36
|
suggestedQuestions,
|
|
28
37
|
answers,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
export default function useConversation({ pia, itemId, isConversation, callbacks, }) {
|
|
2
|
+
export default function useConversation({ pia, itemId, isConversation, callbacks, tracking, }) {
|
|
3
3
|
const { suggestedQuestions, answers, threadId } = pia;
|
|
4
4
|
const { getAnswer } = answers;
|
|
5
5
|
const context = useMemo(() => ({ itemId, threadId }), [itemId, threadId]);
|
|
@@ -8,8 +8,11 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
8
8
|
const [conversationHistory, setConversationHistory] = useState([]);
|
|
9
9
|
const entryIdRef = useRef(0);
|
|
10
10
|
const prevAnswerValueRef = useRef(answers.data?.value);
|
|
11
|
+
const hasTrackedCurrentAnswerRef = useRef(false);
|
|
12
|
+
const answersRef = useRef(answers);
|
|
11
13
|
const callbacksRef = useRef(callbacks);
|
|
12
14
|
const contextRef = useRef(context);
|
|
15
|
+
const trackingRef = useRef(tracking);
|
|
13
16
|
const lastQuestionRef = useRef('');
|
|
14
17
|
const lastSourceRef = useRef('user');
|
|
15
18
|
useEffect(() => {
|
|
@@ -18,9 +21,16 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
18
21
|
useEffect(() => {
|
|
19
22
|
contextRef.current = context;
|
|
20
23
|
}, [context]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
trackingRef.current = tracking;
|
|
26
|
+
}, [tracking]);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
answersRef.current = answers;
|
|
29
|
+
}, [answers]);
|
|
21
30
|
const submitQuestion = useCallback((question, source) => {
|
|
22
31
|
lastSourceRef.current = source;
|
|
23
32
|
lastQuestionRef.current = question;
|
|
33
|
+
hasTrackedCurrentAnswerRef.current = false;
|
|
24
34
|
setCurrentQuestion(question);
|
|
25
35
|
getAnswer(question);
|
|
26
36
|
if (isConversation) {
|
|
@@ -30,16 +40,23 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
30
40
|
}
|
|
31
41
|
}, [getAnswer, isConversation]);
|
|
32
42
|
const handleSubmitQuestion = useCallback((question) => {
|
|
43
|
+
trackingRef.current?.trackQuestionSubmit(question);
|
|
33
44
|
callbacksRef.current?.onQuestionSubmit?.(question, contextRef.current, 'user');
|
|
34
45
|
submitQuestion(question, 'user');
|
|
35
46
|
}, [submitQuestion]);
|
|
36
47
|
const handleQuestionClick = useCallback((question) => {
|
|
48
|
+
trackingRef.current?.trackQuestionClick(question);
|
|
37
49
|
callbacksRef.current?.onQuestionSubmit?.(question, contextRef.current, 'suggestion');
|
|
38
50
|
submitQuestion(question, 'suggestion');
|
|
39
51
|
}, [submitQuestion]);
|
|
40
52
|
const handleInputFocus = useCallback(() => {
|
|
53
|
+
trackingRef.current?.trackFocus();
|
|
41
54
|
callbacksRef.current?.onFocus?.(contextRef.current);
|
|
42
55
|
}, []);
|
|
56
|
+
const handleFeedback = useCallback((type) => {
|
|
57
|
+
trackingRef.current?.trackAnswerFeedback(type, answersRef.current.data?.qna_result_id);
|
|
58
|
+
callbacksRef.current?.onFeedback?.(type);
|
|
59
|
+
}, []);
|
|
43
60
|
const resetState = useCallback(() => {
|
|
44
61
|
setCurrentQuestion('');
|
|
45
62
|
setDisplayedQuestions(suggestedQuestions.data);
|
|
@@ -58,7 +75,11 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
58
75
|
useEffect(() => {
|
|
59
76
|
if (answers.data?.follow_up_questions)
|
|
60
77
|
setDisplayedQuestions(answers.data.follow_up_questions);
|
|
61
|
-
|
|
78
|
+
if (answers.data && lastQuestionRef.current && !hasTrackedCurrentAnswerRef.current) {
|
|
79
|
+
hasTrackedCurrentAnswerRef.current = true;
|
|
80
|
+
trackingRef.current?.trackAnswerView(lastQuestionRef.current, answers.data, answers.items);
|
|
81
|
+
}
|
|
82
|
+
}, [answers.data, answers.items]);
|
|
62
83
|
useEffect(() => {
|
|
63
84
|
const answerValue = answers.data?.value ?? '';
|
|
64
85
|
if (!answerValue)
|
|
@@ -113,6 +134,7 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
113
134
|
handleSubmitQuestion,
|
|
114
135
|
handleQuestionClick,
|
|
115
136
|
handleInputFocus,
|
|
137
|
+
handleFeedback,
|
|
116
138
|
resetState,
|
|
117
139
|
};
|
|
118
140
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { FeedbackType } from '../types';
|
|
3
|
+
export default function useTracking({ cioClient, itemId, itemName, variationId, threadId, }) {
|
|
4
|
+
const tracker = cioClient?.tracker;
|
|
5
|
+
const baseParams = useMemo(() => ({
|
|
6
|
+
itemId,
|
|
7
|
+
itemName,
|
|
8
|
+
...(variationId && { variationId }),
|
|
9
|
+
...(threadId && { threadId }),
|
|
10
|
+
}), [itemId, itemName, variationId, threadId]);
|
|
11
|
+
const mapQuestions = useCallback((questions) => questions.map((q) => ({ question: q.value })), []);
|
|
12
|
+
const trackViews = useCallback((questions, viewTimespans) => {
|
|
13
|
+
tracker?.trackProductInsightsAgentViews({
|
|
14
|
+
...baseParams,
|
|
15
|
+
questions: mapQuestions(questions),
|
|
16
|
+
viewTimespans,
|
|
17
|
+
});
|
|
18
|
+
}, [tracker, baseParams, mapQuestions]);
|
|
19
|
+
const trackView = useCallback((questions) => {
|
|
20
|
+
tracker?.trackProductInsightsAgentView({
|
|
21
|
+
...baseParams,
|
|
22
|
+
questions: mapQuestions(questions),
|
|
23
|
+
});
|
|
24
|
+
}, [tracker, baseParams, mapQuestions]);
|
|
25
|
+
const trackOutOfView = useCallback(() => {
|
|
26
|
+
tracker?.trackProductInsightsAgentOutOfView(baseParams);
|
|
27
|
+
}, [tracker, baseParams]);
|
|
28
|
+
const trackFocus = useCallback(() => {
|
|
29
|
+
tracker?.trackProductInsightsAgentFocus(baseParams);
|
|
30
|
+
}, [tracker, baseParams]);
|
|
31
|
+
const trackQuestionClick = useCallback((question) => {
|
|
32
|
+
tracker?.trackProductInsightsAgentQuestionClick({
|
|
33
|
+
...baseParams,
|
|
34
|
+
question,
|
|
35
|
+
});
|
|
36
|
+
}, [tracker, baseParams]);
|
|
37
|
+
const trackQuestionSubmit = useCallback((question) => {
|
|
38
|
+
tracker?.trackProductInsightsAgentQuestionSubmit({
|
|
39
|
+
...baseParams,
|
|
40
|
+
question,
|
|
41
|
+
});
|
|
42
|
+
}, [tracker, baseParams]);
|
|
43
|
+
const trackAnswerView = useCallback((question, answerData, items) => {
|
|
44
|
+
tracker?.trackProductInsightsAgentAnswerView({
|
|
45
|
+
...baseParams,
|
|
46
|
+
question,
|
|
47
|
+
answerText: answerData.value,
|
|
48
|
+
qnaResultId: answerData.qna_result_id,
|
|
49
|
+
...(items &&
|
|
50
|
+
items.length > 0 && {
|
|
51
|
+
items: items.map((item) => ({
|
|
52
|
+
itemId: item.id,
|
|
53
|
+
itemName: item.name,
|
|
54
|
+
})),
|
|
55
|
+
}),
|
|
56
|
+
...(answerData.follow_up_questions &&
|
|
57
|
+
answerData.follow_up_questions.length > 0 && {
|
|
58
|
+
followUpQuestions: answerData.follow_up_questions.map((q) => ({
|
|
59
|
+
value: q.value,
|
|
60
|
+
})),
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
}, [tracker, baseParams]);
|
|
64
|
+
const trackAnswerFeedback = useCallback((feedbackType, qnaResultId) => {
|
|
65
|
+
const feedbackLabel = feedbackType === FeedbackType.UP ? 'thumbs_up' : 'thumbs_down';
|
|
66
|
+
tracker?.trackProductInsightsAgentAnswerFeedback({
|
|
67
|
+
...baseParams,
|
|
68
|
+
feedbackLabel,
|
|
69
|
+
...(qnaResultId && { qnaResultId }),
|
|
70
|
+
});
|
|
71
|
+
}, [tracker, baseParams]);
|
|
72
|
+
const trackResultClick = useCallback((clickedItem, position, question, qnaResultId) => {
|
|
73
|
+
tracker?.trackProductInsightsAgentResultClick({
|
|
74
|
+
itemId: clickedItem.id,
|
|
75
|
+
itemName: clickedItem.name,
|
|
76
|
+
...(clickedItem.variationId && { variationId: clickedItem.variationId }),
|
|
77
|
+
question,
|
|
78
|
+
seedItemId: itemId,
|
|
79
|
+
seedItemName: itemName,
|
|
80
|
+
...(variationId && { seedVariationId: variationId }),
|
|
81
|
+
...(qnaResultId && { qnaResultId }),
|
|
82
|
+
...(threadId && { threadId }),
|
|
83
|
+
position,
|
|
84
|
+
});
|
|
85
|
+
}, [tracker, itemId, itemName, variationId, threadId]);
|
|
86
|
+
return useMemo(() => ({
|
|
87
|
+
trackViews,
|
|
88
|
+
trackView,
|
|
89
|
+
trackOutOfView,
|
|
90
|
+
trackFocus,
|
|
91
|
+
trackQuestionClick,
|
|
92
|
+
trackQuestionSubmit,
|
|
93
|
+
trackAnswerView,
|
|
94
|
+
trackAnswerFeedback,
|
|
95
|
+
trackResultClick,
|
|
96
|
+
}), [
|
|
97
|
+
trackViews,
|
|
98
|
+
trackView,
|
|
99
|
+
trackOutOfView,
|
|
100
|
+
trackFocus,
|
|
101
|
+
trackQuestionClick,
|
|
102
|
+
trackQuestionSubmit,
|
|
103
|
+
trackAnswerView,
|
|
104
|
+
trackAnswerFeedback,
|
|
105
|
+
trackResultClick,
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
const FLUSH_INTERVAL_MS = 5 * 60 * 1000;
|
|
3
|
+
export default function useViewportTracking({ tracking, questions, }) {
|
|
4
|
+
const nodeRef = useRef(null);
|
|
5
|
+
const observerRef = useRef(null);
|
|
6
|
+
const intervalRef = useRef(null);
|
|
7
|
+
const timespansRef = useRef([]);
|
|
8
|
+
const entryTimeRef = useRef(null);
|
|
9
|
+
const questionsRef = useRef(questions);
|
|
10
|
+
const trackingRef = useRef(tracking);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const questionsJustArrived = questionsRef.current.length === 0 && questions.length > 0;
|
|
13
|
+
questionsRef.current = questions;
|
|
14
|
+
if (questionsJustArrived && entryTimeRef.current) {
|
|
15
|
+
trackingRef.current.trackView(questions);
|
|
16
|
+
}
|
|
17
|
+
}, [questions]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
trackingRef.current = tracking;
|
|
20
|
+
}, [tracking]);
|
|
21
|
+
// Stable: only reads refs
|
|
22
|
+
const flush = useCallback(() => {
|
|
23
|
+
if (entryTimeRef.current) {
|
|
24
|
+
timespansRef.current.push({
|
|
25
|
+
start: entryTimeRef.current,
|
|
26
|
+
end: new Date().toISOString(),
|
|
27
|
+
});
|
|
28
|
+
entryTimeRef.current = null;
|
|
29
|
+
}
|
|
30
|
+
if (timespansRef.current.length > 0 && questionsRef.current.length > 0) {
|
|
31
|
+
trackingRef.current.trackViews(questionsRef.current, [...timespansRef.current]);
|
|
32
|
+
timespansRef.current = [];
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
const setupObserver = useCallback((element) => {
|
|
36
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
37
|
+
if (entry.isIntersecting) {
|
|
38
|
+
entryTimeRef.current = new Date().toISOString();
|
|
39
|
+
if (questionsRef.current.length > 0) {
|
|
40
|
+
trackingRef.current.trackView(questionsRef.current);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (entryTimeRef.current) {
|
|
44
|
+
timespansRef.current.push({
|
|
45
|
+
start: entryTimeRef.current,
|
|
46
|
+
end: new Date().toISOString(),
|
|
47
|
+
});
|
|
48
|
+
entryTimeRef.current = null;
|
|
49
|
+
trackingRef.current.trackOutOfView();
|
|
50
|
+
}
|
|
51
|
+
}, { threshold: 0.5 });
|
|
52
|
+
observer.observe(element);
|
|
53
|
+
observerRef.current = observer;
|
|
54
|
+
intervalRef.current = setInterval(flush, FLUSH_INTERVAL_MS);
|
|
55
|
+
const handleVisibilityChange = () => {
|
|
56
|
+
if (document.visibilityState === 'hidden') {
|
|
57
|
+
flush();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
61
|
+
return () => {
|
|
62
|
+
observer.disconnect();
|
|
63
|
+
if (intervalRef.current)
|
|
64
|
+
clearInterval(intervalRef.current);
|
|
65
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
66
|
+
flush();
|
|
67
|
+
};
|
|
68
|
+
}, [flush]);
|
|
69
|
+
const cleanupRef = useRef(null);
|
|
70
|
+
const containerRef = useCallback((node) => {
|
|
71
|
+
if (cleanupRef.current) {
|
|
72
|
+
cleanupRef.current();
|
|
73
|
+
cleanupRef.current = null;
|
|
74
|
+
}
|
|
75
|
+
nodeRef.current = node;
|
|
76
|
+
if (node) {
|
|
77
|
+
cleanupRef.current = setupObserver(node);
|
|
78
|
+
}
|
|
79
|
+
}, [setupObserver]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
return () => {
|
|
82
|
+
if (cleanupRef.current) {
|
|
83
|
+
cleanupRef.current();
|
|
84
|
+
cleanupRef.current = null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}, []);
|
|
88
|
+
return { containerRef };
|
|
89
|
+
}
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '2.
|
|
1
|
+
export default '2.9.0';
|
|
@@ -7,6 +7,8 @@ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, Incl
|
|
|
7
7
|
apiKey: string;
|
|
8
8
|
/** The product item ID to fetch insights for. */
|
|
9
9
|
itemId: string;
|
|
10
|
+
/** The product display name, sent with tracking events. */
|
|
11
|
+
itemName: string;
|
|
10
12
|
/** Thread ID for conversation context. Must be a valid UUID (e.g., "550e8400-e29b-41d4-a716-446655440000"). */
|
|
11
13
|
threadId?: string;
|
|
12
14
|
/** Optional variation ID for the product. */
|
|
@@ -5,6 +5,9 @@ interface PiaCustomCarouselProps {
|
|
|
5
5
|
items: Array<Item>;
|
|
6
6
|
componentOverrides?: CarouselOverrides<Item>;
|
|
7
7
|
callbacks?: Callbacks;
|
|
8
|
+
onResultClick?: (item: Item, position: number, question: string, qnaResultId?: string) => void;
|
|
9
|
+
question?: string;
|
|
10
|
+
qnaResultId?: string;
|
|
8
11
|
}
|
|
9
|
-
export default function PiaCustomCarousel({ items, componentOverrides, callbacks, }: PiaCustomCarouselProps): React.JSX.Element | null;
|
|
12
|
+
export default function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, }: PiaCustomCarouselProps): React.JSX.Element | null;
|
|
10
13
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ConversationEntry, Translations, Item, Callbacks, CioPiaComponentOverrides, DisclaimerPosition } from '../../types';
|
|
2
|
+
import { ConversationEntry, FeedbackType, Translations, Item, Callbacks, CioPiaComponentOverrides, DisclaimerPosition } from '../../types';
|
|
3
3
|
export interface ConversationHistoryProps {
|
|
4
4
|
conversationHistory: ConversationEntry[];
|
|
5
5
|
isLoading: boolean;
|
|
@@ -22,5 +22,8 @@ export interface ConversationHistoryProps {
|
|
|
22
22
|
translations?: Translations;
|
|
23
23
|
callbacks?: Callbacks;
|
|
24
24
|
componentOverrides?: CioPiaComponentOverrides;
|
|
25
|
+
handleFeedback?: (type: FeedbackType) => void;
|
|
26
|
+
onResultClick?: (item: Item, position: number, question: string, qnaResultId?: string) => void;
|
|
27
|
+
qnaResultId?: string;
|
|
25
28
|
}
|
|
26
|
-
export default function ConversationHistory({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, }: ConversationHistoryProps): React.JSX.Element;
|
|
29
|
+
export default function ConversationHistory({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, handleFeedback, onResultClick, qnaResultId, }: ConversationHistoryProps): React.JSX.Element;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Question } from '../../types';
|
|
2
|
+
import { FeedbackType, Item, Question } from '../../types';
|
|
3
3
|
import { ConversationHistoryProps } from '../ConversationHistory/ConversationHistory';
|
|
4
4
|
export interface PiaConversationProps extends ConversationHistoryProps {
|
|
5
5
|
displayedQuestions: Question[];
|
|
6
6
|
handleSubmitQuestion: (question: string) => void;
|
|
7
7
|
handleQuestionClick: (question: string) => void;
|
|
8
8
|
containerRef?: (node: HTMLDivElement | null) => void;
|
|
9
|
+
handleFeedback?: (type: FeedbackType) => void;
|
|
10
|
+
onResultClick?: (item: Item, position: number, question: string, qnaResultId?: string) => void;
|
|
11
|
+
qnaResultId?: string;
|
|
9
12
|
onInputFocus?: () => void;
|
|
10
13
|
}
|
|
11
|
-
export default function PiaConversation({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, displayedQuestions, handleSubmitQuestion, handleQuestionClick, containerRef, onInputFocus, }: PiaConversationProps): React.JSX.Element;
|
|
14
|
+
export default function PiaConversation({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, displayedQuestions, handleSubmitQuestion, handleQuestionClick, containerRef, handleFeedback, onResultClick, qnaResultId, onInputFocus, }: PiaConversationProps): React.JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Callbacks, CioPiaComponentOverrides, DisclaimerPosition, Translations, Item } from '../../types';
|
|
2
|
+
import { Callbacks, CioPiaComponentOverrides, DisclaimerPosition, FeedbackType, Translations, Item } from '../../types';
|
|
3
3
|
interface PiaInlineAnswerProps {
|
|
4
4
|
currentAnswer: string;
|
|
5
5
|
currentItems: Item[] | null;
|
|
@@ -9,6 +9,10 @@ interface PiaInlineAnswerProps {
|
|
|
9
9
|
translations?: Translations;
|
|
10
10
|
callbacks?: Callbacks;
|
|
11
11
|
componentOverrides?: CioPiaComponentOverrides;
|
|
12
|
+
onFeedback?: (type: FeedbackType) => void;
|
|
13
|
+
onResultClick?: (item: Item, position: number, question: string, qnaResultId?: string) => void;
|
|
14
|
+
question?: string;
|
|
15
|
+
qnaResultId?: string;
|
|
12
16
|
}
|
|
13
|
-
export default function PiaInlineAnswer({ currentAnswer, currentItems, showFeedback, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, }: PiaInlineAnswerProps): React.JSX.Element;
|
|
17
|
+
export default function PiaInlineAnswer({ currentAnswer, currentItems, showFeedback, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, onFeedback, onResultClick, question, qnaResultId, }: PiaInlineAnswerProps): React.JSX.Element;
|
|
14
18
|
export {};
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const DEMO_API_KEY = "key_13TO6qN9H3BP3Wrn";
|
|
2
2
|
export declare const DEMO_ITEM_ID = "149100215";
|
|
3
|
+
export declare const DEMO_ITEM_NAME = "Ensure Original Nutrition Shake";
|
|
3
4
|
export declare const DEMO_QUESTION = "What is the nutritional content of this shake?";
|
|
4
5
|
export declare const DEMO_QUESTION_ALTERNATIVE_PRODUCTS = "What alternative products are available for this item?";
|
|
5
6
|
export declare const MOCK_QUESTIONS: {
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { ConstructorClientOptions, Nullable } from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import MockConstructorIOClient from './mocks/MockConstructorIOClient';
|
|
3
|
-
/**
|
|
4
|
-
* Uses MockConstructorIOClient for now, to be replaced with the actual Client JS object
|
|
5
|
-
*/
|
|
1
|
+
import ConstructorIOClient, { ConstructorClientOptions, Nullable } from '@constructor-io/constructorio-client-javascript';
|
|
6
2
|
export type UseCioClientProps = {
|
|
7
3
|
apiKey?: string;
|
|
8
|
-
cioClient?: Nullable<
|
|
4
|
+
cioClient?: Nullable<ConstructorIOClient>;
|
|
9
5
|
options?: Omit<ConstructorClientOptions, 'apiKey' | 'sendTrackingEvents' | 'version'>;
|
|
10
6
|
};
|
|
11
|
-
type UseCioClient = (props: UseCioClientProps) => Nullable<
|
|
7
|
+
type UseCioClient = (props: UseCioClientProps) => Nullable<ConstructorIOClient> | never;
|
|
12
8
|
declare const useCioClient: UseCioClient;
|
|
13
9
|
export default useCioClient;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Tracker } from '@constructor-io/constructorio-client-javascript/lib/types/constructorio';
|
|
1
2
|
import { Formatters, SuggestedQuestionsParameters } from '../types';
|
|
2
3
|
import MockConstructorIOClient from './mocks/MockConstructorIOClient';
|
|
3
4
|
import { UseAnswerResultsReturn } from './useAnswerResults';
|
|
@@ -13,6 +14,9 @@ export interface UseCioPiaProps {
|
|
|
13
14
|
formatImageUrl?: Formatters['formatImageUrl'];
|
|
14
15
|
}
|
|
15
16
|
export interface UseCioPiaReturn {
|
|
17
|
+
cioClient: {
|
|
18
|
+
tracker: Tracker;
|
|
19
|
+
};
|
|
16
20
|
threadId: string;
|
|
17
21
|
suggestedQuestions: UseSuggestedQuestionsReturn;
|
|
18
22
|
answers: UseAnswerResultsReturn;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Callbacks, ConversationEntry, PiaCallbackContext, Question, Item } from '../types';
|
|
1
|
+
import { Callbacks, ConversationEntry, FeedbackType, PiaCallbackContext, Question, Item } from '../types';
|
|
2
2
|
import { UseCioPiaReturn } from './useCioPia';
|
|
3
|
+
import { UseTrackingReturn } from './useTracking';
|
|
3
4
|
export interface UseConversationProps {
|
|
4
5
|
pia: UseCioPiaReturn;
|
|
5
6
|
itemId: string;
|
|
6
7
|
isConversation: boolean;
|
|
7
8
|
callbacks?: Callbacks;
|
|
9
|
+
tracking?: UseTrackingReturn;
|
|
8
10
|
}
|
|
9
11
|
export interface UseConversationReturn {
|
|
10
12
|
currentQuestion: string;
|
|
@@ -18,6 +20,7 @@ export interface UseConversationReturn {
|
|
|
18
20
|
handleSubmitQuestion: (question: string) => void;
|
|
19
21
|
handleQuestionClick: (question: string) => void;
|
|
20
22
|
handleInputFocus: () => void;
|
|
23
|
+
handleFeedback: (type: FeedbackType) => void;
|
|
21
24
|
resetState: () => void;
|
|
22
25
|
}
|
|
23
|
-
export default function useConversation({ pia, itemId, isConversation, callbacks, }: UseConversationProps): UseConversationReturn;
|
|
26
|
+
export default function useConversation({ pia, itemId, isConversation, callbacks, tracking, }: UseConversationProps): UseConversationReturn;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Tracker } from '@constructor-io/constructorio-client-javascript/lib/types/constructorio';
|
|
2
|
+
import { FeedbackType, Item, Question, GetAnswerResultsResponse } from '../types';
|
|
3
|
+
export interface TimeSpan {
|
|
4
|
+
start: string;
|
|
5
|
+
end: string;
|
|
6
|
+
}
|
|
7
|
+
export interface UseTrackingProps {
|
|
8
|
+
cioClient?: {
|
|
9
|
+
tracker: Tracker;
|
|
10
|
+
};
|
|
11
|
+
itemId: string;
|
|
12
|
+
itemName: string;
|
|
13
|
+
variationId?: string;
|
|
14
|
+
threadId?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UseTrackingReturn {
|
|
17
|
+
trackViews: (questions: Question[], viewTimespans: TimeSpan[]) => void;
|
|
18
|
+
trackView: (questions: Question[]) => void;
|
|
19
|
+
trackOutOfView: () => void;
|
|
20
|
+
trackFocus: () => void;
|
|
21
|
+
trackQuestionClick: (question: string) => void;
|
|
22
|
+
trackQuestionSubmit: (question: string) => void;
|
|
23
|
+
trackAnswerView: (question: string, answerData: GetAnswerResultsResponse, items?: Item[] | null) => void;
|
|
24
|
+
trackAnswerFeedback: (feedbackType: FeedbackType, qnaResultId?: string) => void;
|
|
25
|
+
trackResultClick: (clickedItem: Item, position: number, question: string, qnaResultId?: string) => void;
|
|
26
|
+
}
|
|
27
|
+
export default function useTracking({ cioClient, itemId, itemName, variationId, threadId, }: UseTrackingProps): UseTrackingReturn;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Question } from '../types';
|
|
2
|
+
import { UseTrackingReturn } from './useTracking';
|
|
3
|
+
export interface UseViewportTrackingProps {
|
|
4
|
+
tracking: UseTrackingReturn;
|
|
5
|
+
questions: Question[];
|
|
6
|
+
}
|
|
7
|
+
export interface UseViewportTrackingReturn {
|
|
8
|
+
containerRef: (node: HTMLDivElement | null) => void;
|
|
9
|
+
}
|
|
10
|
+
export default function useViewportTracking({ tracking, questions, }: UseViewportTrackingProps): UseViewportTrackingReturn;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Nullable } from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import { Formatters, Item
|
|
2
|
+
import { ApiItem, Formatters, Item } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Converts a raw ApiItem from the Get Answers API response into an Item object
|
|
5
5
|
* that can be used in the Carousel component.
|
package/lib/types/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "2.
|
|
1
|
+
declare const _default: "2.9.0";
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructor-io/constructorio-ui-pia",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Constructor.io AI Product Insights Agent UI library for web applications",
|
|
5
5
|
"author": "Constructor.io Corporation",
|
|
6
6
|
"license": "MIT",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"prepare": "husky"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@constructor-io/constructorio-client-javascript": "^2.
|
|
71
|
+
"@constructor-io/constructorio-client-javascript": "^2.89.0",
|
|
72
72
|
"@constructor-io/constructorio-ui-components": "^1.5.0",
|
|
73
73
|
"react": ">=16.12.0",
|
|
74
74
|
"react-dom": ">=16.12.0",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"storybook": "^8.6.4",
|
|
124
124
|
"typescript": "^4.9.4",
|
|
125
125
|
"typescript-eslint": "^8.59.2",
|
|
126
|
-
"vite": "^6.3
|
|
126
|
+
"vite": "^6.4.3",
|
|
127
127
|
"vite-plugin-css-injected-by-js": "^3.5.2",
|
|
128
128
|
"webpack": "^5.98.0"
|
|
129
129
|
},
|
|
@@ -134,5 +134,8 @@
|
|
|
134
134
|
"dependencies": {
|
|
135
135
|
"dompurify": "^3.4.2",
|
|
136
136
|
"marked": "^15.0.12"
|
|
137
|
+
},
|
|
138
|
+
"overrides": {
|
|
139
|
+
"uuid": "^11.1.1"
|
|
137
140
|
}
|
|
138
141
|
}
|