@constructor-io/constructorio-ui-pia 2.11.0 → 2.13.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 +62 -62
- package/lib/cjs/components/CioPia/CioPiaQna.js +10 -4
- package/lib/cjs/components/CioPia/CioPiaStyles.css +5 -0
- package/lib/cjs/components/CioPia/PiaCustomCarousel.js +34 -29
- package/lib/cjs/components/ConversationHistory/ConversationHistory.js +10 -5
- package/lib/cjs/components/Error/ErrorBlock.js +7 -5
- package/lib/cjs/components/Feedback/Feedback.js +4 -4
- package/lib/cjs/components/Input/Input.js +2 -1
- package/lib/cjs/components/LoadingSkeleton/LoadingSkeleton.js +2 -1
- package/lib/cjs/components/PiaConversation/PiaModal.js +22 -4
- package/lib/cjs/components/StatusRegion/StatusRegion.js +34 -0
- package/lib/cjs/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.js +1 -1
- package/lib/cjs/components/icons/QuestionIcon.js +1 -1
- package/lib/cjs/components/icons/SendIcon.js +1 -1
- package/lib/cjs/components/icons/SparklesIcon.js +1 -1
- package/lib/cjs/hooks/useAnswerResults.js +4 -14
- package/lib/cjs/hooks/useCioPia.js +42 -4
- package/lib/cjs/hooks/useConversation.js +10 -8
- package/lib/cjs/hooks/usePiaClient.js +2 -7
- package/lib/cjs/hooks/useRecsPod.js +10 -5
- package/lib/cjs/hooks/useSuggestedQuestions.js +10 -26
- package/lib/cjs/types.js +0 -2
- package/lib/cjs/utils/translate.js +12 -0
- package/lib/cjs/version.js +1 -1
- package/lib/mjs/components/CioPia/CioPiaQna.js +10 -4
- package/lib/mjs/components/CioPia/CioPiaStyles.css +5 -0
- package/lib/mjs/components/CioPia/PiaCustomCarousel.js +53 -30
- package/lib/mjs/components/ConversationHistory/ConversationHistory.js +10 -5
- package/lib/mjs/components/Error/ErrorBlock.js +7 -5
- package/lib/mjs/components/Feedback/Feedback.js +4 -4
- package/lib/mjs/components/Input/Input.js +2 -1
- package/lib/mjs/components/LoadingSkeleton/LoadingSkeleton.js +2 -1
- package/lib/mjs/components/PiaConversation/PiaModal.js +22 -4
- package/lib/mjs/components/StatusRegion/StatusRegion.js +28 -0
- package/lib/mjs/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.js +1 -1
- package/lib/mjs/components/icons/QuestionIcon.js +1 -1
- package/lib/mjs/components/icons/SendIcon.js +1 -1
- package/lib/mjs/components/icons/SparklesIcon.js +1 -1
- package/lib/mjs/hooks/useAnswerResults.js +4 -13
- package/lib/mjs/hooks/useCioPia.js +43 -4
- package/lib/mjs/hooks/useConversation.js +7 -5
- package/lib/mjs/hooks/usePiaClient.js +2 -7
- package/lib/mjs/hooks/useRecsPod.js +9 -5
- package/lib/mjs/hooks/useSuggestedQuestions.js +12 -24
- package/lib/mjs/types.js +0 -1
- package/lib/mjs/utils/translate.js +12 -0
- package/lib/mjs/version.js +1 -1
- package/lib/types/components/CioPia/types.d.ts +20 -5
- package/lib/types/components/ConversationHistory/ConversationHistory.d.ts +7 -1
- package/lib/types/components/Error/ErrorBlock.d.ts +9 -1
- package/lib/types/components/StatusRegion/StatusRegion.d.ts +13 -0
- package/lib/types/hooks/useAnswerResults.d.ts +4 -4
- package/lib/types/hooks/useCioPia.d.ts +20 -5
- package/lib/types/hooks/usePiaClient.d.ts +4 -9
- package/lib/types/hooks/useRecsPod.d.ts +2 -2
- package/lib/types/hooks/useSuggestedQuestions.d.ts +3 -4
- package/lib/types/types.d.ts +53 -6
- package/lib/types/utils/translate.d.ts +3 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +3 -3
- package/lib/cjs/hooks/mocks/MockConstructorIOClient.js +0 -24
- package/lib/cjs/hooks/mocks/agent.js +0 -183
- package/lib/cjs/hooks/mocks/types.js +0 -2
- package/lib/mjs/hooks/mocks/MockConstructorIOClient.js +0 -38
- package/lib/mjs/hooks/mocks/agent.js +0 -173
- package/lib/mjs/hooks/mocks/types.js +0 -1
- package/lib/types/hooks/mocks/MockConstructorIOClient.d.ts +0 -14
- package/lib/types/hooks/mocks/agent.d.ts +0 -23
- package/lib/types/hooks/mocks/types.d.ts +0 -77
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { translate } from '../../utils/translate';
|
|
3
|
+
// Inline so it stays hidden for consumers who do not load the stylesheet.
|
|
4
|
+
const SR_ONLY_STYLE = {
|
|
5
|
+
position: 'absolute',
|
|
6
|
+
width: 1,
|
|
7
|
+
height: 1,
|
|
8
|
+
padding: 0,
|
|
9
|
+
margin: -1,
|
|
10
|
+
overflow: 'hidden',
|
|
11
|
+
clip: 'rect(0, 0, 0, 0)',
|
|
12
|
+
whiteSpace: 'nowrap',
|
|
13
|
+
border: 0,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Visually hidden `role='status'`. Keep it mounted and change only `message`:
|
|
17
|
+
* a live region created together with its text is announced inconsistently.
|
|
18
|
+
*/
|
|
19
|
+
export default function StatusRegion({ message, 'data-testid': testId }) {
|
|
20
|
+
return (React.createElement("div", { className: 'cio-pia-sr-only', style: SR_ONLY_STYLE, role: 'status', "data-testid": testId }, message));
|
|
21
|
+
}
|
|
22
|
+
export function answerStatusMessage(isLoading, hasAnswer, translations) {
|
|
23
|
+
if (isLoading)
|
|
24
|
+
return translate('Loading answer', translations);
|
|
25
|
+
if (hasAnswer)
|
|
26
|
+
return translate('Answer ready', translations);
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export default function SuggestedQuestionsSkeleton() {
|
|
3
|
-
return (React.createElement("div", { className: 'cio-pia-suggested-questions-container', "data-testid": 'suggested-questions-skeleton', "aria-
|
|
3
|
+
return (React.createElement("div", { className: 'cio-pia-suggested-questions-container', "data-testid": 'suggested-questions-skeleton', "aria-hidden": 'true' },
|
|
4
4
|
React.createElement("div", { className: 'cio-pia-suggested-question-skeleton' }),
|
|
5
5
|
React.createElement("div", { className: 'cio-pia-suggested-question-skeleton' }),
|
|
6
6
|
React.createElement("div", { className: 'cio-pia-suggested-question-skeleton' })));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export default function QuestionIcon() {
|
|
3
|
-
return (React.createElement("svg", { "aria-hidden": 'true', width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
3
|
+
return (React.createElement("svg", { "aria-hidden": 'true', focusable: 'false', width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
4
4
|
React.createElement("path", { d: 'M8.00004 1.33331C4.32004 1.33331 1.33337 4.31998 1.33337 7.99998C1.33337 11.68 4.32004 14.6666 8.00004 14.6666C11.68 14.6666 14.6667 11.68 14.6667 7.99998C14.6667 4.31998 11.68 1.33331 8.00004 1.33331ZM8.66671 12.6666H7.33337V11.3333H8.66671V12.6666ZM10.0467 7.49998L9.44671 8.11331C8.96671 8.59998 8.66671 8.99998 8.66671 9.99998H7.33337V9.66665C7.33337 8.93331 7.63337 8.26665 8.11337 7.77998L8.94004 6.93998C9.18671 6.69998 9.33337 6.36665 9.33337 5.99998C9.33337 5.26665 8.73337 4.66665 8.00004 4.66665C7.26671 4.66665 6.66671 5.26665 6.66671 5.99998H5.33337C5.33337 4.52665 6.52671 3.33331 8.00004 3.33331C9.47337 3.33331 10.6667 4.52665 10.6667 5.99998C10.6667 6.58665 10.4267 7.11998 10.0467 7.49998Z', fill: '#0F1324', fillOpacity: '0.6' })));
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export default function SendIcon() {
|
|
3
|
-
return (React.createElement("svg", { "aria-hidden": 'true', width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
3
|
+
return (React.createElement("svg", { "aria-hidden": 'true', focusable: 'false', width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
4
4
|
React.createElement("path", { d: 'M14.8934 7.09533L14.8884 7.09314L1.54219 1.55752C1.42993 1.51053 1.30778 1.49211 1.18666 1.50388C1.06554 1.51566 0.949225 1.55728 0.848125 1.62502C0.741311 1.69501 0.653573 1.79045 0.592805 1.90277C0.532037 2.01509 0.500145 2.14075 0.5 2.26845V5.80877C0.50006 5.98335 0.561019 6.15243 0.67237 6.28689C0.783722 6.42134 0.938491 6.51274 1.11 6.54533L8.38906 7.89127C8.41767 7.89669 8.44348 7.91193 8.46205 7.93435C8.48062 7.95677 8.49078 7.98497 8.49078 8.01408C8.49078 8.04319 8.48062 8.07139 8.46205 8.09381C8.44348 8.11623 8.41767 8.13147 8.38906 8.13689L1.11031 9.48283C0.938851 9.51533 0.784092 9.60661 0.67269 9.74094C0.561288 9.87528 0.500219 10.0443 0.5 10.2188V13.7597C0.499917 13.8816 0.530111 14.0017 0.587871 14.1091C0.645632 14.2165 0.729152 14.3079 0.830938 14.375C0.953375 14.4564 1.09706 14.4998 1.24406 14.5C1.34626 14.4999 1.4474 14.4794 1.54156 14.4397L14.8875 8.93564L14.8934 8.93283C15.0731 8.85563 15.2262 8.72745 15.3337 8.56415C15.4413 8.40086 15.4986 8.20961 15.4986 8.01408C15.4986 7.81855 15.4413 7.6273 15.3337 7.46401C15.2262 7.30071 15.0731 7.17253 14.8934 7.09533Z', fill: 'white' })));
|
|
5
5
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export default function SparklesIcon() {
|
|
3
|
-
return (React.createElement("svg", { "aria-hidden": 'true', width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
3
|
+
return (React.createElement("svg", { "aria-hidden": 'true', focusable: 'false', width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
|
|
4
4
|
React.createElement("path", { d: 'M12.6667 6.0013L13.5001 4.16797L15.3334 3.33464L13.5001 2.5013L12.6667 0.667969L11.8334 2.5013L10.0001 3.33464L11.8334 4.16797L12.6667 6.0013Z', fill: '#1C1917' }),
|
|
5
5
|
React.createElement("path", { d: 'M12.6667 10.0013L11.8334 11.8346L10.0001 12.668L11.8334 13.5013L12.6667 15.3346L13.5001 13.5013L15.3334 12.668L13.5001 11.8346L12.6667 10.0013Z', fill: '#1C1917' }),
|
|
6
6
|
React.createElement("path", { d: 'M7.66675 6.33464L6.00008 2.66797L4.33341 6.33464L0.666748 8.0013L4.33341 9.66797L6.00008 13.3346L7.66675 9.66797L11.3334 8.0013L7.66675 6.33464ZM6.66008 8.6613L6.00008 10.1146L5.34008 8.6613L3.88675 8.0013L5.34008 7.3413L6.00008 5.88797L6.66008 7.3413L8.11341 8.0013L6.66008 8.6613Z', fill: '#1C1917' })));
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import { extractAndTransformItems } from '../utils/transformers';
|
|
3
|
-
const fetchAnswerResults = async ({ client, itemId, question, variationId, threadId, parameters, }) => {
|
|
4
|
-
const response = await client.agent.getAnswerResults({
|
|
5
|
-
itemId,
|
|
6
|
-
variationId,
|
|
7
|
-
threadId,
|
|
8
|
-
question,
|
|
9
|
-
parameters,
|
|
10
|
-
});
|
|
11
|
-
return response;
|
|
12
|
-
};
|
|
13
3
|
export default function useAnswerResults({ itemId, variationId, threadId, cioClient, parameters, formatImageUrl, }) {
|
|
14
4
|
const [answerResults, setAnswerResults] = useState(null);
|
|
15
5
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -21,9 +11,10 @@ export default function useAnswerResults({ itemId, variationId, threadId, cioCli
|
|
|
21
11
|
setIsLoading(true);
|
|
22
12
|
setError(null);
|
|
23
13
|
setAnswerResults(null);
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
|
|
14
|
+
cioClient.agent.pia
|
|
15
|
+
.getAnswerResults(itemId, question, { threadId, variationId, ...parameters })
|
|
16
|
+
.then((response) => {
|
|
17
|
+
setAnswerResults(response);
|
|
27
18
|
setError(null);
|
|
28
19
|
})
|
|
29
20
|
.catch((err) => {
|
|
@@ -1,27 +1,66 @@
|
|
|
1
1
|
import useAnswerResults from './useAnswerResults';
|
|
2
2
|
import usePiaClient from './usePiaClient';
|
|
3
3
|
import useSuggestedQuestions from './useSuggestedQuestions';
|
|
4
|
+
// TODO: remove SUPPORTED_DEPRECATED_PARAMS and mapDeprecatedParameters once the
|
|
5
|
+
// deprecated `parameters` prop is removed from CioPiaProps.
|
|
6
|
+
const SUPPORTED_DEPRECATED_PARAMS = {
|
|
7
|
+
guard: { camelKey: 'guard', isObject: false },
|
|
8
|
+
num_results: { camelKey: 'numResults', isObject: false },
|
|
9
|
+
numResults: { camelKey: 'numResults', isObject: false },
|
|
10
|
+
pre_filter_expression: { camelKey: 'preFilterExpression', isObject: true },
|
|
11
|
+
preFilterExpression: { camelKey: 'preFilterExpression', isObject: true },
|
|
12
|
+
fmt_options: { camelKey: 'fmtOptions', isObject: true },
|
|
13
|
+
fmtOptions: { camelKey: 'fmtOptions', isObject: true },
|
|
14
|
+
};
|
|
15
|
+
function mapDeprecatedParameters(params) {
|
|
16
|
+
if (!params)
|
|
17
|
+
return undefined;
|
|
18
|
+
const mapped = {};
|
|
19
|
+
let hasEntries = false;
|
|
20
|
+
for (const [key, value] of Object.entries(params)) {
|
|
21
|
+
const config = SUPPORTED_DEPRECATED_PARAMS[key];
|
|
22
|
+
if (!config)
|
|
23
|
+
continue;
|
|
24
|
+
hasEntries = true;
|
|
25
|
+
if (config.isObject && typeof value === 'string') {
|
|
26
|
+
try {
|
|
27
|
+
mapped[config.camelKey] = JSON.parse(value);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
mapped[config.camelKey] = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
mapped[config.camelKey] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return hasEntries ? mapped : undefined;
|
|
38
|
+
}
|
|
4
39
|
export default function useCioPia(props) {
|
|
5
|
-
const { apiKey, itemId, variationId, threadId: providedThreadId, cioClient: providedClient, suggestedQuestionsParameters, parameters, formatImageUrl, } = props;
|
|
40
|
+
const { apiKey, itemId, variationId, threadId: providedThreadId, cioClient: providedClient, suggestedQuestionsParameters, answerParameters, parameters, formatImageUrl, } = props;
|
|
6
41
|
const { cioClient: client, threadId } = usePiaClient({
|
|
7
42
|
apiKey,
|
|
8
43
|
threadId: providedThreadId,
|
|
9
44
|
cioClient: providedClient,
|
|
10
45
|
});
|
|
46
|
+
const mappedDeprecated = mapDeprecatedParameters(parameters);
|
|
47
|
+
const mergedSuggestedParams = mappedDeprecated || suggestedQuestionsParameters
|
|
48
|
+
? { ...mappedDeprecated, ...suggestedQuestionsParameters }
|
|
49
|
+
: undefined;
|
|
50
|
+
const mergedAnswerParams = mappedDeprecated || answerParameters ? { ...mappedDeprecated, ...answerParameters } : undefined;
|
|
11
51
|
const suggestedQuestions = useSuggestedQuestions({
|
|
12
52
|
itemId,
|
|
13
53
|
variationId,
|
|
14
54
|
threadId,
|
|
15
55
|
cioClient: client,
|
|
16
|
-
parameters:
|
|
17
|
-
requestParameters: parameters,
|
|
56
|
+
parameters: mergedSuggestedParams,
|
|
18
57
|
});
|
|
19
58
|
const answers = useAnswerResults({
|
|
20
59
|
itemId,
|
|
21
60
|
variationId,
|
|
22
61
|
threadId,
|
|
23
62
|
cioClient: client,
|
|
24
|
-
parameters,
|
|
63
|
+
parameters: mergedAnswerParams,
|
|
25
64
|
formatImageUrl,
|
|
26
65
|
});
|
|
27
66
|
return {
|
|
@@ -7,7 +7,7 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
7
7
|
const [displayedQuestions, setDisplayedQuestions] = useState([]);
|
|
8
8
|
const [conversationHistory, setConversationHistory] = useState([]);
|
|
9
9
|
const entryIdRef = useRef(0);
|
|
10
|
-
const
|
|
10
|
+
const prevAnswerDataRef = useRef(answers.data);
|
|
11
11
|
const hasTrackedCurrentAnswerRef = useRef(false);
|
|
12
12
|
const answersRef = useRef(answers);
|
|
13
13
|
const callbacksRef = useRef(callbacks);
|
|
@@ -61,13 +61,13 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
61
61
|
setCurrentQuestion('');
|
|
62
62
|
setDisplayedQuestions(suggestedQuestions.data);
|
|
63
63
|
setConversationHistory([]);
|
|
64
|
-
|
|
64
|
+
prevAnswerDataRef.current = null;
|
|
65
65
|
}, [suggestedQuestions.data]);
|
|
66
66
|
useEffect(() => {
|
|
67
67
|
setCurrentQuestion('');
|
|
68
68
|
setDisplayedQuestions([]);
|
|
69
69
|
setConversationHistory([]);
|
|
70
|
-
|
|
70
|
+
prevAnswerDataRef.current = null;
|
|
71
71
|
}, [itemId]);
|
|
72
72
|
useEffect(() => {
|
|
73
73
|
setDisplayedQuestions(suggestedQuestions.data);
|
|
@@ -84,9 +84,11 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
|
|
|
84
84
|
const answerValue = answers.data?.value ?? '';
|
|
85
85
|
if (!answerValue)
|
|
86
86
|
return;
|
|
87
|
-
|
|
87
|
+
// Compare by response object, not by text: two consecutive answers with
|
|
88
|
+
// identical text are still two answers and both must reach the history.
|
|
89
|
+
if (answers.data === prevAnswerDataRef.current)
|
|
88
90
|
return;
|
|
89
|
-
|
|
91
|
+
prevAnswerDataRef.current = answers.data;
|
|
90
92
|
const answerThreadId = answers.data?.thread_id;
|
|
91
93
|
const qnaResultId = answers.data?.qna_result_id;
|
|
92
94
|
if (isConversation) {
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
3
3
|
import version from '../version';
|
|
4
|
-
/**
|
|
5
|
-
* Resolves the client and thread ID every mode needs: the caller's client when they supplied
|
|
6
|
-
* one, otherwise a client built from the API key, plus a generated thread ID that stays stable
|
|
7
|
-
* for the life of the component.
|
|
8
|
-
*/
|
|
9
4
|
export default function usePiaClient({ apiKey, threadId: providedThreadId, cioClient: providedClient, }) {
|
|
10
5
|
const [generatedThreadId] = useState(() => crypto.randomUUID());
|
|
11
6
|
const threadId = providedThreadId || generatedThreadId;
|
|
12
7
|
const client = useMemo(() => {
|
|
13
8
|
if (providedClient)
|
|
14
9
|
return providedClient;
|
|
15
|
-
return new
|
|
10
|
+
return new ConstructorIOClient({
|
|
16
11
|
apiKey,
|
|
17
12
|
sendTrackingEvents: true,
|
|
18
13
|
version: `cio-ui-pia-${version}`,
|
|
@@ -30,9 +30,13 @@ export default function useRecsPod({ itemId, variationId, threadId, cioClient, p
|
|
|
30
30
|
const strategy = parameters?.strategy || DEFAULT_STRATEGY;
|
|
31
31
|
const { numResults, defaultTitle } = parameters || {};
|
|
32
32
|
const fetchResult = useCallback((shopperInput, source) => {
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
33
|
+
// TODO: once getRecs is added to the JS client SDK, replace this guard+cast
|
|
34
|
+
// with a direct call: cioClient.agent.getRecs({...})
|
|
35
|
+
const getRecs = cioClient?.agent?.getRecs;
|
|
36
|
+
if (!cioClient || !getRecs) {
|
|
37
|
+
if (cioClient && !getRecs) {
|
|
38
|
+
console.info('[CioPia] getRecs is not available on the client SDK yet.');
|
|
39
|
+
}
|
|
36
40
|
setIsLoading(false);
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
@@ -42,8 +46,8 @@ export default function useRecsPod({ itemId, variationId, threadId, cioClient, p
|
|
|
42
46
|
setIsLoading(true);
|
|
43
47
|
setError(null);
|
|
44
48
|
setHasUnsupportedInput(false);
|
|
45
|
-
|
|
46
|
-
.
|
|
49
|
+
getRecs
|
|
50
|
+
.call(cioClient.agent, {
|
|
47
51
|
itemId,
|
|
48
52
|
variationId,
|
|
49
53
|
threadId,
|
|
@@ -1,35 +1,22 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
const response = await client.agent.getSuggestedQuestions({
|
|
4
|
-
itemId,
|
|
5
|
-
variationId,
|
|
6
|
-
threadId,
|
|
7
|
-
parameters,
|
|
8
|
-
requestParameters,
|
|
9
|
-
});
|
|
10
|
-
return response.questions;
|
|
11
|
-
};
|
|
12
|
-
export default function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, requestParameters, }) {
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
export default function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, }) {
|
|
13
3
|
const [questions, setQuestions] = useState([]);
|
|
14
4
|
const [isLoading, setIsLoading] = useState(false);
|
|
15
5
|
const [error, setError] = useState(null);
|
|
16
|
-
|
|
17
|
-
const requestParametersKey = JSON.stringify(requestParameters);
|
|
6
|
+
const serializedParameters = useMemo(() => JSON.stringify(parameters), [parameters]);
|
|
18
7
|
const fetchResult = useCallback(() => {
|
|
19
8
|
if (!cioClient)
|
|
20
9
|
return;
|
|
21
10
|
setIsLoading(true);
|
|
22
11
|
setError(null);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
itemId,
|
|
26
|
-
variationId,
|
|
12
|
+
cioClient.agent.pia
|
|
13
|
+
.getSuggestedQuestions(itemId, {
|
|
27
14
|
threadId,
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
variationId,
|
|
16
|
+
...parameters,
|
|
30
17
|
})
|
|
31
|
-
.then((
|
|
32
|
-
setQuestions(
|
|
18
|
+
.then((response) => {
|
|
19
|
+
setQuestions(response.questions);
|
|
33
20
|
setError(null);
|
|
34
21
|
})
|
|
35
22
|
.catch((err) => {
|
|
@@ -38,8 +25,9 @@ export default function useSuggestedQuestions({ itemId, variationId, threadId, c
|
|
|
38
25
|
.finally(() => {
|
|
39
26
|
setIsLoading(false);
|
|
40
27
|
});
|
|
41
|
-
//
|
|
42
|
-
|
|
28
|
+
// parameters is serialized via serializedParameters to prevent refetch on identity changes
|
|
29
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
30
|
+
}, [cioClient, itemId, variationId, threadId, serializedParameters]);
|
|
43
31
|
useEffect(() => {
|
|
44
32
|
fetchResult();
|
|
45
33
|
}, [fetchResult]);
|
package/lib/mjs/types.js
CHANGED
|
@@ -2,12 +2,21 @@ import { DISCLAIMER_TEXT, RECS_FALLBACK_TITLE, RECS_INPUT_PLACEHOLDER, RECS_LOAD
|
|
|
2
2
|
const defaultTranslations = {
|
|
3
3
|
'Any questions about this product?': 'Any questions about this product?',
|
|
4
4
|
'Ask anything': 'Ask anything',
|
|
5
|
+
'Your question': 'Your question',
|
|
5
6
|
Send: 'Send',
|
|
6
7
|
[DISCLAIMER_TEXT]: DISCLAIMER_TEXT,
|
|
7
8
|
'Is this answer useful?': 'Is this answer useful?',
|
|
8
9
|
'Learn More.': 'Learn More.',
|
|
9
10
|
'Ask about this product': 'Ask about this product',
|
|
10
11
|
'Add to Cart': 'Add to Cart',
|
|
12
|
+
'Unexpected error': 'Unexpected error',
|
|
13
|
+
'thumbs up': 'thumbs up',
|
|
14
|
+
'thumbs down': 'thumbs down',
|
|
15
|
+
Close: 'Close',
|
|
16
|
+
Retry: 'Retry',
|
|
17
|
+
'Conversation history': 'Conversation history',
|
|
18
|
+
'Loading answer': 'Loading answer',
|
|
19
|
+
'Answer ready': 'Answer ready',
|
|
11
20
|
[RECS_LOADING_TITLE]: RECS_LOADING_TITLE,
|
|
12
21
|
[RECS_FALLBACK_TITLE]: RECS_FALLBACK_TITLE,
|
|
13
22
|
[RECS_UNSUPPORTED_REQUEST]: RECS_UNSUPPORTED_REQUEST,
|
|
@@ -18,6 +27,9 @@ const defaultTranslations = {
|
|
|
18
27
|
* Translates a word using the provided translations object.
|
|
19
28
|
* Falls back to English defaults if translation is not provided.
|
|
20
29
|
*
|
|
30
|
+
* An explicitly empty string is returned as-is: blanking out a string is how
|
|
31
|
+
* consumers hide it.
|
|
32
|
+
*
|
|
21
33
|
* @param word - The key to translate
|
|
22
34
|
* @param translations - Optional user-provided translations object
|
|
23
35
|
* @returns The translated string or the original word if no translation exists
|
package/lib/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '2.
|
|
1
|
+
export default '2.13.0';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IncludeComponentOverrides, IncludeRenderProps } from '@constructor-io/constructorio-ui-components';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { CioPiaRenderProps, CioPiaComponentOverrides, Callbacks, CioPiaDisplayConfigs, CioPiaTrackingConfigs, Translations, SuggestedQuestionsParameters, AnswerRequestParameters, RecsPodParameters, Formatters, ProductCardDisplayProps } from '../../types';
|
|
3
|
+
import type { CioClient } from '../../hooks/usePiaClient';
|
|
4
4
|
export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, IncludeComponentOverrides<CioPiaComponentOverrides> {
|
|
5
5
|
/** Your Constructor.io API key. */
|
|
6
6
|
apiKey: string;
|
|
@@ -13,7 +13,7 @@ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, Incl
|
|
|
13
13
|
/** Optional variation ID for the product. */
|
|
14
14
|
variationId?: string;
|
|
15
15
|
/** Optional Constructor.io client instance. If not provided, one will be created internally. */
|
|
16
|
-
cioClient?:
|
|
16
|
+
cioClient?: CioClient;
|
|
17
17
|
/** Display configuration options (mode, type, showFeedback, etc.). */
|
|
18
18
|
displayConfigs?: CioPiaDisplayConfigs;
|
|
19
19
|
/** Tracking configuration options (viewThreshold, etc.). */
|
|
@@ -30,11 +30,26 @@ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, Incl
|
|
|
30
30
|
translations?: Translations;
|
|
31
31
|
/** Parameters for the suggested questions request. */
|
|
32
32
|
suggestedQuestionsParameters?: SuggestedQuestionsParameters;
|
|
33
|
+
/** Parameters for the answer request. */
|
|
34
|
+
answerParameters?: AnswerRequestParameters;
|
|
33
35
|
/** Parameters for the recommendations request, used by `mode: 'recommendations'`. */
|
|
34
36
|
recsPodParameters?: RecsPodParameters;
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
+
* @deprecated Use `answerParameters` and `suggestedQuestionsParameters` instead.
|
|
39
|
+
*
|
|
40
|
+
* Only the following keys are forwarded (snake_case or camelCase accepted):
|
|
41
|
+
*
|
|
42
|
+
* Suggested-questions endpoint:
|
|
43
|
+
* - `num_results` / `numResults` → numResults
|
|
44
|
+
* - `pre_filter_expression` / `preFilterExpression` → preFilterExpression (JSON-parsed if string)
|
|
45
|
+
*
|
|
46
|
+
* Answer endpoint:
|
|
47
|
+
* - `guard` → guard
|
|
48
|
+
* - `pre_filter_expression` / `preFilterExpression` → preFilterExpression (JSON-parsed if string)
|
|
49
|
+
* - `fmt_options` / `fmtOptions` → fmtOptions (JSON-parsed if string)
|
|
50
|
+
*
|
|
51
|
+
* All other keys (e.g. `ef-*`) are silently dropped.
|
|
52
|
+
* Typed parameters take precedence over values specified here.
|
|
38
53
|
*/
|
|
39
54
|
parameters?: Record<string, string | number | boolean>;
|
|
40
55
|
}
|
|
@@ -3,6 +3,12 @@ import { ConversationEntry, FeedbackType, Translations, Item, Callbacks, CioPiaC
|
|
|
3
3
|
export interface ConversationHistoryProps {
|
|
4
4
|
conversationHistory: ConversationEntry[];
|
|
5
5
|
isLoading: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Whether the answer request itself is in flight. `isLoading` also covers the follow-up
|
|
8
|
+
* questions request, which is fine for the visible skeleton but would make the status region
|
|
9
|
+
* announce "Loading answer" while only the questions are loading. Defaults to `isLoading`.
|
|
10
|
+
*/
|
|
11
|
+
isAnswerLoading?: boolean;
|
|
6
12
|
error: Error | null;
|
|
7
13
|
/**
|
|
8
14
|
* Items for the latest conversation entry's carousel.
|
|
@@ -27,4 +33,4 @@ export interface ConversationHistoryProps {
|
|
|
27
33
|
qnaResultId?: string;
|
|
28
34
|
priceCurrency?: string;
|
|
29
35
|
}
|
|
30
|
-
export default function ConversationHistory({ conversationHistory, isLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, handleFeedback, onResultClick, qnaResultId, priceCurrency, }: ConversationHistoryProps): React.JSX.Element;
|
|
36
|
+
export default function ConversationHistory({ conversationHistory, isLoading, isAnswerLoading, error, currentItems, showFeedback, showPreviousItems, learnMoreUrl, disclaimerPosition, translations, callbacks, componentOverrides, handleFeedback, onResultClick, qnaResultId, priceCurrency, }: ConversationHistoryProps): React.JSX.Element;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Translations } from '../../types';
|
|
2
3
|
interface ErrorBlockProps {
|
|
3
4
|
message: string;
|
|
4
5
|
onRetry?: () => void;
|
|
6
|
+
translations?: Translations;
|
|
7
|
+
/**
|
|
8
|
+
* Whether the message is a `role='alert'` live region. Turn off when the block is rendered
|
|
9
|
+
* inside another live region (such as the conversation `role='log'`), which already announces
|
|
10
|
+
* inserted text: nesting the two would announce the error twice. Defaults to true.
|
|
11
|
+
*/
|
|
12
|
+
announce?: boolean;
|
|
5
13
|
}
|
|
6
|
-
declare function ErrorBlock({ message, onRetry }: ErrorBlockProps): React.JSX.Element;
|
|
14
|
+
declare function ErrorBlock({ message, onRetry, translations, announce, }: ErrorBlockProps): React.JSX.Element;
|
|
7
15
|
export default ErrorBlock;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Translations } from '../../types';
|
|
3
|
+
interface StatusRegionProps {
|
|
4
|
+
message: string;
|
|
5
|
+
'data-testid'?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Visually hidden `role='status'`. Keep it mounted and change only `message`:
|
|
9
|
+
* a live region created together with its text is announced inconsistently.
|
|
10
|
+
*/
|
|
11
|
+
export default function StatusRegion({ message, 'data-testid': testId }: StatusRegionProps): React.JSX.Element;
|
|
12
|
+
export declare function answerStatusMessage(isLoading: boolean, hasAnswer: boolean, translations?: Translations): string;
|
|
13
|
+
export {};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Nullable } from '@constructor-io/constructorio-client-javascript';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { AnswerRequestParameters, Formatters, Item, GetAnswerResultsResponse } from '../types';
|
|
3
|
+
import type { CioClient } from './usePiaClient';
|
|
4
4
|
export interface UseAnswerResultsProps {
|
|
5
5
|
itemId: string;
|
|
6
6
|
variationId?: string;
|
|
7
7
|
threadId?: string;
|
|
8
|
-
cioClient:
|
|
9
|
-
parameters?:
|
|
8
|
+
cioClient: CioClient;
|
|
9
|
+
parameters?: AnswerRequestParameters;
|
|
10
10
|
formatImageUrl?: Formatters['formatImageUrl'];
|
|
11
11
|
}
|
|
12
12
|
export interface UseAnswerResultsReturn {
|
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
import { Tracker } from '@constructor-io/constructorio-client-javascript/lib/types/constructorio';
|
|
2
|
-
import { Formatters, SuggestedQuestionsParameters } from '../types';
|
|
3
|
-
import MockConstructorIOClient from './mocks/MockConstructorIOClient';
|
|
2
|
+
import { AnswerRequestParameters, Formatters, SuggestedQuestionsParameters } from '../types';
|
|
4
3
|
import { UseAnswerResultsReturn } from './useAnswerResults';
|
|
4
|
+
import { CioClient } from './usePiaClient';
|
|
5
5
|
import { UseSuggestedQuestionsReturn } from './useSuggestedQuestions';
|
|
6
|
+
export type { CioClient };
|
|
6
7
|
export interface UseCioPiaProps {
|
|
7
8
|
apiKey: string;
|
|
8
9
|
itemId: string;
|
|
9
10
|
variationId?: string;
|
|
10
11
|
threadId?: string;
|
|
11
|
-
cioClient?:
|
|
12
|
+
cioClient?: CioClient;
|
|
12
13
|
suggestedQuestionsParameters?: SuggestedQuestionsParameters;
|
|
14
|
+
answerParameters?: AnswerRequestParameters;
|
|
13
15
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* @deprecated Use `answerParameters` and `suggestedQuestionsParameters` instead.
|
|
17
|
+
*
|
|
18
|
+
* Only the following keys are forwarded (snake_case or camelCase accepted):
|
|
19
|
+
*
|
|
20
|
+
* Suggested-questions endpoint:
|
|
21
|
+
* - `num_results` / `numResults` → numResults
|
|
22
|
+
* - `pre_filter_expression` / `preFilterExpression` → preFilterExpression (JSON-parsed if string)
|
|
23
|
+
*
|
|
24
|
+
* Answer endpoint:
|
|
25
|
+
* - `guard` → guard
|
|
26
|
+
* - `pre_filter_expression` / `preFilterExpression` → preFilterExpression (JSON-parsed if string)
|
|
27
|
+
* - `fmt_options` / `fmtOptions` → fmtOptions (JSON-parsed if string)
|
|
28
|
+
*
|
|
29
|
+
* All other keys (e.g. `ef-*`) are silently dropped.
|
|
30
|
+
* Typed parameters take precedence over values specified here.
|
|
16
31
|
*/
|
|
17
32
|
parameters?: Record<string, string | number | boolean>;
|
|
18
33
|
/** Define outside the component or wrap with useCallback to avoid unnecessary re-renders. */
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ConstructorIOClient from '@constructor-io/constructorio-client-javascript';
|
|
2
|
+
export type CioClient = InstanceType<typeof ConstructorIOClient>;
|
|
2
3
|
export interface UsePiaClientProps {
|
|
3
4
|
apiKey: string;
|
|
4
|
-
/** Thread ID for conversation context. Must be a valid UUID. One is generated when absent. */
|
|
5
5
|
threadId?: string;
|
|
6
|
-
cioClient?:
|
|
6
|
+
cioClient?: CioClient;
|
|
7
7
|
}
|
|
8
8
|
export interface UsePiaClientReturn {
|
|
9
|
-
cioClient:
|
|
9
|
+
cioClient: CioClient;
|
|
10
10
|
threadId: string;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Resolves the client and thread ID every mode needs: the caller's client when they supplied
|
|
14
|
-
* one, otherwise a client built from the API key, plus a generated thread ID that stays stable
|
|
15
|
-
* for the life of the component.
|
|
16
|
-
*/
|
|
17
12
|
export default function usePiaClient({ apiKey, threadId: providedThreadId, cioClient: providedClient, }: UsePiaClientProps): UsePiaClientReturn;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { CioClient } from './usePiaClient';
|
|
2
2
|
import { Formatters, Item, RecsPodParameters, RecsRefinement, Translations } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* What the shopper did to ask for this refinement.
|
|
@@ -12,7 +12,7 @@ export interface UseRecsPodProps {
|
|
|
12
12
|
itemId: string;
|
|
13
13
|
variationId?: string;
|
|
14
14
|
threadId?: string;
|
|
15
|
-
cioClient?:
|
|
15
|
+
cioClient?: CioClient;
|
|
16
16
|
parameters?: RecsPodParameters;
|
|
17
17
|
formatImageUrl?: Formatters['formatImageUrl'];
|
|
18
18
|
translations?: Translations;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Question, SuggestedQuestionsParameters } from '../types';
|
|
2
|
-
import
|
|
2
|
+
import type { CioClient } from './usePiaClient';
|
|
3
3
|
export interface UseSuggestedQuestionsProps {
|
|
4
4
|
itemId: string;
|
|
5
5
|
variationId?: string;
|
|
6
6
|
threadId?: string;
|
|
7
|
-
cioClient?:
|
|
7
|
+
cioClient?: CioClient;
|
|
8
8
|
parameters?: SuggestedQuestionsParameters;
|
|
9
|
-
requestParameters?: Record<string, string | number | boolean>;
|
|
10
9
|
}
|
|
11
10
|
export interface UseSuggestedQuestionsReturn {
|
|
12
11
|
data: Array<Question>;
|
|
@@ -14,4 +13,4 @@ export interface UseSuggestedQuestionsReturn {
|
|
|
14
13
|
error: Error | null;
|
|
15
14
|
getSuggestedQuestions: () => void;
|
|
16
15
|
}
|
|
17
|
-
export default function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters,
|
|
16
|
+
export default function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, }: UseSuggestedQuestionsProps): UseSuggestedQuestionsReturn;
|