@constructor-io/constructorio-ui-pia 2.1.0 → 2.1.2

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 +12 -12
  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 +15 -4
@@ -0,0 +1,138 @@
1
+ // Create URL for PIA API
2
+ function createAgentUrl({ itemId, threadId, variationId, question, isStreaming = false, options, parameters = {}, }) {
3
+ const { apiKey, agentServiceUrl } = options;
4
+ if (!agentServiceUrl)
5
+ throw new Error('Agent service URL is required');
6
+ let baseUrl = `${agentServiceUrl}/v1/item_questions`;
7
+ if (question) {
8
+ baseUrl += `/${encodeURIComponent(question)}/answer`;
9
+ }
10
+ if (isStreaming) {
11
+ baseUrl += '/streaming';
12
+ }
13
+ const url = new URL(baseUrl);
14
+ url.searchParams.append('item_id', itemId);
15
+ url.searchParams.append('key', apiKey);
16
+ if (threadId) {
17
+ url.searchParams.append('thread_id', threadId);
18
+ }
19
+ if (variationId) {
20
+ url.searchParams.append('variation_id', variationId);
21
+ }
22
+ // Any additional parameters
23
+ Object.entries(parameters).forEach(([key, value]) => {
24
+ if (value !== undefined) {
25
+ url.searchParams.append(key, String(value));
26
+ }
27
+ });
28
+ return url.toString();
29
+ }
30
+ // Map camelCase SuggestedQuestionsParameters to snake_case query params expected by the API
31
+ function mapSuggestedQuestionsParams(params) {
32
+ const result = {};
33
+ if (params.numResults !== undefined)
34
+ result.num_results = params.numResults;
35
+ return result;
36
+ }
37
+ class MockAgent {
38
+ options;
39
+ constructor(options) {
40
+ this.options = options;
41
+ }
42
+ async getSuggestedQuestions({ itemId, variationId, threadId, parameters = {}, }) {
43
+ if (!itemId)
44
+ throw new Error('Item ID is required');
45
+ if (!this.options.apiKey)
46
+ throw new Error('API key is required');
47
+ const url = createAgentUrl({
48
+ itemId,
49
+ variationId,
50
+ threadId,
51
+ options: this.options,
52
+ parameters: mapSuggestedQuestionsParams(parameters),
53
+ });
54
+ try {
55
+ const response = await fetch(url);
56
+ if (!response.ok)
57
+ throw new Error(`Request failed with status ${response.status}`);
58
+ const data = await response.json();
59
+ return data;
60
+ }
61
+ catch (error) {
62
+ const errorMessage = error instanceof Error ? error.message : String(error);
63
+ throw new Error(errorMessage);
64
+ }
65
+ }
66
+ async getAnswerResults({ itemId, variationId, threadId, question, parameters = {}, }) {
67
+ if (!itemId)
68
+ throw new Error('Item ID is required');
69
+ if (!question)
70
+ throw new Error('Question is required');
71
+ if (!this.options.apiKey)
72
+ throw new Error('API key is required');
73
+ const url = createAgentUrl({
74
+ itemId,
75
+ threadId,
76
+ variationId,
77
+ question,
78
+ options: this.options,
79
+ parameters,
80
+ });
81
+ try {
82
+ const response = await fetch(url);
83
+ if (!response.ok)
84
+ throw new Error(`Request failed with status ${response.status}`);
85
+ const data = await response.json();
86
+ return data;
87
+ }
88
+ catch (error) {
89
+ const errorMessage = error instanceof Error ? error.message : String(error);
90
+ throw new Error(errorMessage);
91
+ }
92
+ }
93
+ async getAnswerResultsStream({ itemId, threadId, variationId, question, parameters, onStart, onMessage, onEnd, }) {
94
+ if (!itemId)
95
+ throw new Error('Item ID is required');
96
+ if (!question)
97
+ throw new Error('Question is required');
98
+ if (!this.options.apiKey)
99
+ throw new Error('API key is required');
100
+ const url = createAgentUrl({
101
+ itemId,
102
+ threadId,
103
+ variationId,
104
+ question,
105
+ isStreaming: true,
106
+ options: this.options,
107
+ parameters,
108
+ });
109
+ try {
110
+ const eventSource = new EventSource(url);
111
+ eventSource.addEventListener('open', (event) => {
112
+ const data = JSON.parse(event.data);
113
+ if (onStart)
114
+ onStart(data);
115
+ });
116
+ eventSource.addEventListener('message', (event) => {
117
+ const data = JSON.parse(event.data);
118
+ if (onMessage)
119
+ onMessage(data);
120
+ });
121
+ eventSource.addEventListener('end', (event) => {
122
+ const data = JSON.parse(event.data);
123
+ if (onEnd)
124
+ onEnd(data);
125
+ eventSource.close();
126
+ });
127
+ eventSource.onerror = () => {
128
+ eventSource.close();
129
+ throw new Error('Unexpected error occurred. Please try again.');
130
+ };
131
+ }
132
+ catch (error) {
133
+ const errorMessage = error instanceof Error ? error.message : String(error);
134
+ throw new Error(errorMessage);
135
+ }
136
+ }
137
+ }
138
+ export default MockAgent;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { transformResultItem } from '../utils/transformers';
3
+ const extractAndTransformItems = (data) => {
4
+ if (!data?.item_results?.response?.results) {
5
+ return null;
6
+ }
7
+ const { results } = data.item_results.response;
8
+ if (!Array.isArray(results) || results.length === 0) {
9
+ return null;
10
+ }
11
+ const transformedItems = results
12
+ .map(transformResultItem)
13
+ .filter((item) => item !== null);
14
+ return transformedItems.length > 0 ? transformedItems : null;
15
+ };
16
+ const fetchAnswerResults = async ({ client, itemId, question, variationId, threadId, }) => {
17
+ const response = await client.agent.getAnswerResults({
18
+ itemId,
19
+ variationId,
20
+ threadId,
21
+ question,
22
+ });
23
+ return response;
24
+ };
25
+ export default function useAnswerResults({ itemId, variationId, threadId, cioClient, }) {
26
+ const [answerResults, setAnswerResults] = useState(null);
27
+ const [items, setItems] = useState(null);
28
+ const [isLoading, setIsLoading] = useState(false);
29
+ const [error, setError] = useState(null);
30
+ const fetchResult = useCallback((question) => {
31
+ if (!cioClient)
32
+ return;
33
+ setIsLoading(true);
34
+ setError(null);
35
+ fetchAnswerResults({ client: cioClient, itemId, question, variationId, threadId })
36
+ .then((fetchedAnswerResults) => {
37
+ setAnswerResults(fetchedAnswerResults);
38
+ setItems(extractAndTransformItems(fetchedAnswerResults));
39
+ setError(null);
40
+ })
41
+ .catch((err) => {
42
+ setError(err instanceof Error ? err : new Error('Error fetching answer'));
43
+ setAnswerResults(null);
44
+ setItems(null);
45
+ })
46
+ .finally(() => {
47
+ setIsLoading(false);
48
+ });
49
+ }, [cioClient, itemId, variationId, threadId]);
50
+ return {
51
+ data: answerResults,
52
+ items,
53
+ isLoading,
54
+ error,
55
+ getAnswer: fetchResult,
56
+ };
57
+ }
@@ -0,0 +1,23 @@
1
+ import { useMemo } from 'react';
2
+ import version from '../version';
3
+ import MockConstructorIOClient from './mocks/MockConstructorIOClient';
4
+ const useCioClient = ({ apiKey, cioClient, options } = {}) => {
5
+ if (!apiKey && !cioClient) {
6
+ throw new Error('Api Key or Constructor Client required');
7
+ }
8
+ const memoizedCioClient = useMemo(() => {
9
+ if (cioClient)
10
+ return cioClient;
11
+ if (apiKey) {
12
+ return new MockConstructorIOClient({
13
+ apiKey,
14
+ sendTrackingEvents: true,
15
+ version: `cio-ui-pia-${version}`,
16
+ ...options,
17
+ });
18
+ }
19
+ return null;
20
+ }, [apiKey, cioClient, options]);
21
+ return memoizedCioClient;
22
+ };
23
+ export default useCioClient;
@@ -0,0 +1,25 @@
1
+ import useAnswerResults from './useAnswerResults';
2
+ import useCioClient from './useCioClient';
3
+ import useSuggestedQuestions from './useSuggestedQuestions';
4
+ export default function useCioPia(props) {
5
+ const { apiKey, itemId, variationId, threadId, cioClient: providedClient, suggestedQuestionsParameters, } = props;
6
+ const defaultClient = useCioClient({ apiKey });
7
+ const client = providedClient || defaultClient;
8
+ const suggestedQuestions = useSuggestedQuestions({
9
+ itemId,
10
+ variationId,
11
+ threadId,
12
+ cioClient: client,
13
+ parameters: suggestedQuestionsParameters,
14
+ });
15
+ const answers = useAnswerResults({
16
+ itemId,
17
+ variationId,
18
+ threadId,
19
+ cioClient: client,
20
+ });
21
+ return {
22
+ suggestedQuestions,
23
+ answers,
24
+ };
25
+ }
@@ -0,0 +1,71 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ export default function useConversation({ pia, itemId, isConversation, }) {
3
+ const { suggestedQuestions, answers } = pia;
4
+ const { getAnswer } = answers;
5
+ const [currentQuestion, setCurrentQuestion] = useState('');
6
+ const [displayedQuestions, setDisplayedQuestions] = useState([]);
7
+ const [conversationHistory, setConversationHistory] = useState([]);
8
+ const entryIdRef = useRef(0);
9
+ const prevAnswerValueRef = useRef(answers.data?.value);
10
+ const handleSubmitQuestion = useCallback((question) => {
11
+ setCurrentQuestion(question);
12
+ getAnswer(question);
13
+ if (isConversation) {
14
+ entryIdRef.current += 1;
15
+ const id = entryIdRef.current;
16
+ setConversationHistory((prev) => [...prev, { id, question, answer: '' }]);
17
+ }
18
+ }, [getAnswer, isConversation]);
19
+ const resetState = useCallback(() => {
20
+ setCurrentQuestion('');
21
+ setDisplayedQuestions(suggestedQuestions.data);
22
+ setConversationHistory([]);
23
+ prevAnswerValueRef.current = undefined;
24
+ }, [suggestedQuestions.data]);
25
+ useEffect(() => {
26
+ setCurrentQuestion('');
27
+ setDisplayedQuestions([]);
28
+ setConversationHistory([]);
29
+ prevAnswerValueRef.current = undefined;
30
+ }, [itemId]);
31
+ useEffect(() => {
32
+ setDisplayedQuestions(suggestedQuestions.data);
33
+ }, [suggestedQuestions.data]);
34
+ useEffect(() => {
35
+ if (answers.data?.follow_up_questions)
36
+ setDisplayedQuestions(answers.data.follow_up_questions);
37
+ }, [answers.data]);
38
+ useEffect(() => {
39
+ const answerValue = answers.data?.value ?? '';
40
+ if (!isConversation || !answerValue)
41
+ return;
42
+ if (answerValue === prevAnswerValueRef.current)
43
+ return;
44
+ prevAnswerValueRef.current = answerValue;
45
+ setConversationHistory((prev) => {
46
+ if (prev.length === 0)
47
+ return prev;
48
+ const updated = [...prev];
49
+ updated[updated.length - 1] = {
50
+ ...updated[updated.length - 1],
51
+ answer: answerValue,
52
+ };
53
+ return updated;
54
+ });
55
+ }, [isConversation, answers.data]);
56
+ const currentAnswer = answers.data?.value ?? '';
57
+ const currentItems = answers.items ?? null;
58
+ const error = answers.error || suggestedQuestions.error;
59
+ const isLoading = answers.isLoading || suggestedQuestions.isLoading;
60
+ return {
61
+ currentQuestion,
62
+ displayedQuestions,
63
+ conversationHistory,
64
+ currentAnswer,
65
+ currentItems,
66
+ isLoading,
67
+ error,
68
+ handleSubmitQuestion,
69
+ resetState,
70
+ };
71
+ }
@@ -0,0 +1,48 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ const fetchSuggestedQuestions = async ({ client, itemId, variationId, threadId, parameters, }) => {
3
+ const response = await client.agent.getSuggestedQuestions({
4
+ itemId,
5
+ variationId,
6
+ threadId,
7
+ parameters,
8
+ });
9
+ return response.questions;
10
+ };
11
+ export default function useSuggestedQuestions({ itemId, variationId, threadId, cioClient, parameters, }) {
12
+ const [questions, setQuestions] = useState([]);
13
+ const [isLoading, setIsLoading] = useState(false);
14
+ const [error, setError] = useState(null);
15
+ const fetchResult = useCallback(() => {
16
+ if (!cioClient)
17
+ return;
18
+ setIsLoading(true);
19
+ setError(null);
20
+ fetchSuggestedQuestions({
21
+ client: cioClient,
22
+ itemId,
23
+ variationId,
24
+ threadId,
25
+ parameters,
26
+ })
27
+ .then((fetchedQuestions) => {
28
+ setQuestions(fetchedQuestions);
29
+ setError(null);
30
+ })
31
+ .catch((err) => {
32
+ setError(err instanceof Error ? err : new Error('Error fetching questions'));
33
+ })
34
+ .finally(() => {
35
+ setIsLoading(false);
36
+ });
37
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- primitive dep prevents refetch on object reference change
38
+ }, [cioClient, itemId, variationId, threadId, parameters?.numResults]);
39
+ useEffect(() => {
40
+ fetchResult();
41
+ }, [fetchResult]);
42
+ return {
43
+ data: questions,
44
+ isLoading,
45
+ error,
46
+ getSuggestedQuestions: fetchResult,
47
+ };
48
+ }
@@ -0,0 +1,6 @@
1
+ import CioPia from './components/CioPia';
2
+ // Components
3
+ export { default as CioPia } from './components/CioPia';
4
+ // Default
5
+ export * from './types';
6
+ export default CioPia;
@@ -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,6 @@
1
+ export var FeedbackType;
2
+ (function (FeedbackType) {
3
+ FeedbackType["UP"] = "up";
4
+ FeedbackType["DOWN"] = "down";
5
+ })(FeedbackType || (FeedbackType = {}));
6
+ export * from './hooks/mocks/types';
@@ -0,0 +1 @@
1
+ export * from './styleHelpers';
@@ -0,0 +1,9 @@
1
+ // eslint-disable-next-line import/prefer-default-export
2
+ export function getPreferredColorScheme() {
3
+ let colorScheme = 'light';
4
+ // Check if the dark-mode Media-Query matches
5
+ if (window.matchMedia('(prefers-color-scheme: dark)')?.matches) {
6
+ colorScheme = 'dark';
7
+ }
8
+ return colorScheme;
9
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Converts a raw ApiItem from the Get Answers API response into an Item object
3
+ * that can be used in the Carousel component.
4
+ */
5
+ export function transformResultItem(resultItem) {
6
+ const { value, matched_terms: matchedTerms, data, ...otherFields } = resultItem;
7
+ if (!data || typeof data !== 'object' || !data.id || !value) {
8
+ return null;
9
+ }
10
+ 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, ...otherMetadataFields } = data;
11
+ return {
12
+ id,
13
+ name: value,
14
+ matchedTerms,
15
+ variationId,
16
+ description,
17
+ url,
18
+ imageUrl,
19
+ price,
20
+ salePrice,
21
+ rating,
22
+ reviewsCount,
23
+ tags,
24
+ slCampaignId,
25
+ slCampaignOwner,
26
+ badge,
27
+ data: { ...otherMetadataFields },
28
+ ...otherFields,
29
+ };
30
+ }
@@ -0,0 +1,25 @@
1
+ import { DISCLAIMER_TEXT } from '../constants';
2
+ const defaultTranslations = {
3
+ 'Any questions about this product?': 'Any questions about this product?',
4
+ 'Ask anything': 'Ask anything',
5
+ Send: 'Send',
6
+ [DISCLAIMER_TEXT]: DISCLAIMER_TEXT,
7
+ 'Is this answer useful?': 'Is this answer useful?',
8
+ 'Learn More.': 'Learn More.',
9
+ 'Ask about this product': 'Ask about this product',
10
+ };
11
+ /**
12
+ * Translates a word using the provided translations object.
13
+ * Falls back to English defaults if translation is not provided.
14
+ *
15
+ * @param word - The key to translate
16
+ * @param translations - Optional user-provided translations object
17
+ * @returns The translated string or the original word if no translation exists
18
+ */
19
+ // eslint-disable-next-line import/prefer-default-export
20
+ export const translate = (word, translations) => {
21
+ if (translations && translations[word] !== undefined) {
22
+ return translations[word];
23
+ }
24
+ return defaultTranslations[word] || word;
25
+ };
@@ -0,0 +1 @@
1
+ export default '2.1.2';
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { ComponentOverrideProps } from '@constructor-io/constructorio-ui-components';
3
+ import { AnswerRenderProps } from '../../types';
4
+ interface AnswerProps {
5
+ text: string;
6
+ componentOverride?: ComponentOverrideProps<AnswerRenderProps>;
7
+ }
8
+ declare function Answer({ text, componentOverride }: AnswerProps): React.JSX.Element | null;
9
+ export default Answer;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { IncludeComponentOverrides, IncludeRenderProps } from '@constructor-io/constructorio-ui-components';
3
+ import MockConstructorIOClient from '../../hooks/mocks/MockConstructorIOClient';
4
+ import { CioPiaRenderProps, CioPiaComponentOverrides, Callbacks, CioPiaDisplayConfigs, Translations, SuggestedQuestionsParameters } from '../../types';
5
+ export interface CioPiaProps extends IncludeRenderProps<CioPiaRenderProps>, IncludeComponentOverrides<CioPiaComponentOverrides> {
6
+ apiKey: string;
7
+ itemId: string;
8
+ /** Thread ID for conversation context. Must be a valid UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") */
9
+ threadId?: string;
10
+ variationId?: string;
11
+ cioClient?: MockConstructorIOClient;
12
+ displayConfigs?: CioPiaDisplayConfigs;
13
+ callbacks?: Callbacks;
14
+ translations?: Translations;
15
+ suggestedQuestionsParameters?: SuggestedQuestionsParameters;
16
+ }
17
+ export default function CioPia(props: CioPiaProps): React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { ComponentOverrideProps } from '@constructor-io/constructorio-ui-components';
3
+ import { Translations, DisclaimerRenderProps } from '../../types';
4
+ export default function Disclaimer({ learnMoreUrl, translations, componentOverride, }: {
5
+ learnMoreUrl?: string;
6
+ translations?: Translations;
7
+ componentOverride?: ComponentOverrideProps<DisclaimerRenderProps>;
8
+ }): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { CarouselOverrides } from '@constructor-io/constructorio-ui-components';
3
+ import { Callbacks, Item } from '../../types';
4
+ interface PiaCustomCarouselProps {
5
+ items: Array<Item>;
6
+ componentOverrides?: CarouselOverrides<Item>;
7
+ callbacks?: Callbacks;
8
+ }
9
+ export default function PiaCustomCarousel({ items, componentOverrides, callbacks, }: PiaCustomCarouselProps): React.JSX.Element | null;
10
+ export {};
@@ -0,0 +1,3 @@
1
+ import CioPia from './CioPia';
2
+ export * from './CioPia';
3
+ export default CioPia;
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import { ConversationEntry, Translations, Item, Callbacks, CioPiaComponentOverrides } from '../../types';
3
+ export interface ConversationHistoryProps {
4
+ conversationHistory: ConversationEntry[];
5
+ isLoading: boolean;
6
+ error: Error | null;
7
+ currentItems?: Item[] | null;
8
+ showFeedback?: boolean;
9
+ learnMoreUrl?: string;
10
+ translations?: Translations;
11
+ callbacks?: Callbacks;
12
+ componentOverrides?: CioPiaComponentOverrides;
13
+ }
14
+ export default function ConversationHistory({ conversationHistory, isLoading, error, currentItems, showFeedback, learnMoreUrl, translations, callbacks, componentOverrides, }: ConversationHistoryProps): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface ErrorBlockProps {
3
+ message: string;
4
+ onRetry?: () => void;
5
+ }
6
+ declare function ErrorBlock({ message, onRetry }: ErrorBlockProps): React.JSX.Element;
7
+ export default ErrorBlock;