@constructor-io/constructorio-ui-pia 2.1.0 → 2.1.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.
Files changed (120) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +219 -15
  3. package/dist/constructorio-ui-pia-bundled.js +11 -11
  4. package/lib/cjs/components/Answer/Answer.js +13 -0
  5. package/lib/cjs/components/Answer/AnswerStyles.css +8 -0
  6. package/lib/cjs/components/CioPia/CioPia.js +76 -0
  7. package/lib/cjs/components/CioPia/CioPiaStyles.css +32 -0
  8. package/lib/cjs/components/CioPia/Disclaimer.js +16 -0
  9. package/lib/cjs/components/CioPia/PiaCustomCarousel.js +41 -0
  10. package/lib/cjs/components/CioPia/index.js +6 -0
  11. package/lib/cjs/components/ConversationHistory/ConversationHistory.js +36 -0
  12. package/lib/cjs/components/ConversationHistory/ConversationHistoryStyles.css +51 -0
  13. package/lib/cjs/components/Error/ErrorBlock.js +23 -0
  14. package/lib/cjs/components/Error/ErrorStyles.css +46 -0
  15. package/lib/cjs/components/Feedback/Feedback.js +28 -0
  16. package/lib/cjs/components/Feedback/FeedbackStyles.css +31 -0
  17. package/lib/cjs/components/Input/Input.js +40 -0
  18. package/lib/cjs/components/Input/InputStyles.css +102 -0
  19. package/lib/cjs/components/LoadingSkeleton/LoadingSkeleton.js +11 -0
  20. package/lib/cjs/components/LoadingSkeleton/LoadingSkeletonStyles.css +32 -0
  21. package/lib/cjs/components/PiaConversation/PiaConversation.js +20 -0
  22. package/lib/cjs/components/PiaConversation/PiaConversationStyles.css +91 -0
  23. package/lib/cjs/components/PiaConversation/PiaModal.js +74 -0
  24. package/lib/cjs/components/SuggestedQuestion/SuggestedQuestion.js +16 -0
  25. package/lib/cjs/components/SuggestedQuestion/SuggestedQuestionStyles.css +33 -0
  26. package/lib/cjs/components/SuggestedQuestionsContainer/SuggestedQuestionsContainer.js +14 -0
  27. package/lib/cjs/components/SuggestedQuestionsContainer/SuggestedQuestionsContainerStyles.css +37 -0
  28. package/lib/cjs/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.js +11 -0
  29. package/lib/cjs/constants.js +17 -0
  30. package/lib/cjs/hooks/mocks/MockConstructorIOClient.js +20 -0
  31. package/lib/cjs/hooks/mocks/agent.js +146 -0
  32. package/lib/cjs/hooks/mocks/types.js +2 -0
  33. package/lib/cjs/hooks/useAnswerResults.js +62 -0
  34. package/lib/cjs/hooks/useCioClient.js +21 -0
  35. package/lib/cjs/hooks/useCioPia.js +29 -0
  36. package/lib/cjs/hooks/useConversation.js +74 -0
  37. package/lib/cjs/hooks/useSuggestedQuestions.js +52 -0
  38. package/lib/cjs/stories/assets/storybook-styles.css +112 -0
  39. package/lib/cjs/styles.css +26 -0
  40. package/lib/cjs/types.js +10 -0
  41. package/lib/cjs/utils/index.js +4 -0
  42. package/lib/cjs/utils/styleHelpers.js +14 -0
  43. package/lib/cjs/utils/transformers.js +29 -0
  44. package/lib/cjs/utils/translate.js +29 -0
  45. package/lib/cjs/version.js +3 -0
  46. package/lib/mjs/components/Answer/Answer.js +10 -0
  47. package/lib/mjs/components/Answer/AnswerStyles.css +8 -0
  48. package/lib/mjs/components/CioPia/CioPia.js +72 -0
  49. package/lib/mjs/components/CioPia/CioPiaStyles.css +32 -0
  50. package/lib/mjs/components/CioPia/Disclaimer.js +12 -0
  51. package/lib/mjs/components/CioPia/PiaCustomCarousel.js +37 -0
  52. package/lib/mjs/components/CioPia/index.js +3 -0
  53. package/lib/mjs/components/ConversationHistory/ConversationHistory.js +32 -0
  54. package/lib/mjs/components/ConversationHistory/ConversationHistoryStyles.css +51 -0
  55. package/lib/mjs/components/Error/ErrorBlock.js +20 -0
  56. package/lib/mjs/components/Error/ErrorStyles.css +46 -0
  57. package/lib/mjs/components/Feedback/Feedback.js +24 -0
  58. package/lib/mjs/components/Feedback/FeedbackStyles.css +31 -0
  59. package/lib/mjs/components/Input/Input.js +37 -0
  60. package/lib/mjs/components/Input/InputStyles.css +102 -0
  61. package/lib/mjs/components/LoadingSkeleton/LoadingSkeleton.js +7 -0
  62. package/lib/mjs/components/LoadingSkeleton/LoadingSkeletonStyles.css +32 -0
  63. package/lib/mjs/components/PiaConversation/PiaConversation.js +16 -0
  64. package/lib/mjs/components/PiaConversation/PiaConversationStyles.css +91 -0
  65. package/lib/mjs/components/PiaConversation/PiaModal.js +70 -0
  66. package/lib/mjs/components/SuggestedQuestion/SuggestedQuestion.js +13 -0
  67. package/lib/mjs/components/SuggestedQuestion/SuggestedQuestionStyles.css +33 -0
  68. package/lib/mjs/components/SuggestedQuestionsContainer/SuggestedQuestionsContainer.js +10 -0
  69. package/lib/mjs/components/SuggestedQuestionsContainer/SuggestedQuestionsContainerStyles.css +37 -0
  70. package/lib/mjs/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.js +7 -0
  71. package/lib/mjs/constants.js +14 -0
  72. package/lib/mjs/hooks/mocks/MockConstructorIOClient.js +35 -0
  73. package/lib/mjs/hooks/mocks/agent.js +138 -0
  74. package/lib/mjs/hooks/mocks/types.js +1 -0
  75. package/lib/mjs/hooks/useAnswerResults.js +57 -0
  76. package/lib/mjs/hooks/useCioClient.js +23 -0
  77. package/lib/mjs/hooks/useCioPia.js +25 -0
  78. package/lib/mjs/hooks/useConversation.js +71 -0
  79. package/lib/mjs/hooks/useSuggestedQuestions.js +48 -0
  80. package/lib/mjs/index.js +6 -0
  81. package/lib/mjs/stories/assets/storybook-styles.css +112 -0
  82. package/lib/mjs/styles.css +26 -0
  83. package/lib/mjs/types.js +6 -0
  84. package/lib/mjs/utils/index.js +1 -0
  85. package/lib/mjs/utils/styleHelpers.js +9 -0
  86. package/lib/mjs/utils/transformers.js +30 -0
  87. package/lib/mjs/utils/translate.js +25 -0
  88. package/lib/mjs/version.js +1 -0
  89. package/lib/types/components/Answer/Answer.d.ts +9 -0
  90. package/lib/types/components/CioPia/CioPia.d.ts +17 -0
  91. package/lib/types/components/CioPia/Disclaimer.d.ts +8 -0
  92. package/lib/types/components/CioPia/PiaCustomCarousel.d.ts +10 -0
  93. package/lib/types/components/CioPia/index.d.ts +3 -0
  94. package/lib/types/components/ConversationHistory/ConversationHistory.d.ts +14 -0
  95. package/lib/types/components/Error/ErrorBlock.d.ts +7 -0
  96. package/lib/types/components/Feedback/Feedback.d.ts +10 -0
  97. package/lib/types/components/Input/Input.d.ts +12 -0
  98. package/lib/types/components/LoadingSkeleton/LoadingSkeleton.d.ts +2 -0
  99. package/lib/types/components/PiaConversation/PiaConversation.d.ts +8 -0
  100. package/lib/types/components/PiaConversation/PiaModal.d.ts +12 -0
  101. package/lib/types/components/SuggestedQuestion/SuggestedQuestion.d.ts +7 -0
  102. package/lib/types/components/SuggestedQuestionsContainer/SuggestedQuestionsContainer.d.ts +10 -0
  103. package/lib/types/components/SuggestedQuestionsContainer/SuggestedQuestionsSkeleton.d.ts +2 -0
  104. package/lib/types/constants.d.ts +8 -0
  105. package/lib/types/hooks/mocks/MockConstructorIOClient.d.ts +14 -0
  106. package/lib/types/hooks/mocks/agent.d.ts +10 -0
  107. package/lib/types/hooks/mocks/types.d.ts +75 -0
  108. package/lib/types/hooks/useAnswerResults.d.ts +18 -0
  109. package/lib/types/hooks/useCioClient.d.ts +13 -0
  110. package/lib/types/hooks/useCioPia.d.ts +17 -0
  111. package/lib/types/hooks/useConversation.d.ts +19 -0
  112. package/lib/types/hooks/useSuggestedQuestions.d.ts +16 -0
  113. package/lib/types/index.d.ts +5 -0
  114. package/lib/types/types.d.ts +102 -0
  115. package/lib/types/utils/index.d.ts +1 -0
  116. package/lib/types/utils/styleHelpers.d.ts +1 -0
  117. package/lib/types/utils/transformers.d.ts +7 -0
  118. package/lib/types/utils/translate.d.ts +10 -0
  119. package/lib/types/version.d.ts +2 -0
  120. package/package.json +14 -3
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const react_1 = require("react");
5
+ const fetchSuggestedQuestions = ({ client, itemId, variationId, threadId, parameters, }) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
6
+ const response = yield client.agent.getSuggestedQuestions({
7
+ itemId,
8
+ variationId,
9
+ threadId,
10
+ parameters,
11
+ });
12
+ return response.questions;
13
+ });
14
+ function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, }) {
15
+ const [questions, setQuestions] = (0, react_1.useState)([]);
16
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
17
+ const [error, setError] = (0, react_1.useState)(null);
18
+ const fetchResult = (0, react_1.useCallback)(() => {
19
+ if (!cioClient)
20
+ return;
21
+ setIsLoading(true);
22
+ setError(null);
23
+ fetchSuggestedQuestions({
24
+ client: cioClient,
25
+ itemId,
26
+ variationId,
27
+ threadId,
28
+ parameters,
29
+ })
30
+ .then((fetchedQuestions) => {
31
+ setQuestions(fetchedQuestions);
32
+ setError(null);
33
+ })
34
+ .catch((err) => {
35
+ setError(err instanceof Error ? err : new Error('Error fetching questions'));
36
+ })
37
+ .finally(() => {
38
+ setIsLoading(false);
39
+ });
40
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- primitive dep prevents refetch on object reference change
41
+ }, [cioClient, itemId, variationId, threadId, parameters === null || parameters === void 0 ? void 0 : parameters.numResults]);
42
+ (0, react_1.useEffect)(() => {
43
+ fetchResult();
44
+ }, [fetchResult]);
45
+ return {
46
+ data: questions,
47
+ isLoading,
48
+ error,
49
+ getSuggestedQuestions: fetchResult,
50
+ };
51
+ }
52
+ exports.default = useSuggestedQuestions;
@@ -0,0 +1,112 @@
1
+ div.subheading {
2
+ --mediumdark: '#999999';
3
+ font-weight: 700;
4
+ font-size: 13px;
5
+ color: #999;
6
+ letter-spacing: 6px;
7
+ line-height: 24px;
8
+ text-transform: uppercase;
9
+ margin-bottom: 12px;
10
+ margin-top: 40px;
11
+ }
12
+
13
+ div.link-list {
14
+ display: grid;
15
+ grid-template-columns: 1fr;
16
+ grid-template-rows: 1fr 1fr;
17
+ row-gap: 10px;
18
+ }
19
+
20
+ @media (min-width: 620px) {
21
+ div.link-list {
22
+ row-gap: 20px;
23
+ column-gap: 20px;
24
+ grid-template-columns: 1fr 1fr;
25
+ }
26
+ }
27
+
28
+ @media all and (-ms-high-contrast:none) {
29
+ div.link-list {
30
+ display: -ms-grid;
31
+ -ms-grid-columns: 1fr 1fr;
32
+ -ms-grid-rows: 1fr 1fr;
33
+ }
34
+ }
35
+
36
+ a.link-item {
37
+ display: block;
38
+ padding: 20px;
39
+ border: 1px solid #00000010;
40
+ border-radius: 5px;
41
+ transition: background 150ms ease-out, border 150ms ease-out, transform 150ms ease-out;
42
+ color: #333333;
43
+ display: flex;
44
+ align-items: flex-start;
45
+ }
46
+
47
+ a.link-item:hover {
48
+ border-color: #1EA7FD50;
49
+ transform: translate3d(0, -3px, 0);
50
+ box-shadow: rgba(0, 0, 0, 0.08) 0 3px 10px 0;
51
+ }
52
+
53
+ a.link-item:active {
54
+ border-color: #1EA7FD;
55
+ transform: translate3d(0, 0, 0);
56
+ }
57
+
58
+ a.link-item strong {
59
+ font-weight: 700;
60
+ display: block;
61
+ margin-bottom: 2px;
62
+ }
63
+
64
+ a.link-item img {
65
+ height: 40px;
66
+ width: 40px;
67
+ margin-right: 15px;
68
+ flex: none;
69
+ }
70
+
71
+ a.link-item span,
72
+ a.link-item p {
73
+ margin: 0;
74
+ font-size: 14px;
75
+ line-height: 20px;
76
+ }
77
+
78
+ span.badge {
79
+ display: inline-block;
80
+ border-radius: 1em;
81
+ font-size: 11px;
82
+ line-height: 12px;
83
+ font-weight: 700;
84
+ background: #E7FDD8;
85
+ color: #66BF3C;
86
+ padding: 4px 12px;
87
+ margin-right: 10px;
88
+ vertical-align: top;
89
+ }
90
+
91
+ div.badge-wrapper {
92
+ font-size: 13px;
93
+ line-height: 20px;
94
+ margin-top: 40px;
95
+ margin-bottom: 40px;
96
+ }
97
+
98
+ div.badge-wrapper code {
99
+ font-size: 12px;
100
+ display: inline-block;
101
+ }
102
+
103
+
104
+ .badge.tip {
105
+ background: #E7FDD8;
106
+ color: #66BF3C;
107
+ }
108
+
109
+ .badge.note {
110
+ background: #d8e8fd;
111
+ color: #3c70bf;
112
+ }
@@ -0,0 +1,26 @@
1
+ @import '@constructor-io/constructorio-ui-components/styles.css';
2
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap');
3
+ @import './components/SuggestedQuestion/SuggestedQuestionStyles.css';
4
+ @import './components/SuggestedQuestionsContainer/SuggestedQuestionsContainerStyles.css';
5
+ @import './components/Input/InputStyles.css';
6
+ @import './components/Feedback/FeedbackStyles.css';
7
+ @import './components/Answer/AnswerStyles.css';
8
+ @import './components/Error/ErrorStyles.css';
9
+ @import './components/LoadingSkeleton/LoadingSkeletonStyles.css';
10
+ @import './components/CioPia/CioPiaStyles.css';
11
+ @import './components/ConversationHistory/ConversationHistoryStyles.css';
12
+ @import './components/PiaConversation/PiaConversationStyles.css';
13
+
14
+ .cio-pia-container {
15
+ font-family: 'Inter', sans-serif;
16
+ --cio-pia-chat-question-bg: #f0f0f5;
17
+ --cio-pia-chat-answer-bg: #f8f8fa;
18
+ --cio-pia-conversation-max-height: 600px;
19
+ --cio-pia-modal-height: 80vh;
20
+ --cio-pia-modal-max-width: 640px;
21
+ --cio-pia-modal-min-width: 320px;
22
+ }
23
+
24
+ .cio-pia-container * {
25
+ font-family: inherit;
26
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FeedbackType = void 0;
4
+ const tslib_1 = require("tslib");
5
+ var FeedbackType;
6
+ (function (FeedbackType) {
7
+ FeedbackType["UP"] = "up";
8
+ FeedbackType["DOWN"] = "down";
9
+ })(FeedbackType = exports.FeedbackType || (exports.FeedbackType = {}));
10
+ tslib_1.__exportStar(require("./hooks/mocks/types"), exports);
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./styleHelpers"), exports);
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPreferredColorScheme = void 0;
4
+ // eslint-disable-next-line import/prefer-default-export
5
+ function getPreferredColorScheme() {
6
+ var _a;
7
+ let colorScheme = 'light';
8
+ // Check if the dark-mode Media-Query matches
9
+ if ((_a = window.matchMedia('(prefers-color-scheme: dark)')) === null || _a === void 0 ? void 0 : _a.matches) {
10
+ colorScheme = 'dark';
11
+ }
12
+ return colorScheme;
13
+ }
14
+ exports.getPreferredColorScheme = getPreferredColorScheme;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformResultItem = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Converts a raw ApiItem from the Get Answers API response into an Item object
7
+ * that can be used in the Carousel component.
8
+ */
9
+ function transformResultItem(resultItem) {
10
+ const { value, matched_terms: matchedTerms, data } = resultItem, otherFields = tslib_1.__rest(resultItem, ["value", "matched_terms", "data"]);
11
+ if (!data || typeof data !== 'object' || !data.id || !value) {
12
+ return null;
13
+ }
14
+ const { id, variation_id: variationId, description, url, image_url: imageUrl, price, sale_price: salePrice, rating, reviews_count: reviewsCount, tags, sl_campaign_id: slCampaignId, sl_campaign_owner: slCampaignOwner, badge } = data, otherMetadataFields = tslib_1.__rest(data, ["id", "variation_id", "description", "url", "image_url", "price", "sale_price", "rating", "reviews_count", "tags", "sl_campaign_id", "sl_campaign_owner", "badge"]);
15
+ return Object.assign({ id, name: value, matchedTerms,
16
+ variationId,
17
+ description,
18
+ url,
19
+ imageUrl,
20
+ price,
21
+ salePrice,
22
+ rating,
23
+ reviewsCount,
24
+ tags,
25
+ slCampaignId,
26
+ slCampaignOwner,
27
+ badge, data: Object.assign({}, otherMetadataFields) }, otherFields);
28
+ }
29
+ exports.transformResultItem = transformResultItem;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.translate = void 0;
4
+ const constants_1 = require("../constants");
5
+ const defaultTranslations = {
6
+ 'Any questions about this product?': 'Any questions about this product?',
7
+ 'Ask anything': 'Ask anything',
8
+ Send: 'Send',
9
+ [constants_1.DISCLAIMER_TEXT]: constants_1.DISCLAIMER_TEXT,
10
+ 'Is this answer useful?': 'Is this answer useful?',
11
+ 'Learn More.': 'Learn More.',
12
+ 'Ask about this product': 'Ask about this product',
13
+ };
14
+ /**
15
+ * Translates a word using the provided translations object.
16
+ * Falls back to English defaults if translation is not provided.
17
+ *
18
+ * @param word - The key to translate
19
+ * @param translations - Optional user-provided translations object
20
+ * @returns The translated string or the original word if no translation exists
21
+ */
22
+ // eslint-disable-next-line import/prefer-default-export
23
+ const translate = (word, translations) => {
24
+ if (translations && translations[word] !== undefined) {
25
+ return translations[word];
26
+ }
27
+ return defaultTranslations[word] || word;
28
+ };
29
+ exports.translate = translate;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = '2.1.1';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { RenderPropsWrapper, } from '@constructor-io/constructorio-ui-components';
3
+ function Answer({ text, componentOverride }) {
4
+ if (!text) {
5
+ return null;
6
+ }
7
+ return (React.createElement(RenderPropsWrapper, { props: { text }, override: componentOverride?.reactNode },
8
+ React.createElement("div", { className: 'cio-pia-answer', "data-testid": 'answer-text' }, text)));
9
+ }
10
+ export default Answer;
@@ -0,0 +1,8 @@
1
+ .cio-pia-answer {
2
+ width: 100%;
3
+ font-size: 14px;
4
+ font-style: normal;
5
+ font-weight: 500;
6
+ color: #14151a;
7
+ text-align: start;
8
+ }
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import { RenderPropsWrapper, } from '@constructor-io/constructorio-ui-components';
3
+ import Disclaimer from './Disclaimer';
4
+ import Input from '../Input/Input';
5
+ import SuggestedQuestionsContainer from '../SuggestedQuestionsContainer/SuggestedQuestionsContainer';
6
+ import Answer from '../Answer/Answer';
7
+ import Feedback from '../Feedback/Feedback';
8
+ import useCioPia from '../../hooks/useCioPia';
9
+ import useConversation from '../../hooks/useConversation';
10
+ import ErrorBlock from '../Error/ErrorBlock';
11
+ import LoadingSkeleton from '../LoadingSkeleton/LoadingSkeleton';
12
+ import { translate } from '../../utils/translate';
13
+ import PiaCustomCarousel from './PiaCustomCarousel';
14
+ import PiaModal from '../PiaConversation/PiaModal';
15
+ import PiaConversation from '../PiaConversation/PiaConversation';
16
+ export default function CioPia(props) {
17
+ const { apiKey, itemId, threadId, variationId, cioClient, displayConfigs, componentOverrides, callbacks, children, translations, suggestedQuestionsParameters, } = props;
18
+ const { learnMoreUrl, showFeedback, mode = 'default', type = 'inline' } = displayConfigs || {};
19
+ const isConversation = mode === 'conversation' || type === 'modal';
20
+ const pia = useCioPia({
21
+ apiKey,
22
+ itemId,
23
+ threadId,
24
+ variationId,
25
+ cioClient,
26
+ suggestedQuestionsParameters,
27
+ });
28
+ const { currentQuestion, displayedQuestions, conversationHistory, currentAnswer, currentItems, isLoading, error, handleSubmitQuestion, resetState, } = useConversation({ pia, itemId, isConversation });
29
+ const renderProps = {
30
+ items: currentItems,
31
+ isLoading,
32
+ error,
33
+ currentAnswer,
34
+ currentQuestion,
35
+ displayedQuestions,
36
+ handleSubmitQuestion,
37
+ conversationHistory,
38
+ };
39
+ const conversationHistoryProps = {
40
+ conversationHistory,
41
+ isLoading,
42
+ error,
43
+ currentItems,
44
+ showFeedback,
45
+ learnMoreUrl,
46
+ translations,
47
+ callbacks,
48
+ componentOverrides,
49
+ displayedQuestions,
50
+ handleSubmitQuestion,
51
+ };
52
+ if (type === 'modal') {
53
+ return (React.createElement(PiaModal, { initialQuestions: pia.suggestedQuestions.data, handleSubmitQuestion: handleSubmitQuestion, isLoading: isLoading, componentOverrides: componentOverrides, translations: translations, onClose: resetState },
54
+ React.createElement(PiaConversation, { ...conversationHistoryProps })));
55
+ }
56
+ if (isConversation)
57
+ return React.createElement(PiaConversation, { ...conversationHistoryProps });
58
+ // Default inline mode
59
+ return (React.createElement("div", { className: 'cio-pia-container', "data-testid": 'cio-pia-container' },
60
+ React.createElement(RenderPropsWrapper, { props: renderProps, override: children || componentOverrides?.reactNode },
61
+ React.createElement("p", { className: 'cio-pia-title', "data-testid": 'cio-pia-title' }, translate('Any questions about this product?', translations)),
62
+ React.createElement(Input, { onSubmit: handleSubmitQuestion, value: currentQuestion, translations: translations }),
63
+ isLoading && React.createElement(LoadingSkeleton, null),
64
+ !isLoading && error && React.createElement(ErrorBlock, { message: error?.message || 'Unexpected error' }),
65
+ !isLoading && !error && (React.createElement(React.Fragment, null,
66
+ currentAnswer && (React.createElement("div", { className: 'cio-pia-answer-container' },
67
+ React.createElement(Answer, { text: currentAnswer, componentOverride: componentOverrides?.answer }),
68
+ currentItems && (React.createElement(PiaCustomCarousel, { items: currentItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks })),
69
+ showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: callbacks?.onFeedback, componentOverride: componentOverrides?.feedback })),
70
+ React.createElement(Disclaimer, { learnMoreUrl: learnMoreUrl, translations: translations, componentOverride: componentOverrides?.disclaimer }))),
71
+ React.createElement(SuggestedQuestionsContainer, { questions: displayedQuestions, onQuestionClick: handleSubmitQuestion, componentOverride: componentOverrides?.suggestedQuestions }))))));
72
+ }
@@ -0,0 +1,32 @@
1
+ .cio-pia-container {
2
+ width: 100%;
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 24px;
6
+ }
7
+
8
+ .cio-pia-title {
9
+ color: #14151a;
10
+ font-size: 18px;
11
+ font-weight: 400;
12
+ text-align: start;
13
+ margin-top: 0;
14
+ margin-bottom: 0;
15
+ }
16
+
17
+ .cio-pia-answer-container {
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: 8px;
21
+ }
22
+
23
+ .cio-pia-disclaimer {
24
+ font-size: 12px;
25
+ font-weight: 400;
26
+ text-align: start;
27
+ color: rgba(15, 19, 36, 0.7);
28
+ }
29
+
30
+ .cio-pia-learn-more {
31
+ color: inherit;
32
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { RenderPropsWrapper, } from '@constructor-io/constructorio-ui-components';
3
+ import { translate } from '../../utils/translate';
4
+ import { DISCLAIMER_TEXT } from '../../constants';
5
+ export default function Disclaimer({ learnMoreUrl, translations, componentOverride, }) {
6
+ return (React.createElement(RenderPropsWrapper, { props: { learnMoreUrl, translations }, override: componentOverride?.reactNode },
7
+ React.createElement("span", { className: 'cio-pia-disclaimer' },
8
+ translate(DISCLAIMER_TEXT, translations),
9
+ ' ',
10
+ learnMoreUrl && (React.createElement("a", { href: learnMoreUrl, target: '_blank', rel: 'noopener noreferrer', className: 'cio-pia-learn-more' },
11
+ React.createElement("u", null, translate('Learn More.', translations)))))));
12
+ }
@@ -0,0 +1,37 @@
1
+ import React, { useCallback, useRef, useEffect } from 'react';
2
+ import { Carousel, CIO_EVENTS, } from '@constructor-io/constructorio-ui-components';
3
+ export default function PiaCustomCarousel({ items, componentOverrides, callbacks, }) {
4
+ const wrapperRef = useRef(null);
5
+ // Determine to use user-defined click handler or default behavior
6
+ const productClickHandler = useCallback((item) => {
7
+ const { onProductCardClick } = callbacks || {};
8
+ if (onProductCardClick) {
9
+ onProductCardClick(item);
10
+ }
11
+ else if (item?.url) {
12
+ window.open(item.url, '_blank', 'noopener,noreferrer');
13
+ }
14
+ }, [callbacks]);
15
+ // Set up event listener for product card clicks
16
+ useEffect(() => {
17
+ const el = wrapperRef.current;
18
+ if (!el)
19
+ return undefined;
20
+ const handleClick = (e) => {
21
+ const { product } = e.detail;
22
+ if (product) {
23
+ productClickHandler(product);
24
+ }
25
+ };
26
+ el.addEventListener(CIO_EVENTS.productCard.click, handleClick);
27
+ return () => {
28
+ el.removeEventListener(CIO_EVENTS.productCard.click, handleClick);
29
+ };
30
+ }, [productClickHandler]);
31
+ // If there are no items, do not render the carousel
32
+ if (items.length === 0) {
33
+ return null;
34
+ }
35
+ return (React.createElement("div", { ref: wrapperRef },
36
+ React.createElement(Carousel, { items: items, componentOverrides: componentOverrides })));
37
+ }
@@ -0,0 +1,3 @@
1
+ import CioPia from './CioPia';
2
+ export * from './CioPia';
3
+ export default CioPia;
@@ -0,0 +1,32 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import Answer from '../Answer/Answer';
3
+ import Feedback from '../Feedback/Feedback';
4
+ import Disclaimer from '../CioPia/Disclaimer';
5
+ import ErrorBlock from '../Error/ErrorBlock';
6
+ import LoadingSkeleton from '../LoadingSkeleton/LoadingSkeleton';
7
+ import PiaCustomCarousel from '../CioPia/PiaCustomCarousel';
8
+ export default function ConversationHistory({ conversationHistory, isLoading, error, currentItems, showFeedback, learnMoreUrl, translations, callbacks, componentOverrides, }) {
9
+ const scrollContainerRef = useRef(null);
10
+ useEffect(() => {
11
+ const container = scrollContainerRef.current;
12
+ if (!container)
13
+ return undefined;
14
+ const frameId = requestAnimationFrame(() => {
15
+ container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
16
+ });
17
+ return () => cancelAnimationFrame(frameId);
18
+ }, [conversationHistory, isLoading]);
19
+ return (React.createElement("div", { ref: scrollContainerRef, className: 'cio-pia-conversation-history', role: 'log', "aria-label": 'Conversation history' }, conversationHistory.map((entry, index) => {
20
+ const isLast = index === conversationHistory.length - 1;
21
+ return (React.createElement("div", { key: entry.id, className: 'cio-pia-conversation-entry' },
22
+ React.createElement("div", { className: 'cio-pia-chat-question' }, entry.question),
23
+ isLast && isLoading && (React.createElement("div", { className: 'cio-pia-conversation-loading', "aria-live": 'polite' },
24
+ React.createElement(LoadingSkeleton, null))),
25
+ isLast && !isLoading && error && (React.createElement(ErrorBlock, { message: error.message || 'Unexpected error' })),
26
+ entry.answer && (React.createElement("div", { className: 'cio-pia-answer-container' },
27
+ React.createElement(Answer, { text: entry.answer, componentOverride: componentOverrides?.answer }),
28
+ isLast && currentItems && (React.createElement(PiaCustomCarousel, { items: currentItems, componentOverrides: componentOverrides?.carousel, callbacks: callbacks })),
29
+ isLast && showFeedback && (React.createElement(Feedback, { translations: translations, onFeedback: callbacks?.onFeedback, componentOverride: componentOverrides?.feedback })))),
30
+ isLast && entry.answer && (React.createElement(Disclaimer, { learnMoreUrl: learnMoreUrl, translations: translations, componentOverride: componentOverrides?.disclaimer }))));
31
+ })));
32
+ }
@@ -0,0 +1,51 @@
1
+ .cio-pia-conversation-history {
2
+ --conversation-gap: 16px;
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--conversation-gap);
6
+ overflow-y: auto;
7
+ flex: 1;
8
+ min-height: 0;
9
+ }
10
+
11
+ .cio-pia-conversation-entry {
12
+ display: flex;
13
+ flex-direction: column;
14
+ gap: var(--conversation-gap);
15
+ }
16
+
17
+ .cio-pia-chat-question {
18
+ align-self: flex-end;
19
+ background: var(--cio-pia-chat-question-bg);
20
+ border-radius: 12px 12px 12px 12px;
21
+ padding: 8px 12px;
22
+ font-size: 14px;
23
+ font-weight: 500;
24
+ color: #14151a;
25
+ max-width: 80%;
26
+ text-align: start;
27
+ word-break: break-word;
28
+ transition: border-radius 0.3s ease;
29
+ }
30
+
31
+ .cio-pia-conversation-entry .cio-pia-answer-container {
32
+ align-self: flex-start;
33
+ background: var(--cio-pia-chat-answer-bg);
34
+ border-radius: 12px 12px 12px 12px;
35
+ padding: 8px 12px;
36
+ max-width: 80%;
37
+ transition: border-radius 0.3s ease;
38
+ }
39
+
40
+ .cio-pia-conversation-entry:last-child .cio-pia-chat-question {
41
+ border-bottom-right-radius: 2px;
42
+ }
43
+
44
+ .cio-pia-conversation-entry:last-child .cio-pia-answer-container {
45
+ border-bottom-left-radius: 2px;
46
+ }
47
+
48
+ .cio-pia-conversation-loading {
49
+ align-self: flex-start;
50
+ max-width: 80%;
51
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ function WarningIcon() {
3
+ return (React.createElement("svg", { width: '24', height: '24', viewBox: '0 0 24 24', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
4
+ React.createElement("path", { d: 'M12.0004 2C6.4804 2 2.0004 6.48 2.0004 12C2.0004 17.52 6.4804 22 12.0004 22C17.5204 22 22.0004 17.52 22.0004 12C22.0004 6.48 17.5204 2 12.0004 2ZM13.0004 17H11.0004V15H13.0004V17ZM13.0004 13H11.0004V7H13.0004V13Z', fill: '#F1923A' })));
5
+ }
6
+ function RetryIcon() {
7
+ return (React.createElement("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' },
8
+ React.createElement("path", { d: 'M11.7633 4.23329C10.7966 3.26663 9.46992 2.66663 7.99659 2.66663C5.04992 2.66663 2.66992 5.05329 2.66992 7.99996C2.66992 10.9466 5.04992 13.3333 7.99659 13.3333C10.4833 13.3333 12.5566 11.6333 13.1499 9.33329H11.7633C11.2166 10.8866 9.73659 12 7.99659 12C5.78992 12 3.99659 10.2066 3.99659 7.99996C3.99659 5.79329 5.78992 3.99996 7.99659 3.99996C9.10326 3.99996 10.0899 4.45996 10.8099 5.18663L8.66326 7.33329H13.3299V2.66663L11.7633 4.23329Z', fill: 'white' })));
9
+ }
10
+ function ErrorBlock({ message, onRetry = undefined }) {
11
+ return (React.createElement("div", { className: 'cio-pia-error-block-container', "data-testid": 'error-block' },
12
+ React.createElement("div", { className: 'cio-pia-error-block-warning-icon-container', "data-testid": 'error-block-warning-icon' },
13
+ React.createElement(WarningIcon, null)),
14
+ React.createElement("div", { className: 'cio-pia-error-block-text-container' },
15
+ React.createElement("p", { className: 'cio-pia-error-block-text' }, message),
16
+ onRetry && (React.createElement("button", { type: 'button', onClick: onRetry, className: 'cio-pia-error-block-retry-button' },
17
+ React.createElement(RetryIcon, null),
18
+ "Retry")))));
19
+ }
20
+ export default ErrorBlock;
@@ -0,0 +1,46 @@
1
+ .cio-pia-error-block-container {
2
+ width: 100%;
3
+ max-width: 320px;
4
+ display: flex;
5
+ align-items: center;
6
+ gap: 12px;
7
+ background-color: #fef8f4;
8
+ border-radius: 6px;
9
+ padding: 12px;
10
+ border: 1px solid #f4e3db;
11
+ font-weight: 400;
12
+ font-size: 14px;
13
+ }
14
+
15
+ .cio-pia-error-block-warning-icon-container {
16
+ flex: 0 0 24px;
17
+ width: 24px;
18
+ height: 24px;
19
+ }
20
+
21
+ .cio-pia-error-block-text-container {
22
+ display: flex;
23
+ flex-direction: column;
24
+ margin: 4px 0px;
25
+ }
26
+
27
+ .cio-pia-error-block-text {
28
+ margin: 0.5rem 0rem;
29
+ overflow: hidden;
30
+ text-overflow: ellipsis;
31
+ }
32
+
33
+ .cio-pia-error-block-retry-button {
34
+ width: 76px;
35
+ display: inline-flex;
36
+ align-items: center;
37
+ background-color: #121212;
38
+ color: #fff;
39
+ border: none;
40
+ padding: 6px 10px;
41
+ border-radius: 4px;
42
+ cursor: pointer;
43
+ gap: 4px;
44
+ font-size: 14px;
45
+ font-weight: 500;
46
+ }
@@ -0,0 +1,24 @@
1
+ import React, { useState } from 'react';
2
+ import { RenderPropsWrapper, } from '@constructor-io/constructorio-ui-components';
3
+ import { FeedbackType } from '../../types';
4
+ import { translate } from '../../utils/translate';
5
+ function ThumbsUpIcon({ isSelected }) {
6
+ return (React.createElement("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' }, isSelected ? (React.createElement("path", { d: 'M3.33317 6.66667H0.666504V14.6667H3.33317V6.66667ZM5.99984 14.6667C5.2665 14.6667 4.6665 14.0667 4.6665 13.3333V6.66667C4.6665 6.3 4.81317 5.96667 5.05317 5.72667L9.4465 1.33333L10.1532 2.03333C10.3332 2.21333 10.4465 2.46667 10.4465 2.74L10.4265 2.95333L9.79317 6.00001H13.9998C14.7332 6.00001 15.3332 6.60001 15.3332 7.33334V8.66668C15.3332 8.84001 15.2998 9.00001 15.2398 9.15334L13.2265 13.8533C13.0265 14.3333 12.5532 14.6667 11.9998 14.6667H5.99984Z', fill: '#4CAF50', fillOpacity: '1' })) : (React.createElement("path", { d: 'M5.99984 14.6667H11.9998C12.5532 14.6667 13.0265 14.3333 13.2265 13.8533L15.2398 9.15334C15.2998 9.00001 15.3332 8.84001 15.3332 8.66668V7.33334C15.3332 6.60001 14.7332 6.00001 13.9998 6.00001H9.79317L10.4265 2.95334L10.4465 2.74001C10.4465 2.46668 10.3332 2.21334 10.1532 2.03334L9.4465 1.33334L5.05317 5.72668C4.81317 5.96668 4.6665 6.30001 4.6665 6.66668V13.3333C4.6665 14.0667 5.2665 14.6667 5.99984 14.6667ZM5.99984 6.66668L8.89317 3.77334L7.99984 7.33334H13.9998V8.66668L11.9998 13.3333H5.99984V6.66668ZM0.666504 6.66668H3.33317V14.6667H0.666504V6.66668Z', fill: '#0F1324', fillOpacity: '0.6' }))));
7
+ }
8
+ function ThumbsDownIcon({ isSelected }) {
9
+ return (React.createElement("svg", { width: '16', height: '16', viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg' }, isSelected ? (React.createElement("path", { d: 'M12.6665 9.33334H15.3332V1.33334H12.6665V9.33334ZM9.99984 1.33334C10.7332 1.33334 11.3332 1.93334 11.3332 2.66668V9.33334C11.3332 9.70001 11.1865 10.0333 10.9465 10.2733L6.55317 14.6667L5.8465 13.9667C5.6665 13.7867 5.55317 13.5333 5.55317 13.26L5.57317 13.0467L6.2065 10H1.99984C1.2665 10 0.666504 9.40001 0.666504 8.66668V7.33334C0.666504 7.16001 0.699837 7.00001 0.759837 6.84668L2.77317 2.14668C2.97317 1.66668 3.4465 1.33334 3.99984 1.33334H9.99984Z', fill: '#F44336', fillOpacity: '1' })) : (React.createElement("path", { d: 'M9.99984 1.33334H3.99984C3.4465 1.33334 2.97317 1.66668 2.77317 2.14668L0.759837 6.84668C0.699837 7.00001 0.666504 7.16001 0.666504 7.33334V8.66668C0.666504 9.40001 1.2665 10 1.99984 10H6.2065L5.57317 13.0467L5.55317 13.26C5.55317 13.5333 5.6665 13.7867 5.8465 13.9667L6.55317 14.6667L10.9465 10.2733C11.1865 10.0333 11.3332 9.70001 11.3332 9.33334V2.66668C11.3332 1.93334 10.7332 1.33334 9.99984 1.33334ZM9.99984 9.33334L7.1065 12.2267L7.99984 8.66668H1.99984V7.33334L3.99984 2.66668H9.99984V9.33334ZM12.6665 1.33334H15.3332V9.33334H12.6665V1.33334Z', fill: '#0F1324', fillOpacity: '0.6' }))));
10
+ }
11
+ export default function Feedback({ translations, onFeedback, componentOverride }) {
12
+ const [feedback, setFeedback] = useState(null);
13
+ const handleFeedback = (type) => {
14
+ setFeedback(type);
15
+ onFeedback?.(type);
16
+ };
17
+ return (React.createElement(RenderPropsWrapper, { props: { translations, onFeedback }, override: componentOverride?.reactNode },
18
+ React.createElement("div", { className: 'cio-pia-feedback-container' },
19
+ React.createElement("p", { className: 'cio-pia-feedback-text' }, translate('Is this answer useful?', translations)),
20
+ React.createElement("button", { type: 'button', className: 'cio-pia-feedback-button', "aria-label": 'thumbs up', onClick: () => handleFeedback(FeedbackType.UP) },
21
+ React.createElement(ThumbsUpIcon, { isSelected: feedback === FeedbackType.UP })),
22
+ React.createElement("button", { type: 'button', className: 'cio-pia-feedback-button', "aria-label": 'thumbs down', onClick: () => handleFeedback(FeedbackType.DOWN) },
23
+ React.createElement(ThumbsDownIcon, { isSelected: feedback === FeedbackType.DOWN })))));
24
+ }