@constructor-io/constructorio-ui-pia 2.15.0 → 2.16.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.
@@ -23,7 +23,7 @@ const CheckoutTriggerBar_1 = tslib_1.__importDefault(require("./CheckoutTriggerB
23
23
  // eslint-disable-next-line complexity
24
24
  function CioPiaQna(props) {
25
25
  var _a;
26
- const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, answerParameters, parameters, trackingConfigs, checkoutTriggers, abTest, } = props;
26
+ const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, answerParameters, parameters, trackingConfigs, checkoutTriggers, abTest, initialConversationHistory, } = props;
27
27
  const { learnMoreUrl, showFeedback, mode = 'default', type = 'inline', showPreviousItems, disclaimerPosition = 'bottom', } = displayConfigs || {};
28
28
  const isConversation = mode === 'conversation' || type === 'modal';
29
29
  const { priceCurrency } = productCardProps || {};
@@ -46,7 +46,14 @@ function CioPiaQna(props) {
46
46
  variationId,
47
47
  threadId: pia.threadId,
48
48
  });
49
- const { currentQuestion, displayedQuestions, conversationHistory, currentAnswer, currentItems, isLoading, error, context, handleSubmitQuestion, handleQuestionClick, handleInputFocus, handleFeedback, resetState, } = (0, useConversation_1.default)({ pia, itemId, isConversation, callbacks, tracking });
49
+ const { currentQuestion, displayedQuestions, conversationHistory, currentAnswer, currentItems, isLoading, error, context, handleSubmitQuestion, handleQuestionClick, handleInputFocus, handleFeedback, resetState, } = (0, useConversation_1.default)({
50
+ pia,
51
+ itemId,
52
+ isConversation,
53
+ callbacks,
54
+ tracking,
55
+ initialConversationHistory,
56
+ });
50
57
  const { containerRef: viewportContainerRef } = (0, useViewportTracking_1.default)({
51
58
  tracking,
52
59
  questions: displayedQuestions,
@@ -81,7 +88,8 @@ function CioPiaQna(props) {
81
88
  isAnswerLoading,
82
89
  error,
83
90
  checkoutElement,
84
- currentItems,
91
+ // Until a live answer arrives, a seeded last entry shows its own items. `null` would hide them.
92
+ currentItems: pia.answers.data ? currentItems : undefined,
85
93
  showFeedback,
86
94
  showPreviousItems,
87
95
  learnMoreUrl,
@@ -22,11 +22,16 @@ function ConversationHistory({ conversationHistory, isLoading, isAnswerLoading =
22
22
  return () => cancelAnimationFrame(frameId);
23
23
  }, [conversationHistory, isLoading]);
24
24
  const lastEntry = conversationHistory[conversationHistory.length - 1];
25
- const answerStatus = (0, StatusRegion_1.answerStatusMessage)(isAnswerLoading, !!(lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.answer), translations);
25
+ // An entry answered before mount (a restored conversation) was not just answered: announcing
26
+ // "Answer ready" on page load would tell the shopper something happened when nothing did.
27
+ const [answeredOnMountId] = (0, react_1.useState)(() => ((lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.answer) ? lastEntry.id : undefined));
28
+ const hasNewAnswer = !!(lastEntry === null || lastEntry === void 0 ? void 0 : lastEntry.answer) && lastEntry.id !== answeredOnMountId;
29
+ const answerStatus = (0, StatusRegion_1.answerStatusMessage)(isAnswerLoading, hasNewAnswer, translations);
26
30
  const disclaimer = (react_1.default.createElement(Disclaimer_1.default, { learnMoreUrl: learnMoreUrl, translations: translations, componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.disclaimer }));
27
31
  return (react_1.default.createElement("div", { className: 'cio-pia-conversation-history' },
28
32
  disclaimerPosition === 'top' && disclaimer,
29
33
  react_1.default.createElement("div", { ref: scrollContainerRef, className: 'cio-pia-conversation-entries', role: 'log', tabIndex: 0, "aria-label": (0, translate_1.translate)('Conversation history', translations) }, conversationHistory.map((entry, index) => {
34
+ var _a;
30
35
  const isLast = index === conversationHistory.length - 1;
31
36
  const previousEntryItems = showPreviousItems ? entry.items : null;
32
37
  const latestEntryItems = currentItems !== undefined ? currentItems : entry.items;
@@ -38,7 +43,7 @@ function ConversationHistory({ conversationHistory, isLoading, isAnswerLoading =
38
43
  isLast && !isLoading && error && (react_1.default.createElement(ErrorBlock_1.default, { key: error.message || (0, translate_1.translate)('Unexpected error', translations), message: error.message, translations: translations, announce: false })),
39
44
  entry.answer && (react_1.default.createElement("div", { className: 'cio-pia-answer-container' },
40
45
  react_1.default.createElement(Answer_1.default, { text: entry.answer, componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.answer }),
41
- 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 })),
46
+ 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: (_a = entry.qnaResultId) !== null && _a !== void 0 ? _a : (isLast ? qnaResultId : undefined), translations: translations, priceCurrency: priceCurrency })),
42
47
  isLast && showFeedback && (react_1.default.createElement(Feedback_1.default, { translations: translations, onFeedback: handleFeedback, componentOverride: componentOverrides === null || componentOverrides === void 0 ? void 0 : componentOverrides.feedback }))))));
43
48
  })),
44
49
  disclaimerPosition === 'bottom' && disclaimer,
@@ -1,15 +1,40 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const react_1 = require("react");
4
- function useConversation({ pia, itemId, isConversation, callbacks, tracking, }) {
4
+ function isRestorableEntry(entry) {
5
+ if (typeof entry !== 'object' || entry === null)
6
+ return false;
7
+ const { question, answer, items } = entry;
8
+ return (typeof question === 'string' &&
9
+ typeof answer === 'string' &&
10
+ (items === undefined || items === null || Array.isArray(items)));
11
+ }
12
+ // The seed comes from the host's own storage, typed or not: a malformed one must not take the widget down.
13
+ function restoreHistory(seed) {
14
+ if (!Array.isArray(seed)) {
15
+ console.warn('[CioPia] initialConversationHistory must be an array. It was ignored.');
16
+ return [];
17
+ }
18
+ const entries = seed.filter(isRestorableEntry);
19
+ if (entries.length < seed.length) {
20
+ console.warn(`[CioPia] ${seed.length - entries.length} initialConversationHistory entries were ignored. Each needs a string question and answer, and items must be an array.`);
21
+ }
22
+ // Seeded ids are replaced: `id` is the React key, so a caller's values cannot be trusted to be unique.
23
+ return entries.map((entry, index) => (Object.assign(Object.assign({}, entry), { id: index + 1 })));
24
+ }
25
+ function useConversation({ pia, itemId, isConversation, callbacks, tracking, initialConversationHistory, }) {
5
26
  var _a, _b, _c;
6
27
  const { suggestedQuestions, answers, threadId } = pia;
7
28
  const { getAnswer } = answers;
8
29
  const context = (0, react_1.useMemo)(() => ({ itemId, threadId }), [itemId, threadId]);
9
30
  const [currentQuestion, setCurrentQuestion] = (0, react_1.useState)('');
10
31
  const [displayedQuestions, setDisplayedQuestions] = (0, react_1.useState)([]);
11
- const [conversationHistory, setConversationHistory] = (0, react_1.useState)([]);
12
- const entryIdRef = (0, react_1.useRef)(0);
32
+ const [conversationHistory, setConversationHistory] = (0, react_1.useState)(() => isConversation && initialConversationHistory !== undefined
33
+ ? restoreHistory(initialConversationHistory)
34
+ : []);
35
+ const entryIdRef = (0, react_1.useRef)(conversationHistory.length);
36
+ const conversationHistoryRef = (0, react_1.useRef)(conversationHistory);
37
+ const prevItemIdRef = (0, react_1.useRef)(itemId);
13
38
  const prevAnswerDataRef = (0, react_1.useRef)(answers.data);
14
39
  const hasTrackedCurrentAnswerRef = (0, react_1.useRef)(false);
15
40
  const answersRef = (0, react_1.useRef)(answers);
@@ -33,6 +58,9 @@ function useConversation({ pia, itemId, isConversation, callbacks, tracking, })
33
58
  (0, react_1.useEffect)(() => {
34
59
  answersRef.current = answers;
35
60
  }, [answers]);
61
+ (0, react_1.useEffect)(() => {
62
+ conversationHistoryRef.current = conversationHistory;
63
+ }, [conversationHistory]);
36
64
  const submitQuestion = (0, react_1.useCallback)((question, source) => {
37
65
  lastSourceRef.current = source;
38
66
  lastQuestionRef.current = question;
@@ -63,9 +91,12 @@ function useConversation({ pia, itemId, isConversation, callbacks, tracking, })
63
91
  (_c = (_b = callbacksRef.current) === null || _b === void 0 ? void 0 : _b.onFocus) === null || _c === void 0 ? void 0 : _c.call(_b, contextRef.current);
64
92
  }, []);
65
93
  const handleFeedback = (0, react_1.useCallback)((type) => {
66
- var _a, _b, _c, _d;
67
- (_a = trackingRef.current) === null || _a === void 0 ? void 0 : _a.trackAnswerFeedback(type, (_b = answersRef.current.data) === null || _b === void 0 ? void 0 : _b.qna_result_id);
68
- (_d = (_c = callbacksRef.current) === null || _c === void 0 ? void 0 : _c.onFeedback) === null || _d === void 0 ? void 0 : _d.call(_c, type);
94
+ var _a, _b, _c, _d, _e, _f;
95
+ // A seeded last entry has no live response behind it; its own id is the answer rated.
96
+ const history = conversationHistoryRef.current;
97
+ const qnaResultId = (_b = (_a = answersRef.current.data) === null || _a === void 0 ? void 0 : _a.qna_result_id) !== null && _b !== void 0 ? _b : (_c = history[history.length - 1]) === null || _c === void 0 ? void 0 : _c.qnaResultId;
98
+ (_d = trackingRef.current) === null || _d === void 0 ? void 0 : _d.trackAnswerFeedback(type, qnaResultId);
99
+ (_f = (_e = callbacksRef.current) === null || _e === void 0 ? void 0 : _e.onFeedback) === null || _f === void 0 ? void 0 : _f.call(_e, type);
69
100
  }, []);
70
101
  const resetState = (0, react_1.useCallback)(() => {
71
102
  setCurrentQuestion('');
@@ -75,6 +106,10 @@ function useConversation({ pia, itemId, isConversation, callbacks, tracking, })
75
106
  showsFollowUpsRef.current = false;
76
107
  }, [suggestedQuestions.data]);
77
108
  (0, react_1.useEffect)(() => {
109
+ // Skip the mount run: it would wipe the seeded history.
110
+ if (prevItemIdRef.current === itemId)
111
+ return;
112
+ prevItemIdRef.current = itemId;
78
113
  setCurrentQuestion('');
79
114
  setDisplayedQuestions([]);
80
115
  setConversationHistory([]);
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '2.15.0';
3
+ exports.default = '2.16.1';
@@ -19,7 +19,7 @@ import CheckoutTriggerBar from './CheckoutTriggerBar';
19
19
  /** The question-and-answer experience: `mode: 'default'`, `mode: 'conversation'`, and the modal. */
20
20
  // eslint-disable-next-line complexity
21
21
  export default function CioPiaQna(props) {
22
- const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, answerParameters, parameters, trackingConfigs, checkoutTriggers, abTest, } = props;
22
+ const { apiKey, itemId, itemName, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, formatters, productCardProps, children, translations, suggestedQuestionsParameters, answerParameters, parameters, trackingConfigs, checkoutTriggers, abTest, initialConversationHistory, } = props;
23
23
  const { learnMoreUrl, showFeedback, mode = 'default', type = 'inline', showPreviousItems, disclaimerPosition = 'bottom', } = displayConfigs || {};
24
24
  const isConversation = mode === 'conversation' || type === 'modal';
25
25
  const { priceCurrency } = productCardProps || {};
@@ -42,7 +42,14 @@ export default function CioPiaQna(props) {
42
42
  variationId,
43
43
  threadId: pia.threadId,
44
44
  });
45
- const { currentQuestion, displayedQuestions, conversationHistory, currentAnswer, currentItems, isLoading, error, context, handleSubmitQuestion, handleQuestionClick, handleInputFocus, handleFeedback, resetState, } = useConversation({ pia, itemId, isConversation, callbacks, tracking });
45
+ const { currentQuestion, displayedQuestions, conversationHistory, currentAnswer, currentItems, isLoading, error, context, handleSubmitQuestion, handleQuestionClick, handleInputFocus, handleFeedback, resetState, } = useConversation({
46
+ pia,
47
+ itemId,
48
+ isConversation,
49
+ callbacks,
50
+ tracking,
51
+ initialConversationHistory,
52
+ });
46
53
  const { containerRef: viewportContainerRef } = useViewportTracking({
47
54
  tracking,
48
55
  questions: displayedQuestions,
@@ -77,7 +84,8 @@ export default function CioPiaQna(props) {
77
84
  isAnswerLoading,
78
85
  error,
79
86
  checkoutElement,
80
- currentItems,
87
+ // Until a live answer arrives, a seeded last entry shows its own items. `null` would hide them.
88
+ currentItems: pia.answers.data ? currentItems : undefined,
81
89
  showFeedback,
82
90
  showPreviousItems,
83
91
  learnMoreUrl,
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import Answer from '../Answer/Answer';
3
3
  import Feedback from '../Feedback/Feedback';
4
4
  import Disclaimer from '../CioPia/Disclaimer';
@@ -19,7 +19,11 @@ export default function ConversationHistory({ conversationHistory, isLoading, is
19
19
  return () => cancelAnimationFrame(frameId);
20
20
  }, [conversationHistory, isLoading]);
21
21
  const lastEntry = conversationHistory[conversationHistory.length - 1];
22
- const answerStatus = answerStatusMessage(isAnswerLoading, !!lastEntry?.answer, translations);
22
+ // An entry answered before mount (a restored conversation) was not just answered: announcing
23
+ // "Answer ready" on page load would tell the shopper something happened when nothing did.
24
+ const [answeredOnMountId] = useState(() => (lastEntry?.answer ? lastEntry.id : undefined));
25
+ const hasNewAnswer = !!lastEntry?.answer && lastEntry.id !== answeredOnMountId;
26
+ const answerStatus = answerStatusMessage(isAnswerLoading, hasNewAnswer, translations);
23
27
  const disclaimer = (React.createElement(Disclaimer, { learnMoreUrl: learnMoreUrl, translations: translations, componentOverride: componentOverrides?.disclaimer }));
24
28
  return (React.createElement("div", { className: 'cio-pia-conversation-history' },
25
29
  disclaimerPosition === 'top' && disclaimer,
@@ -35,7 +39,7 @@ export default function ConversationHistory({ conversationHistory, isLoading, is
35
39
  isLast && !isLoading && error && (React.createElement(ErrorBlock, { key: error.message || translate('Unexpected error', translations), message: error.message, translations: translations, announce: false })),
36
40
  entry.answer && (React.createElement("div", { className: 'cio-pia-answer-container' },
37
41
  React.createElement(Answer, { text: entry.answer, componentOverride: componentOverrides?.answer }),
38
- carouselItems && (React.createElement(PiaCustomCarousel, { items: carouselItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks, onResultClick: onResultClick, question: entry.question, qnaResultId: qnaResultId, translations: translations, priceCurrency: priceCurrency })),
42
+ carouselItems && (React.createElement(PiaCustomCarousel, { items: carouselItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks, onResultClick: onResultClick, question: entry.question, qnaResultId: entry.qnaResultId ?? (isLast ? qnaResultId : undefined), translations: translations, priceCurrency: priceCurrency })),
39
43
  isLast && showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: handleFeedback, componentOverride: componentOverrides?.feedback }))))));
40
44
  })),
41
45
  disclaimerPosition === 'bottom' && disclaimer,
@@ -1,12 +1,37 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
- export default function useConversation({ pia, itemId, isConversation, callbacks, tracking, }) {
2
+ function isRestorableEntry(entry) {
3
+ if (typeof entry !== 'object' || entry === null)
4
+ return false;
5
+ const { question, answer, items } = entry;
6
+ return (typeof question === 'string' &&
7
+ typeof answer === 'string' &&
8
+ (items === undefined || items === null || Array.isArray(items)));
9
+ }
10
+ // The seed comes from the host's own storage, typed or not: a malformed one must not take the widget down.
11
+ function restoreHistory(seed) {
12
+ if (!Array.isArray(seed)) {
13
+ console.warn('[CioPia] initialConversationHistory must be an array. It was ignored.');
14
+ return [];
15
+ }
16
+ const entries = seed.filter(isRestorableEntry);
17
+ if (entries.length < seed.length) {
18
+ console.warn(`[CioPia] ${seed.length - entries.length} initialConversationHistory entries were ignored. Each needs a string question and answer, and items must be an array.`);
19
+ }
20
+ // Seeded ids are replaced: `id` is the React key, so a caller's values cannot be trusted to be unique.
21
+ return entries.map((entry, index) => ({ ...entry, id: index + 1 }));
22
+ }
23
+ export default function useConversation({ pia, itemId, isConversation, callbacks, tracking, initialConversationHistory, }) {
3
24
  const { suggestedQuestions, answers, threadId } = pia;
4
25
  const { getAnswer } = answers;
5
26
  const context = useMemo(() => ({ itemId, threadId }), [itemId, threadId]);
6
27
  const [currentQuestion, setCurrentQuestion] = useState('');
7
28
  const [displayedQuestions, setDisplayedQuestions] = useState([]);
8
- const [conversationHistory, setConversationHistory] = useState([]);
9
- const entryIdRef = useRef(0);
29
+ const [conversationHistory, setConversationHistory] = useState(() => isConversation && initialConversationHistory !== undefined
30
+ ? restoreHistory(initialConversationHistory)
31
+ : []);
32
+ const entryIdRef = useRef(conversationHistory.length);
33
+ const conversationHistoryRef = useRef(conversationHistory);
34
+ const prevItemIdRef = useRef(itemId);
10
35
  const prevAnswerDataRef = useRef(answers.data);
11
36
  const hasTrackedCurrentAnswerRef = useRef(false);
12
37
  const answersRef = useRef(answers);
@@ -30,6 +55,9 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
30
55
  useEffect(() => {
31
56
  answersRef.current = answers;
32
57
  }, [answers]);
58
+ useEffect(() => {
59
+ conversationHistoryRef.current = conversationHistory;
60
+ }, [conversationHistory]);
33
61
  const submitQuestion = useCallback((question, source) => {
34
62
  lastSourceRef.current = source;
35
63
  lastQuestionRef.current = question;
@@ -57,7 +85,10 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
57
85
  callbacksRef.current?.onFocus?.(contextRef.current);
58
86
  }, []);
59
87
  const handleFeedback = useCallback((type) => {
60
- trackingRef.current?.trackAnswerFeedback(type, answersRef.current.data?.qna_result_id);
88
+ // A seeded last entry has no live response behind it; its own id is the answer rated.
89
+ const history = conversationHistoryRef.current;
90
+ const qnaResultId = answersRef.current.data?.qna_result_id ?? history[history.length - 1]?.qnaResultId;
91
+ trackingRef.current?.trackAnswerFeedback(type, qnaResultId);
61
92
  callbacksRef.current?.onFeedback?.(type);
62
93
  }, []);
63
94
  const resetState = useCallback(() => {
@@ -68,6 +99,10 @@ export default function useConversation({ pia, itemId, isConversation, callbacks
68
99
  showsFollowUpsRef.current = false;
69
100
  }, [suggestedQuestions.data]);
70
101
  useEffect(() => {
102
+ // Skip the mount run: it would wipe the seeded history.
103
+ if (prevItemIdRef.current === itemId)
104
+ return;
105
+ prevItemIdRef.current = itemId;
71
106
  setCurrentQuestion('');
72
107
  setDisplayedQuestions([]);
73
108
  setConversationHistory([]);
@@ -1 +1 @@
1
- export default '2.15.0';
1
+ export default '2.16.1';
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { IncludeComponentOverrides, IncludeRenderProps } from '@constructor-io/constructorio-ui-components';
3
- import { CioPiaRenderProps, CioPiaComponentOverrides, Callbacks, CioPiaAbTest, CioPiaDisplayConfigs, CioPiaTrackingConfigs, Translations, SuggestedQuestionsParameters, AnswerRequestParameters, RecsPodParameters, Formatters, ProductCardDisplayProps } from '../../types';
3
+ import { CioPiaRenderProps, CioPiaComponentOverrides, Callbacks, CioPiaAbTest, CioPiaDisplayConfigs, CioPiaTrackingConfigs, Translations, SuggestedQuestionsParameters, AnswerRequestParameters, RecsPodParameters, Formatters, ConversationEntry, ProductCardDisplayProps } from '../../types';
4
4
  import type { CioClient } from '../../hooks/usePiaClient';
5
5
  /**
6
6
  * A single checkout entry point rendered inside the PIA `default` mode.
@@ -47,6 +47,15 @@ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, Incl
47
47
  itemName: string;
48
48
  /** Thread ID for conversation context. Must be a valid UUID (e.g., "550e8400-e29b-41d4-a716-446655440000"). */
49
49
  threadId?: string;
50
+ /**
51
+ * A conversation you manage, such as one saved from `onAnswer` in the browser or on your own
52
+ * server. When provided, including as `[]`, these entries are the conversation shown before the
53
+ * first question; pass `[]` rather than omitting it when nothing is saved.
54
+ * Read once on mount, so render after it has loaded, and cleared when `itemId` changes. Pass the
55
+ * `threadId` the entries came from, or the agent will not remember them. Used by
56
+ * `mode: 'conversation'` and `type: 'modal'` only; the modal shows them once it opens.
57
+ */
58
+ initialConversationHistory?: ConversationEntry[];
50
59
  /** Optional variation ID for the product. */
51
60
  variationId?: string;
52
61
  /** Optional Constructor.io client instance. If not provided, one will be created internally. */
@@ -7,6 +7,7 @@ export interface UseConversationProps {
7
7
  isConversation: boolean;
8
8
  callbacks?: Callbacks;
9
9
  tracking?: UseTrackingReturn;
10
+ initialConversationHistory?: ConversationEntry[];
10
11
  }
11
12
  export interface UseConversationReturn {
12
13
  currentQuestion: string;
@@ -23,4 +24,4 @@ export interface UseConversationReturn {
23
24
  handleFeedback: (type: FeedbackType) => void;
24
25
  resetState: () => void;
25
26
  }
26
- export default function useConversation({ pia, itemId, isConversation, callbacks, tracking, }: UseConversationProps): UseConversationReturn;
27
+ export default function useConversation({ pia, itemId, isConversation, callbacks, tracking, initialConversationHistory, }: UseConversationProps): UseConversationReturn;
@@ -165,13 +165,18 @@ export interface Item extends Product, Record<string, any> {
165
165
  url?: string;
166
166
  matchedTerms?: string[];
167
167
  }
168
+ /** One question and its answer, as passed to `onAnswer` and accepted by `initialConversationHistory`. */
168
169
  export interface ConversationEntry {
170
+ /** Unique within the conversation. Assigned by the widget: ids in `initialConversationHistory` are replaced. */
169
171
  id: number;
170
172
  question: string;
171
173
  answer: string;
174
+ /** Whether the shopper typed the question or clicked a suggested one. */
172
175
  source: QuestionSource;
176
+ /** Products shown with the answer. */
173
177
  items?: Item[] | null;
174
178
  threadId?: string;
179
+ /** Identifies the answer for feedback and product click tracking. */
175
180
  qnaResultId?: string;
176
181
  }
177
182
  /** Which kind of recommendations to fetch. */
@@ -1,2 +1,2 @@
1
- declare const _default: "2.15.0";
1
+ declare const _default: "2.16.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.15.0",
3
+ "version": "2.16.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",
@@ -36,10 +36,12 @@
36
36
  "require": "./dist/constructorio-ui-pia-bundled.js"
37
37
  },
38
38
  ".": {
39
+ "types": "./lib/types/index.d.ts",
39
40
  "import": "./lib/mjs/index.js",
40
41
  "require": "./lib/cjs/index.js"
41
42
  },
42
43
  "./cjs": {
44
+ "types": "./lib/types/index.d.ts",
43
45
  "import": "./lib/cjs/index.js",
44
46
  "require": "./lib/cjs/index.js"
45
47
  }