@constructor-io/constructorio-ui-pia 2.10.0 → 2.10.1

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.
@@ -4,6 +4,7 @@ const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
5
  const constructorio_ui_components_1 = require("@constructor-io/constructorio-ui-components");
6
6
  const contentTransformers_1 = require("../../utils/contentTransformers");
7
+ const translate_1 = require("../../utils/translate");
7
8
  function HtmlDescription({ product }) {
8
9
  const { description } = product;
9
10
  if (!description)
@@ -44,8 +45,12 @@ function buildMergedOverrides(componentOverrides, priceSectionOverride) {
44
45
  }
45
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) }) }) });
46
47
  }
47
- function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, priceCurrency, }) {
48
+ function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, translations, priceCurrency, }) {
48
49
  const wrapperRef = (0, react_1.useRef)(null);
50
+ const { onAddToCart } = callbacks || {};
51
+ const addToCartHandler = (0, react_1.useCallback)((event, product) => {
52
+ onAddToCart === null || onAddToCart === void 0 ? void 0 : onAddToCart(product, event);
53
+ }, [onAddToCart]);
49
54
  // Determine to use user-defined click handler or default behavior
50
55
  const productClickHandler = (0, react_1.useCallback)((item) => {
51
56
  const position = items.findIndex((i) => i.id === item.id);
@@ -77,7 +82,18 @@ function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick
77
82
  };
78
83
  }, [productClickHandler]);
79
84
  const priceSectionOverride = (0, react_1.useMemo)(() => (priceCurrency ? createPriceSectionOverride(priceCurrency) : undefined), [priceCurrency]);
80
- const mergedOverrides = (0, react_1.useMemo)(() => buildMergedOverrides(componentOverrides, priceSectionOverride), [componentOverrides, priceSectionOverride]);
85
+ const mergedOverrides = (0, react_1.useMemo)(() => {
86
+ var _a;
87
+ const baseOverrides = buildMergedOverrides(componentOverrides, priceSectionOverride);
88
+ const cardOverrides = (_a = baseOverrides.item) === null || _a === void 0 ? void 0 : _a.productCard;
89
+ // The carousel never passes onAddToCart down and the card hides the button without it, so
90
+ // re-render the card with the handler. A full card override owns its own layout - skip it.
91
+ if (!onAddToCart || (cardOverrides === null || cardOverrides === void 0 ? void 0 : cardOverrides.reactNode)) {
92
+ return baseOverrides;
93
+ }
94
+ const addToCartText = (0, translate_1.translate)('Add to Cart', translations);
95
+ 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: addToCartHandler, componentOverrides: cardOverrides })) }) }) });
96
+ }, [componentOverrides, priceSectionOverride, onAddToCart, translations, addToCartHandler]);
81
97
  // If there are no items, do not render the carousel
82
98
  if (items.length === 0) {
83
99
  return null;
@@ -34,7 +34,7 @@ function ConversationHistory({ conversationHistory, isLoading, error, currentIte
34
34
  isLast && !isLoading && error && (react_1.default.createElement(ErrorBlock_1.default, { message: error.message || 'Unexpected error' })),
35
35
  entry.answer && (react_1.default.createElement("div", { className: 'cio-pia-answer-container' },
36
36
  react_1.default.createElement(Answer_1.default, { text: entry.answer, componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.answer }),
37
- carouselItems && (react_1.default.createElement(PiaCustomCarousel_1.default, { items: carouselItems, componentOverrides: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.carousel, callbacks: callbacks, onResultClick: onResultClick, question: entry.question, qnaResultId: qnaResultId, priceCurrency: priceCurrency })),
37
+ carouselItems && (react_1.default.createElement(PiaCustomCarousel_1.default, { items: carouselItems, componentOverrides: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.carousel, callbacks: callbacks, onResultClick: onResultClick, question: entry.question, qnaResultId: qnaResultId, translations: translations, priceCurrency: priceCurrency })),
38
38
  isLast && showFeedback && (react_1.default.createElement(Feedback_1.default, { translations: translations, onFeedback: handleFeedback, componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.feedback }))))));
39
39
  })),
40
40
  disclaimerPosition === 'bottom' && disclaimer));
@@ -11,7 +11,7 @@ function PiaInlineAnswer({ currentAnswer, currentItems, showFeedback, learnMoreU
11
11
  return (react_1.default.createElement("div", { className: 'cio-pia-answer-container' },
12
12
  disclaimerPosition === 'top' && disclaimer,
13
13
  react_1.default.createElement(Answer_1.default, { text: currentAnswer, componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.answer }),
14
- currentItems && (react_1.default.createElement(PiaCustomCarousel_1.default, { items: currentItems, componentOverrides: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.carousel, callbacks: callbacks, onResultClick: onResultClick, question: question, qnaResultId: qnaResultId, priceCurrency: priceCurrency })),
14
+ currentItems && (react_1.default.createElement(PiaCustomCarousel_1.default, { items: currentItems, componentOverrides: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.carousel, callbacks: callbacks, onResultClick: onResultClick, question: question, qnaResultId: qnaResultId, translations: translations, priceCurrency: priceCurrency })),
15
15
  showFeedback && (react_1.default.createElement(Feedback_1.default, { translations: translations, onFeedback: onFeedback || (callbacks === null || callbacks === void 0 ? void 0 : callbacks.onFeedback), componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.feedback })),
16
16
  disclaimerPosition === 'bottom' && disclaimer));
17
17
  }
@@ -6,14 +6,19 @@ const agent_1 = tslib_1.__importDefault(require("./agent"));
6
6
  const version_1 = tslib_1.__importDefault(require("../../version"));
7
7
  class MockConstructorIOClient {
8
8
  constructor(options) {
9
- this.options = Object.assign({ version: options.version || `cio-ui-pia-${version_1.default}`, serviceUrl: options.serviceUrl || 'https://ac.cnstrc.com', quizzesServiceUrl: options.quizzesServiceUrl || 'https://quizzes.cnstrc.com', agentServiceUrl: options.agentServiceUrl || 'https://agent.cnstrc.com', sessionId: options.sessionId || 0, clientId: options.clientId || 'this-is-a-random-client-id', sendTrackingEvents: options.sendTrackingEvents !== undefined ? options.sendTrackingEvents : true, beaconMode: options.beaconMode !== undefined ? options.beaconMode : true, networkParameters: options.networkParameters || {} }, options);
10
- const cioClient = new constructorio_client_javascript_1.default(this.options);
9
+ const isServer = typeof window === 'undefined' || typeof document === 'undefined';
10
+ const clientOptions = Object.assign(Object.assign(Object.assign({ version: options.version || `cio-ui-pia-${version_1.default}`, serviceUrl: options.serviceUrl || 'https://ac.cnstrc.com', quizzesServiceUrl: options.quizzesServiceUrl || 'https://quizzes.cnstrc.com', agentServiceUrl: options.agentServiceUrl || 'https://agent.cnstrc.com' }, (isServer && {
11
+ clientId: options.clientId || 'this-is-a-random-client-id',
12
+ })), { sendTrackingEvents: options.sendTrackingEvents !== undefined ? options.sendTrackingEvents : true, beaconMode: options.beaconMode !== undefined ? options.beaconMode : true, networkParameters: options.networkParameters || {} }), options);
13
+ const cioClient = new constructorio_client_javascript_1.default(clientOptions);
14
+ const { clientId, sessionId } = cioClient.agent.options;
15
+ this.options = Object.assign(Object.assign({}, clientOptions), { clientId, sessionId });
11
16
  this.search = cioClient.search;
12
17
  this.browse = cioClient.browse;
13
18
  this.recommendations = cioClient.recommendations;
14
19
  this.tracker = cioClient.tracker;
15
20
  this.quizzes = cioClient.quizzes;
16
- this.agent = new agent_1.default(Object.assign(Object.assign({}, this.options), { clientId: options.clientId, sessionId: options.sessionId }));
21
+ this.agent = new agent_1.default(this.options);
17
22
  }
18
23
  }
19
24
  exports.default = MockConstructorIOClient;
@@ -10,6 +10,7 @@ const defaultTranslations = {
10
10
  'Is this answer useful?': 'Is this answer useful?',
11
11
  'Learn More.': 'Learn More.',
12
12
  'Ask about this product': 'Ask about this product',
13
+ 'Add to Cart': 'Add to Cart',
13
14
  };
14
15
  /**
15
16
  * Translates a word using the provided translations object.
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '2.10.0';
3
+ exports.default = '2.10.1';
@@ -1,6 +1,7 @@
1
1
  import React, { useCallback, useRef, useEffect, useMemo } from 'react';
2
- import { Carousel, CIO_EVENTS, } from '@constructor-io/constructorio-ui-components';
2
+ import { Carousel, ProductCard, CIO_EVENTS, } from '@constructor-io/constructorio-ui-components';
3
3
  import { sanitizeHtml } from '../../utils/contentTransformers';
4
+ import { translate } from '../../utils/translate';
4
5
  function HtmlDescription({ product }) {
5
6
  const { description } = product;
6
7
  if (!description)
@@ -53,8 +54,12 @@ function buildMergedOverrides(componentOverrides, priceSectionOverride) {
53
54
  },
54
55
  };
55
56
  }
56
- export default function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, priceCurrency, }) {
57
+ export default function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, translations, priceCurrency, }) {
57
58
  const wrapperRef = useRef(null);
59
+ const { onAddToCart } = callbacks || {};
60
+ const addToCartHandler = useCallback((event, product) => {
61
+ onAddToCart?.(product, event);
62
+ }, [onAddToCart]);
58
63
  // Determine to use user-defined click handler or default behavior
59
64
  const productClickHandler = useCallback((item) => {
60
65
  const position = items.findIndex((i) => i.id === item.id);
@@ -86,7 +91,26 @@ export default function PiaCustomCarousel({ items, componentOverrides, callbacks
86
91
  };
87
92
  }, [productClickHandler]);
88
93
  const priceSectionOverride = useMemo(() => (priceCurrency ? createPriceSectionOverride(priceCurrency) : undefined), [priceCurrency]);
89
- const mergedOverrides = useMemo(() => buildMergedOverrides(componentOverrides, priceSectionOverride), [componentOverrides, priceSectionOverride]);
94
+ const mergedOverrides = useMemo(() => {
95
+ const baseOverrides = buildMergedOverrides(componentOverrides, priceSectionOverride);
96
+ const cardOverrides = baseOverrides.item?.productCard;
97
+ // The carousel never passes onAddToCart down and the card hides the button without it, so
98
+ // re-render the card with the handler. A full card override owns its own layout - skip it.
99
+ if (!onAddToCart || cardOverrides?.reactNode) {
100
+ return baseOverrides;
101
+ }
102
+ const addToCartText = translate('Add to Cart', translations);
103
+ return {
104
+ ...baseOverrides,
105
+ item: {
106
+ ...baseOverrides.item,
107
+ productCard: {
108
+ ...cardOverrides,
109
+ reactNode: ({ product }) => (React.createElement(ProductCard, { product: product, className: 'w-full h-full', addToCartText: addToCartText, onAddToCart: addToCartHandler, componentOverrides: cardOverrides })),
110
+ },
111
+ },
112
+ };
113
+ }, [componentOverrides, priceSectionOverride, onAddToCart, translations, addToCartHandler]);
90
114
  // If there are no items, do not render the carousel
91
115
  if (items.length === 0) {
92
116
  return null;
@@ -31,7 +31,7 @@ export default function ConversationHistory({ conversationHistory, isLoading, er
31
31
  isLast && !isLoading && error && (React.createElement(ErrorBlock, { message: error.message || 'Unexpected error' })),
32
32
  entry.answer && (React.createElement("div", { className: 'cio-pia-answer-container' },
33
33
  React.createElement(Answer, { text: entry.answer, componentOverride: componentOverrides?.answer }),
34
- carouselItems && (React.createElement(PiaCustomCarousel, { items: carouselItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks, onResultClick: onResultClick, question: entry.question, qnaResultId: qnaResultId, priceCurrency: priceCurrency })),
34
+ carouselItems && (React.createElement(PiaCustomCarousel, { items: carouselItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks, onResultClick: onResultClick, question: entry.question, qnaResultId: qnaResultId, translations: translations, priceCurrency: priceCurrency })),
35
35
  isLast && showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: handleFeedback, componentOverride: componentOverrides?.feedback }))))));
36
36
  })),
37
37
  disclaimerPosition === 'bottom' && disclaimer));
@@ -8,7 +8,7 @@ export default function PiaInlineAnswer({ currentAnswer, currentItems, showFeedb
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, onResultClick: onResultClick, question: question, qnaResultId: qnaResultId, priceCurrency: priceCurrency })),
11
+ currentItems && (React.createElement(PiaCustomCarousel, { items: currentItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks, onResultClick: onResultClick, question: question, qnaResultId: qnaResultId, translations: translations, priceCurrency: priceCurrency })),
12
12
  showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: onFeedback || callbacks?.onFeedback, componentOverride: componentOverrides?.feedback })),
13
13
  disclaimerPosition === 'bottom' && disclaimer));
14
14
  }
@@ -10,29 +10,29 @@ class MockConstructorIOClient {
10
10
  quizzes;
11
11
  agent;
12
12
  constructor(options) {
13
- this.options = {
13
+ const isServer = typeof window === 'undefined' || typeof document === 'undefined';
14
+ const clientOptions = {
14
15
  version: options.version || `cio-ui-pia-${version}`,
15
16
  serviceUrl: options.serviceUrl || 'https://ac.cnstrc.com',
16
17
  quizzesServiceUrl: options.quizzesServiceUrl || 'https://quizzes.cnstrc.com',
17
18
  agentServiceUrl: options.agentServiceUrl || 'https://agent.cnstrc.com',
18
- sessionId: options.sessionId || 0,
19
- clientId: options.clientId || 'this-is-a-random-client-id',
19
+ ...(isServer && {
20
+ clientId: options.clientId || 'this-is-a-random-client-id',
21
+ }),
20
22
  sendTrackingEvents: options.sendTrackingEvents !== undefined ? options.sendTrackingEvents : true,
21
23
  beaconMode: options.beaconMode !== undefined ? options.beaconMode : true,
22
24
  networkParameters: options.networkParameters || {},
23
25
  ...options,
24
26
  };
25
- const cioClient = new ConstructorioClient(this.options);
27
+ const cioClient = new ConstructorioClient(clientOptions);
28
+ const { clientId, sessionId } = cioClient.agent.options;
29
+ this.options = { ...clientOptions, clientId, sessionId };
26
30
  this.search = cioClient.search;
27
31
  this.browse = cioClient.browse;
28
32
  this.recommendations = cioClient.recommendations;
29
33
  this.tracker = cioClient.tracker;
30
34
  this.quizzes = cioClient.quizzes;
31
- this.agent = new MockAgent({
32
- ...this.options,
33
- clientId: options.clientId,
34
- sessionId: options.sessionId,
35
- });
35
+ this.agent = new MockAgent(this.options);
36
36
  }
37
37
  }
38
38
  export default MockConstructorIOClient;
@@ -7,6 +7,7 @@ const defaultTranslations = {
7
7
  'Is this answer useful?': 'Is this answer useful?',
8
8
  'Learn More.': 'Learn More.',
9
9
  'Ask about this product': 'Ask about this product',
10
+ 'Add to Cart': 'Add to Cart',
10
11
  };
11
12
  /**
12
13
  * Translates a word using the provided translations object.
@@ -1 +1 @@
1
- export default '2.10.0';
1
+ export default '2.10.1';
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { CarouselOverrides } from '@constructor-io/constructorio-ui-components';
3
- import { Callbacks, Item } from '../../types';
3
+ import { Callbacks, Item, Translations } from '../../types';
4
4
  interface PiaCustomCarouselProps {
5
5
  items: Array<Item>;
6
6
  componentOverrides?: CarouselOverrides<Item>;
@@ -8,7 +8,8 @@ interface PiaCustomCarouselProps {
8
8
  onResultClick?: (item: Item, position: number, question: string, qnaResultId?: string) => void;
9
9
  question?: string;
10
10
  qnaResultId?: string;
11
+ translations?: Translations;
11
12
  priceCurrency?: string;
12
13
  }
13
- export default function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, priceCurrency, }: PiaCustomCarouselProps): React.JSX.Element | null;
14
+ export default function PiaCustomCarousel({ items, componentOverrides, callbacks, onResultClick, question, qnaResultId, translations, priceCurrency, }: PiaCustomCarouselProps): React.JSX.Element | null;
14
15
  export {};
@@ -59,6 +59,7 @@ export type Translations = {
59
59
  'Is this answer useful?'?: string;
60
60
  'Learn More.'?: string;
61
61
  'Ask about this product'?: string;
62
+ 'Add to Cart'?: string;
62
63
  };
63
64
  export type QuestionSource = 'user' | 'suggestion';
64
65
  export interface PiaCallbackContext {
@@ -70,6 +71,12 @@ export interface Callbacks {
70
71
  onQuestionSubmit?: (question: string, context: PiaCallbackContext, source: QuestionSource) => void;
71
72
  /** Called when a product card in the carousel is clicked. */
72
73
  onProductCardClick?: (item: Item) => void;
74
+ /**
75
+ * Called when the "Add to Cart" button on a product card is clicked.
76
+ * Providing this callback is what renders the button.
77
+ * Clicking the button does not fire `onProductCardClick`.
78
+ */
79
+ onAddToCart?: (item: Item, event: React.MouseEvent) => void;
73
80
  /** Called when the user submits positive or negative feedback on an answer. */
74
81
  onFeedback?: (type: FeedbackType) => void;
75
82
  /** Called when a new answer is received. Passes the full conversation history. */
@@ -1,2 +1,2 @@
1
- declare const _default: "2.10.0";
1
+ declare const _default: "2.10.1";
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.10.0",
3
+ "version": "2.10.1",
4
4
  "description": "Constructor.io AI Product Insights Agent UI library for web applications",
5
5
  "author": "Constructor.io Corporation",
6
6
  "license": "MIT",
@@ -26,8 +26,10 @@
26
26
  "module": "lib/mjs/index.js",
27
27
  "exports": {
28
28
  "./styles.css": {
29
+ "style": "./lib/mjs/styles.css",
29
30
  "import": "./lib/mjs/styles.css",
30
- "require": "./lib/cjs/styles.css"
31
+ "require": "./lib/cjs/styles.css",
32
+ "default": "./lib/mjs/styles.css"
31
33
  },
32
34
  "./constructorio-ui-pia-bundled": {
33
35
  "import": "./dist/constructorio-ui-pia-bundled.js",