@constructor-io/constructorio-ui-pia 2.11.0 → 2.12.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 +60 -60
- package/lib/cjs/components/CioPia/CioPiaQna.js +2 -1
- package/lib/cjs/components/CioPia/PiaCustomCarousel.js +24 -29
- package/lib/cjs/hooks/useAnswerResults.js +4 -14
- package/lib/cjs/hooks/useCioPia.js +42 -4
- 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/version.js +1 -1
- package/lib/mjs/components/CioPia/CioPiaQna.js +2 -1
- package/lib/mjs/components/CioPia/PiaCustomCarousel.js +43 -30
- package/lib/mjs/hooks/useAnswerResults.js +4 -13
- package/lib/mjs/hooks/useCioPia.js +43 -4
- 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/version.js +1 -1
- package/lib/types/components/CioPia/types.d.ts +20 -5
- 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 +43 -6
- package/lib/types/version.d.ts +1 -1
- package/package.json +2 -2
- 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
|
@@ -21,7 +21,7 @@ const PiaConversation_1 = tslib_1.__importDefault(require("../PiaConversation/Pi
|
|
|
21
21
|
// eslint-disable-next-line complexity
|
|
22
22
|
function CioPiaQna(props) {
|
|
23
23
|
var _a;
|
|
24
|
-
const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, parameters, trackingConfigs, } = props;
|
|
24
|
+
const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, answerParameters, parameters, trackingConfigs, } = props;
|
|
25
25
|
const { learnMoreUrl, showFeedback, mode = 'default', type = 'inline', showPreviousItems, disclaimerPosition = 'bottom', } = displayConfigs || {};
|
|
26
26
|
const isConversation = mode === 'conversation' || type === 'modal';
|
|
27
27
|
const { priceCurrency } = productCardProps || {};
|
|
@@ -32,6 +32,7 @@ function CioPiaQna(props) {
|
|
|
32
32
|
variationId,
|
|
33
33
|
cioClient,
|
|
34
34
|
suggestedQuestionsParameters,
|
|
35
|
+
answerParameters,
|
|
35
36
|
parameters,
|
|
36
37
|
formatImageUrl: formatters === null || formatters === void 0 ? void 0 : formatters.formatImageUrl,
|
|
37
38
|
});
|
|
@@ -46,12 +46,10 @@ function buildMergedOverrides(componentOverrides, priceSectionOverride) {
|
|
|
46
46
|
return Object.assign(Object.assign({}, componentOverrides), { item: Object.assign(Object.assign({}, componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.item), { productCard: Object.assign(Object.assign({}, (_c = componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.item) === null || _c === void 0 ? void 0 : _c.productCard), { content: Object.assign(Object.assign({}, content), defaults) }) }) });
|
|
47
47
|
}
|
|
48
48
|
function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, translations, priceCurrency, }) {
|
|
49
|
-
const wrapperRef = (0, react_1.useRef)(null);
|
|
50
49
|
const { onAddToCart } = callbacks || {};
|
|
51
50
|
const addToCartHandler = (0, react_1.useCallback)((event, product) => {
|
|
52
51
|
onAddToCart === null || onAddToCart === void 0 ? void 0 : onAddToCart(product, event);
|
|
53
52
|
}, [onAddToCart]);
|
|
54
|
-
// Determine to use user-defined click handler or default behavior
|
|
55
53
|
const productClickHandler = (0, react_1.useCallback)((item) => {
|
|
56
54
|
const position = items.findIndex((i) => i.id === item.id);
|
|
57
55
|
if (onResultClick && question) {
|
|
@@ -65,40 +63,37 @@ function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick
|
|
|
65
63
|
window.open(item.url, '_blank', 'noopener,noreferrer');
|
|
66
64
|
}
|
|
67
65
|
}, [callbacks, items, onResultClick, question, qnaResultId]);
|
|
68
|
-
// Set up event listener for product card clicks
|
|
69
|
-
(0, react_1.useEffect)(() => {
|
|
70
|
-
const el = wrapperRef.current;
|
|
71
|
-
if (!el)
|
|
72
|
-
return undefined;
|
|
73
|
-
const handleClick = (e) => {
|
|
74
|
-
const { product } = e.detail;
|
|
75
|
-
if (product) {
|
|
76
|
-
productClickHandler(product);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
el.addEventListener(constructorio_ui_components_1.CIO_EVENTS.productCard.click, handleClick);
|
|
80
|
-
return () => {
|
|
81
|
-
el.removeEventListener(constructorio_ui_components_1.CIO_EVENTS.productCard.click, handleClick);
|
|
82
|
-
};
|
|
83
|
-
}, [productClickHandler]);
|
|
84
66
|
const priceSectionOverride = (0, react_1.useMemo)(() => (priceCurrency ? createPriceSectionOverride(priceCurrency) : undefined), [priceCurrency]);
|
|
67
|
+
const clickHandlerProps = (0, react_1.useMemo)(() => ({
|
|
68
|
+
onProductClick: productClickHandler,
|
|
69
|
+
onAddToCart: onAddToCart ? addToCartHandler : undefined,
|
|
70
|
+
}), [productClickHandler, onAddToCart, addToCartHandler]);
|
|
85
71
|
const mergedOverrides = (0, react_1.useMemo)(() => {
|
|
86
|
-
var _a;
|
|
72
|
+
var _a, _b;
|
|
87
73
|
const baseOverrides = buildMergedOverrides(componentOverrides, priceSectionOverride);
|
|
74
|
+
// Override priority: carousel.reactNode → item.reactNode → item.productCard.reactNode → default ProductCard.
|
|
75
|
+
// The casts are required because the override signatures don't declare these extra props,
|
|
76
|
+
// but consumers receive them as part of the render-props contract documented in CioPiaProps.
|
|
77
|
+
const carouselOverride = baseOverrides.reactNode;
|
|
78
|
+
if (typeof carouselOverride === 'function') {
|
|
79
|
+
return Object.assign(Object.assign({}, baseOverrides), { reactNode: (props) => carouselOverride(Object.assign(Object.assign({}, props), clickHandlerProps)) });
|
|
80
|
+
}
|
|
88
81
|
const cardOverrides = (_a = baseOverrides.item) === null || _a === void 0 ? void 0 : _a.productCard;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
const itemOverride = (_b = baseOverrides.item) === null || _b === void 0 ? void 0 : _b.reactNode;
|
|
83
|
+
if (typeof itemOverride === 'function') {
|
|
84
|
+
return Object.assign(Object.assign({}, baseOverrides), { item: Object.assign(Object.assign({}, baseOverrides.item), { reactNode: (props) => itemOverride(Object.assign(Object.assign({}, props), clickHandlerProps)) }) });
|
|
85
|
+
}
|
|
86
|
+
if (typeof (cardOverrides === null || cardOverrides === void 0 ? void 0 : cardOverrides.reactNode) === 'function') {
|
|
87
|
+
const originalReactNode = cardOverrides.reactNode;
|
|
88
|
+
return Object.assign(Object.assign({}, baseOverrides), { item: Object.assign(Object.assign({}, baseOverrides.item), { productCard: Object.assign(Object.assign({}, cardOverrides), { reactNode: (props) => originalReactNode(Object.assign(Object.assign({}, props), clickHandlerProps)) }) }) });
|
|
93
89
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
90
|
+
// Default: render ProductCard with handlers directly
|
|
91
|
+
const addToCartText = onAddToCart ? (0, translate_1.translate)('Add to Cart', translations) : undefined;
|
|
92
|
+
return Object.assign(Object.assign({}, baseOverrides), { item: Object.assign(Object.assign({}, baseOverrides.item), { productCard: Object.assign(Object.assign({}, cardOverrides), { reactNode: ({ product }) => (react_1.default.createElement(constructorio_ui_components_1.ProductCard, { product: product, className: 'w-full h-full', addToCartText: addToCartText, onAddToCart: clickHandlerProps.onAddToCart, onProductClick: clickHandlerProps.onProductClick, componentOverrides: cardOverrides })) }) }) });
|
|
93
|
+
}, [componentOverrides, priceSectionOverride, clickHandlerProps, onAddToCart, translations]);
|
|
98
94
|
if (items.length === 0) {
|
|
99
95
|
return null;
|
|
100
96
|
}
|
|
101
|
-
return
|
|
102
|
-
react_1.default.createElement(constructorio_ui_components_1.Carousel, { items: items, componentOverrides: mergedOverrides })));
|
|
97
|
+
return react_1.default.createElement(constructorio_ui_components_1.Carousel, { items: items, componentOverrides: mergedOverrides });
|
|
103
98
|
}
|
|
104
99
|
exports.default = PiaCustomCarousel;
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
3
|
const react_1 = require("react");
|
|
5
4
|
const transformers_1 = require("../utils/transformers");
|
|
6
|
-
const fetchAnswerResults = ({ client, itemId, question, variationId, threadId, parameters, }) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
7
|
-
const response = yield client.agent.getAnswerResults({
|
|
8
|
-
itemId,
|
|
9
|
-
variationId,
|
|
10
|
-
threadId,
|
|
11
|
-
question,
|
|
12
|
-
parameters,
|
|
13
|
-
});
|
|
14
|
-
return response;
|
|
15
|
-
});
|
|
16
5
|
function useAnswerResults({ itemId, variationId, threadId, cioClient, parameters, formatImageUrl, }) {
|
|
17
6
|
const [answerResults, setAnswerResults] = (0, react_1.useState)(null);
|
|
18
7
|
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
@@ -24,9 +13,10 @@ function useAnswerResults({ itemId, variationId, threadId, cioClient, parameters
|
|
|
24
13
|
setIsLoading(true);
|
|
25
14
|
setError(null);
|
|
26
15
|
setAnswerResults(null);
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
16
|
+
cioClient.agent.pia
|
|
17
|
+
.getAnswerResults(itemId, question, Object.assign({ threadId, variationId }, parameters))
|
|
18
|
+
.then((response) => {
|
|
19
|
+
setAnswerResults(response);
|
|
30
20
|
setError(null);
|
|
31
21
|
})
|
|
32
22
|
.catch((err) => {
|
|
@@ -4,27 +4,65 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const useAnswerResults_1 = tslib_1.__importDefault(require("./useAnswerResults"));
|
|
5
5
|
const usePiaClient_1 = tslib_1.__importDefault(require("./usePiaClient"));
|
|
6
6
|
const useSuggestedQuestions_1 = tslib_1.__importDefault(require("./useSuggestedQuestions"));
|
|
7
|
+
// TODO: remove SUPPORTED_DEPRECATED_PARAMS and mapDeprecatedParameters once the
|
|
8
|
+
// deprecated `parameters` prop is removed from CioPiaProps.
|
|
9
|
+
const SUPPORTED_DEPRECATED_PARAMS = {
|
|
10
|
+
guard: { camelKey: 'guard', isObject: false },
|
|
11
|
+
num_results: { camelKey: 'numResults', isObject: false },
|
|
12
|
+
numResults: { camelKey: 'numResults', isObject: false },
|
|
13
|
+
pre_filter_expression: { camelKey: 'preFilterExpression', isObject: true },
|
|
14
|
+
preFilterExpression: { camelKey: 'preFilterExpression', isObject: true },
|
|
15
|
+
fmt_options: { camelKey: 'fmtOptions', isObject: true },
|
|
16
|
+
fmtOptions: { camelKey: 'fmtOptions', isObject: true },
|
|
17
|
+
};
|
|
18
|
+
function mapDeprecatedParameters(params) {
|
|
19
|
+
if (!params)
|
|
20
|
+
return undefined;
|
|
21
|
+
const mapped = {};
|
|
22
|
+
let hasEntries = false;
|
|
23
|
+
for (const [key, value] of Object.entries(params)) {
|
|
24
|
+
const config = SUPPORTED_DEPRECATED_PARAMS[key];
|
|
25
|
+
if (!config)
|
|
26
|
+
continue;
|
|
27
|
+
hasEntries = true;
|
|
28
|
+
if (config.isObject && typeof value === 'string') {
|
|
29
|
+
try {
|
|
30
|
+
mapped[config.camelKey] = JSON.parse(value);
|
|
31
|
+
}
|
|
32
|
+
catch (_a) {
|
|
33
|
+
mapped[config.camelKey] = value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
mapped[config.camelKey] = value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return hasEntries ? mapped : undefined;
|
|
41
|
+
}
|
|
7
42
|
function useCioPia(props) {
|
|
8
|
-
const { apiKey, itemId, variationId, threadId: providedThreadId, cioClient: providedClient, suggestedQuestionsParameters, parameters, formatImageUrl, } = props;
|
|
43
|
+
const { apiKey, itemId, variationId, threadId: providedThreadId, cioClient: providedClient, suggestedQuestionsParameters, answerParameters, parameters, formatImageUrl, } = props;
|
|
9
44
|
const { cioClient: client, threadId } = (0, usePiaClient_1.default)({
|
|
10
45
|
apiKey,
|
|
11
46
|
threadId: providedThreadId,
|
|
12
47
|
cioClient: providedClient,
|
|
13
48
|
});
|
|
49
|
+
const mappedDeprecated = mapDeprecatedParameters(parameters);
|
|
50
|
+
const mergedSuggestedParams = mappedDeprecated || suggestedQuestionsParameters
|
|
51
|
+
? Object.assign(Object.assign({}, mappedDeprecated), suggestedQuestionsParameters) : undefined;
|
|
52
|
+
const mergedAnswerParams = mappedDeprecated || answerParameters ? Object.assign(Object.assign({}, mappedDeprecated), answerParameters) : undefined;
|
|
14
53
|
const suggestedQuestions = (0, useSuggestedQuestions_1.default)({
|
|
15
54
|
itemId,
|
|
16
55
|
variationId,
|
|
17
56
|
threadId,
|
|
18
57
|
cioClient: client,
|
|
19
|
-
parameters:
|
|
20
|
-
requestParameters: parameters,
|
|
58
|
+
parameters: mergedSuggestedParams,
|
|
21
59
|
});
|
|
22
60
|
const answers = (0, useAnswerResults_1.default)({
|
|
23
61
|
itemId,
|
|
24
62
|
variationId,
|
|
25
63
|
threadId,
|
|
26
64
|
cioClient: client,
|
|
27
|
-
parameters,
|
|
65
|
+
parameters: mergedAnswerParams,
|
|
28
66
|
formatImageUrl,
|
|
29
67
|
});
|
|
30
68
|
return {
|
|
@@ -2,20 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const react_1 = require("react");
|
|
5
|
-
const
|
|
5
|
+
const constructorio_client_javascript_1 = tslib_1.__importDefault(require("@constructor-io/constructorio-client-javascript"));
|
|
6
6
|
const version_1 = tslib_1.__importDefault(require("../version"));
|
|
7
|
-
/**
|
|
8
|
-
* Resolves the client and thread ID every mode needs: the caller's client when they supplied
|
|
9
|
-
* one, otherwise a client built from the API key, plus a generated thread ID that stays stable
|
|
10
|
-
* for the life of the component.
|
|
11
|
-
*/
|
|
12
7
|
function usePiaClient({ apiKey, threadId: providedThreadId, cioClient: providedClient, }) {
|
|
13
8
|
const [generatedThreadId] = (0, react_1.useState)(() => crypto.randomUUID());
|
|
14
9
|
const threadId = providedThreadId || generatedThreadId;
|
|
15
10
|
const client = (0, react_1.useMemo)(() => {
|
|
16
11
|
if (providedClient)
|
|
17
12
|
return providedClient;
|
|
18
|
-
return new
|
|
13
|
+
return new constructorio_client_javascript_1.default({
|
|
19
14
|
apiKey,
|
|
20
15
|
sendTrackingEvents: true,
|
|
21
16
|
version: `cio-ui-pia-${version_1.default}`,
|
|
@@ -33,9 +33,14 @@ function useRecsPod({ itemId, variationId, threadId, cioClient, parameters, form
|
|
|
33
33
|
const strategy = (parameters === null || parameters === void 0 ? void 0 : parameters.strategy) || DEFAULT_STRATEGY;
|
|
34
34
|
const { numResults, defaultTitle } = parameters || {};
|
|
35
35
|
const fetchResult = (0, react_1.useCallback)((shopperInput, source) => {
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
36
|
+
var _a;
|
|
37
|
+
// TODO: once getRecs is added to the JS client SDK, replace this guard+cast
|
|
38
|
+
// with a direct call: cioClient.agent.getRecs({...})
|
|
39
|
+
const getRecs = (_a = cioClient === null || cioClient === void 0 ? void 0 : cioClient.agent) === null || _a === void 0 ? void 0 : _a.getRecs;
|
|
40
|
+
if (!cioClient || !getRecs) {
|
|
41
|
+
if (cioClient && !getRecs) {
|
|
42
|
+
console.info('[CioPia] getRecs is not available on the client SDK yet.');
|
|
43
|
+
}
|
|
39
44
|
setIsLoading(false);
|
|
40
45
|
return;
|
|
41
46
|
}
|
|
@@ -45,8 +50,8 @@ function useRecsPod({ itemId, variationId, threadId, cioClient, parameters, form
|
|
|
45
50
|
setIsLoading(true);
|
|
46
51
|
setError(null);
|
|
47
52
|
setHasUnsupportedInput(false);
|
|
48
|
-
|
|
49
|
-
.
|
|
53
|
+
getRecs
|
|
54
|
+
.call(cioClient.agent, {
|
|
50
55
|
itemId,
|
|
51
56
|
variationId,
|
|
52
57
|
threadId,
|
|
@@ -1,38 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
3
|
const react_1 = require("react");
|
|
5
|
-
|
|
6
|
-
const response = yield client.agent.getSuggestedQuestions({
|
|
7
|
-
itemId,
|
|
8
|
-
variationId,
|
|
9
|
-
threadId,
|
|
10
|
-
parameters,
|
|
11
|
-
requestParameters,
|
|
12
|
-
});
|
|
13
|
-
return response.questions;
|
|
14
|
-
});
|
|
15
|
-
function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, requestParameters, }) {
|
|
4
|
+
function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, }) {
|
|
16
5
|
const [questions, setQuestions] = (0, react_1.useState)([]);
|
|
17
6
|
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
18
7
|
const [error, setError] = (0, react_1.useState)(null);
|
|
19
|
-
|
|
20
|
-
const requestParametersKey = JSON.stringify(requestParameters);
|
|
8
|
+
const serializedParameters = (0, react_1.useMemo)(() => JSON.stringify(parameters), [parameters]);
|
|
21
9
|
const fetchResult = (0, react_1.useCallback)(() => {
|
|
22
10
|
if (!cioClient)
|
|
23
11
|
return;
|
|
24
12
|
setIsLoading(true);
|
|
25
13
|
setError(null);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
parameters,
|
|
32
|
-
requestParameters,
|
|
33
|
-
})
|
|
34
|
-
.then((fetchedQuestions) => {
|
|
35
|
-
setQuestions(fetchedQuestions);
|
|
14
|
+
cioClient.agent.pia
|
|
15
|
+
.getSuggestedQuestions(itemId, Object.assign({ threadId,
|
|
16
|
+
variationId }, parameters))
|
|
17
|
+
.then((response) => {
|
|
18
|
+
setQuestions(response.questions);
|
|
36
19
|
setError(null);
|
|
37
20
|
})
|
|
38
21
|
.catch((err) => {
|
|
@@ -41,8 +24,9 @@ function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, param
|
|
|
41
24
|
.finally(() => {
|
|
42
25
|
setIsLoading(false);
|
|
43
26
|
});
|
|
44
|
-
//
|
|
45
|
-
|
|
27
|
+
// parameters is serialized via serializedParameters to prevent refetch on identity changes
|
|
28
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
29
|
+
}, [cioClient, itemId, variationId, threadId, serializedParameters]);
|
|
46
30
|
(0, react_1.useEffect)(() => {
|
|
47
31
|
fetchResult();
|
|
48
32
|
}, [fetchResult]);
|
package/lib/cjs/types.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FeedbackType = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
4
|
var FeedbackType;
|
|
6
5
|
(function (FeedbackType) {
|
|
7
6
|
FeedbackType["UP"] = "up";
|
|
8
7
|
FeedbackType["DOWN"] = "down";
|
|
9
8
|
})(FeedbackType = exports.FeedbackType || (exports.FeedbackType = {}));
|
|
10
|
-
tslib_1.__exportStar(require("./hooks/mocks/types"), exports);
|
package/lib/cjs/version.js
CHANGED
|
@@ -17,7 +17,7 @@ import PiaConversation from '../PiaConversation/PiaConversation';
|
|
|
17
17
|
/** The question-and-answer experience: `mode: 'default'`, `mode: 'conversation'`, and the modal. */
|
|
18
18
|
// eslint-disable-next-line complexity
|
|
19
19
|
export default function CioPiaQna(props) {
|
|
20
|
-
const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, parameters, trackingConfigs, } = props;
|
|
20
|
+
const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, answerParameters, parameters, trackingConfigs, } = props;
|
|
21
21
|
const { learnMoreUrl, showFeedback, mode = 'default', type = 'inline', showPreviousItems, disclaimerPosition = 'bottom', } = displayConfigs || {};
|
|
22
22
|
const isConversation = mode === 'conversation' || type === 'modal';
|
|
23
23
|
const { priceCurrency } = productCardProps || {};
|
|
@@ -28,6 +28,7 @@ export default function CioPiaQna(props) {
|
|
|
28
28
|
variationId,
|
|
29
29
|
cioClient,
|
|
30
30
|
suggestedQuestionsParameters,
|
|
31
|
+
answerParameters,
|
|
31
32
|
parameters,
|
|
32
33
|
formatImageUrl: formatters?.formatImageUrl,
|
|
33
34
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useCallback,
|
|
2
|
-
import { Carousel, ProductCard,
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
|
+
import { Carousel, ProductCard, } from '@constructor-io/constructorio-ui-components';
|
|
3
3
|
import { sanitizeHtml } from '../../utils/contentTransformers';
|
|
4
4
|
import { translate } from '../../utils/translate';
|
|
5
5
|
function HtmlDescription({ product }) {
|
|
@@ -55,12 +55,10 @@ function buildMergedOverrides(componentOverrides, priceSectionOverride) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
export default function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, translations, priceCurrency, }) {
|
|
58
|
-
const wrapperRef = useRef(null);
|
|
59
58
|
const { onAddToCart } = callbacks || {};
|
|
60
59
|
const addToCartHandler = useCallback((event, product) => {
|
|
61
60
|
onAddToCart?.(product, event);
|
|
62
61
|
}, [onAddToCart]);
|
|
63
|
-
// Determine to use user-defined click handler or default behavior
|
|
64
62
|
const productClickHandler = useCallback((item) => {
|
|
65
63
|
const position = items.findIndex((i) => i.id === item.id);
|
|
66
64
|
if (onResultClick && question) {
|
|
@@ -74,47 +72,62 @@ export default function PiaCustomCarousel({ items, componentOverrides, callbacks
|
|
|
74
72
|
window.open(item.url, '_blank', 'noopener,noreferrer');
|
|
75
73
|
}
|
|
76
74
|
}, [callbacks, items, onResultClick, question, qnaResultId]);
|
|
77
|
-
// Set up event listener for product card clicks
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
const el = wrapperRef.current;
|
|
80
|
-
if (!el)
|
|
81
|
-
return undefined;
|
|
82
|
-
const handleClick = (e) => {
|
|
83
|
-
const { product } = e.detail;
|
|
84
|
-
if (product) {
|
|
85
|
-
productClickHandler(product);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
el.addEventListener(CIO_EVENTS.productCard.click, handleClick);
|
|
89
|
-
return () => {
|
|
90
|
-
el.removeEventListener(CIO_EVENTS.productCard.click, handleClick);
|
|
91
|
-
};
|
|
92
|
-
}, [productClickHandler]);
|
|
93
75
|
const priceSectionOverride = useMemo(() => (priceCurrency ? createPriceSectionOverride(priceCurrency) : undefined), [priceCurrency]);
|
|
76
|
+
const clickHandlerProps = useMemo(() => ({
|
|
77
|
+
onProductClick: productClickHandler,
|
|
78
|
+
onAddToCart: onAddToCart ? addToCartHandler : undefined,
|
|
79
|
+
}), [productClickHandler, onAddToCart, addToCartHandler]);
|
|
94
80
|
const mergedOverrides = useMemo(() => {
|
|
95
81
|
const baseOverrides = buildMergedOverrides(componentOverrides, priceSectionOverride);
|
|
82
|
+
// Override priority: carousel.reactNode → item.reactNode → item.productCard.reactNode → default ProductCard.
|
|
83
|
+
// The casts are required because the override signatures don't declare these extra props,
|
|
84
|
+
// but consumers receive them as part of the render-props contract documented in CioPiaProps.
|
|
85
|
+
const carouselOverride = baseOverrides.reactNode;
|
|
86
|
+
if (typeof carouselOverride === 'function') {
|
|
87
|
+
return {
|
|
88
|
+
...baseOverrides,
|
|
89
|
+
reactNode: (props) => carouselOverride({ ...props, ...clickHandlerProps }),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
96
92
|
const cardOverrides = baseOverrides.item?.productCard;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
const itemOverride = baseOverrides.item?.reactNode;
|
|
94
|
+
if (typeof itemOverride === 'function') {
|
|
95
|
+
return {
|
|
96
|
+
...baseOverrides,
|
|
97
|
+
item: {
|
|
98
|
+
...baseOverrides.item,
|
|
99
|
+
reactNode: (props) => itemOverride({ ...props, ...clickHandlerProps }),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (typeof cardOverrides?.reactNode === 'function') {
|
|
104
|
+
const originalReactNode = cardOverrides.reactNode;
|
|
105
|
+
return {
|
|
106
|
+
...baseOverrides,
|
|
107
|
+
item: {
|
|
108
|
+
...baseOverrides.item,
|
|
109
|
+
productCard: {
|
|
110
|
+
...cardOverrides,
|
|
111
|
+
reactNode: (props) => originalReactNode({ ...props, ...clickHandlerProps }),
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
101
115
|
}
|
|
102
|
-
|
|
116
|
+
// Default: render ProductCard with handlers directly
|
|
117
|
+
const addToCartText = onAddToCart ? translate('Add to Cart', translations) : undefined;
|
|
103
118
|
return {
|
|
104
119
|
...baseOverrides,
|
|
105
120
|
item: {
|
|
106
121
|
...baseOverrides.item,
|
|
107
122
|
productCard: {
|
|
108
123
|
...cardOverrides,
|
|
109
|
-
reactNode: ({ product }) => (React.createElement(ProductCard, { product: product, className: 'w-full h-full', addToCartText: addToCartText, onAddToCart:
|
|
124
|
+
reactNode: ({ product }) => (React.createElement(ProductCard, { product: product, className: 'w-full h-full', addToCartText: addToCartText, onAddToCart: clickHandlerProps.onAddToCart, onProductClick: clickHandlerProps.onProductClick, componentOverrides: cardOverrides })),
|
|
110
125
|
},
|
|
111
126
|
},
|
|
112
127
|
};
|
|
113
|
-
}, [componentOverrides, priceSectionOverride, onAddToCart, translations
|
|
114
|
-
// If there are no items, do not render the carousel
|
|
128
|
+
}, [componentOverrides, priceSectionOverride, clickHandlerProps, onAddToCart, translations]);
|
|
115
129
|
if (items.length === 0) {
|
|
116
130
|
return null;
|
|
117
131
|
}
|
|
118
|
-
return
|
|
119
|
-
React.createElement(Carousel, { items: items, componentOverrides: mergedOverrides })));
|
|
132
|
+
return React.createElement(Carousel, { items: items, componentOverrides: mergedOverrides });
|
|
120
133
|
}
|
|
@@ -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 {
|
|
@@ -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,
|