srs-heritage-chatbot 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +194 -0
- package/lib/commonjs/assets/chat-icon-mobile.svg +1 -0
- package/lib/commonjs/assets/heritage.png +0 -0
- package/lib/commonjs/assets/posiden.svg +51 -0
- package/lib/commonjs/components/LoadingTips.js +104 -0
- package/lib/commonjs/components/LoadingTips.js.map +1 -0
- package/lib/commonjs/components/email.js +461 -0
- package/lib/commonjs/components/email.js.map +1 -0
- package/lib/commonjs/components/feedback.js +114 -0
- package/lib/commonjs/components/feedback.js.map +1 -0
- package/lib/commonjs/components/header.js +126 -0
- package/lib/commonjs/components/header.js.map +1 -0
- package/lib/commonjs/components/input.js +144 -0
- package/lib/commonjs/components/input.js.map +1 -0
- package/lib/commonjs/components/productCard.js +688 -0
- package/lib/commonjs/components/productCard.js.map +1 -0
- package/lib/commonjs/components/progressCircle.js +99 -0
- package/lib/commonjs/components/progressCircle.js.map +1 -0
- package/lib/commonjs/components/testing.js +74 -0
- package/lib/commonjs/components/testing.js.map +1 -0
- package/lib/commonjs/components/voice.js +184 -0
- package/lib/commonjs/components/voice.js.map +1 -0
- package/lib/commonjs/components/welcomeButton.js +149 -0
- package/lib/commonjs/components/welcomeButton.js.map +1 -0
- package/lib/commonjs/components/welcomeInput.js +137 -0
- package/lib/commonjs/components/welcomeInput.js.map +1 -0
- package/lib/commonjs/contexts/AppContext.js +552 -0
- package/lib/commonjs/contexts/AppContext.js.map +1 -0
- package/lib/commonjs/hooks/Stream.js +599 -0
- package/lib/commonjs/hooks/Stream.js.map +1 -0
- package/lib/commonjs/hooks/useAsyncStorage.js +36 -0
- package/lib/commonjs/hooks/useAsyncStorage.js.map +1 -0
- package/lib/commonjs/index.js +44 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/layout/disclaimer.js +208 -0
- package/lib/commonjs/layout/disclaimer.js.map +1 -0
- package/lib/commonjs/layout/ex.js +254 -0
- package/lib/commonjs/layout/ex.js.map +1 -0
- package/lib/commonjs/layout/icon.js +118 -0
- package/lib/commonjs/layout/icon.js.map +1 -0
- package/lib/commonjs/layout/layout.js +168 -0
- package/lib/commonjs/layout/layout.js.map +1 -0
- package/lib/commonjs/layout/welcome.js +160 -0
- package/lib/commonjs/layout/welcome.js.map +1 -0
- package/lib/commonjs/layout/window.js +396 -0
- package/lib/commonjs/layout/window.js.map +1 -0
- package/lib/commonjs/utils/audioRecorder.js +412 -0
- package/lib/commonjs/utils/audioRecorder.js.map +1 -0
- package/lib/commonjs/utils/cloudinary.js +69 -0
- package/lib/commonjs/utils/cloudinary.js.map +1 -0
- package/lib/commonjs/utils/storage.js +76 -0
- package/lib/commonjs/utils/storage.js.map +1 -0
- package/lib/commonjs/utils/textToSpeech.js +53 -0
- package/lib/commonjs/utils/textToSpeech.js.map +1 -0
- package/lib/module/assets/chat-icon-mobile.svg +1 -0
- package/lib/module/assets/heritage.png +0 -0
- package/lib/module/assets/posiden.svg +51 -0
- package/lib/module/components/LoadingTips.js +95 -0
- package/lib/module/components/LoadingTips.js.map +1 -0
- package/lib/module/components/email.js +452 -0
- package/lib/module/components/email.js.map +1 -0
- package/lib/module/components/feedback.js +105 -0
- package/lib/module/components/feedback.js.map +1 -0
- package/lib/module/components/header.js +117 -0
- package/lib/module/components/header.js.map +1 -0
- package/lib/module/components/input.js +135 -0
- package/lib/module/components/input.js.map +1 -0
- package/lib/module/components/productCard.js +679 -0
- package/lib/module/components/productCard.js.map +1 -0
- package/lib/module/components/progressCircle.js +91 -0
- package/lib/module/components/progressCircle.js.map +1 -0
- package/lib/module/components/testing.js +66 -0
- package/lib/module/components/testing.js.map +1 -0
- package/lib/module/components/voice.js +175 -0
- package/lib/module/components/voice.js.map +1 -0
- package/lib/module/components/welcomeButton.js +140 -0
- package/lib/module/components/welcomeButton.js.map +1 -0
- package/lib/module/components/welcomeInput.js +128 -0
- package/lib/module/components/welcomeInput.js.map +1 -0
- package/lib/module/contexts/AppContext.js +542 -0
- package/lib/module/contexts/AppContext.js.map +1 -0
- package/lib/module/hooks/Stream.js +592 -0
- package/lib/module/hooks/Stream.js.map +1 -0
- package/lib/module/hooks/useAsyncStorage.js +29 -0
- package/lib/module/hooks/useAsyncStorage.js.map +1 -0
- package/lib/module/index.js +36 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/layout/disclaimer.js +199 -0
- package/lib/module/layout/disclaimer.js.map +1 -0
- package/lib/module/layout/ex.js +253 -0
- package/lib/module/layout/ex.js.map +1 -0
- package/lib/module/layout/icon.js +108 -0
- package/lib/module/layout/icon.js.map +1 -0
- package/lib/module/layout/layout.js +160 -0
- package/lib/module/layout/layout.js.map +1 -0
- package/lib/module/layout/welcome.js +150 -0
- package/lib/module/layout/welcome.js.map +1 -0
- package/lib/module/layout/window.js +387 -0
- package/lib/module/layout/window.js.map +1 -0
- package/lib/module/utils/audioRecorder.js +398 -0
- package/lib/module/utils/audioRecorder.js.map +1 -0
- package/lib/module/utils/cloudinary.js +61 -0
- package/lib/module/utils/cloudinary.js.map +1 -0
- package/lib/module/utils/storage.js +67 -0
- package/lib/module/utils/storage.js.map +1 -0
- package/lib/module/utils/textToSpeech.js +43 -0
- package/lib/module/utils/textToSpeech.js.map +1 -0
- package/lib/typescript/components/LoadingTips.d.ts +3 -0
- package/lib/typescript/components/LoadingTips.d.ts.map +1 -0
- package/lib/typescript/components/email.d.ts +6 -0
- package/lib/typescript/components/email.d.ts.map +1 -0
- package/lib/typescript/components/feedback.d.ts +6 -0
- package/lib/typescript/components/feedback.d.ts.map +1 -0
- package/lib/typescript/components/header.d.ts +3 -0
- package/lib/typescript/components/header.d.ts.map +1 -0
- package/lib/typescript/components/input.d.ts +6 -0
- package/lib/typescript/components/input.d.ts.map +1 -0
- package/lib/typescript/components/productCard.d.ts +7 -0
- package/lib/typescript/components/productCard.d.ts.map +1 -0
- package/lib/typescript/components/progressCircle.d.ts +3 -0
- package/lib/typescript/components/progressCircle.d.ts.map +1 -0
- package/lib/typescript/components/testing.d.ts +6 -0
- package/lib/typescript/components/testing.d.ts.map +1 -0
- package/lib/typescript/components/voice.d.ts +5 -0
- package/lib/typescript/components/voice.d.ts.map +1 -0
- package/lib/typescript/components/welcomeButton.d.ts +4 -0
- package/lib/typescript/components/welcomeButton.d.ts.map +1 -0
- package/lib/typescript/components/welcomeInput.d.ts +6 -0
- package/lib/typescript/components/welcomeInput.d.ts.map +1 -0
- package/lib/typescript/contexts/AppContext.d.ts +10 -0
- package/lib/typescript/contexts/AppContext.d.ts.map +1 -0
- package/lib/typescript/hooks/Stream.d.ts +2 -0
- package/lib/typescript/hooks/Stream.d.ts.map +1 -0
- package/lib/typescript/hooks/useAsyncStorage.d.ts +2 -0
- package/lib/typescript/hooks/useAsyncStorage.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +8 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/layout/disclaimer.d.ts +5 -0
- package/lib/typescript/layout/disclaimer.d.ts.map +1 -0
- package/lib/typescript/layout/ex.d.ts +1 -0
- package/lib/typescript/layout/ex.d.ts.map +1 -0
- package/lib/typescript/layout/icon.d.ts +3 -0
- package/lib/typescript/layout/icon.d.ts.map +1 -0
- package/lib/typescript/layout/layout.d.ts +3 -0
- package/lib/typescript/layout/layout.d.ts.map +1 -0
- package/lib/typescript/layout/welcome.d.ts +6 -0
- package/lib/typescript/layout/welcome.d.ts.map +1 -0
- package/lib/typescript/layout/window.d.ts +5 -0
- package/lib/typescript/layout/window.d.ts.map +1 -0
- package/lib/typescript/utils/audioRecorder.d.ts +9 -0
- package/lib/typescript/utils/audioRecorder.d.ts.map +1 -0
- package/lib/typescript/utils/cloudinary.d.ts +17 -0
- package/lib/typescript/utils/cloudinary.d.ts.map +1 -0
- package/lib/typescript/utils/storage.d.ts +29 -0
- package/lib/typescript/utils/storage.d.ts.map +1 -0
- package/lib/typescript/utils/textToSpeech.d.ts +2 -0
- package/lib/typescript/utils/textToSpeech.d.ts.map +1 -0
- package/package.json +109 -0
- package/src/assets/chat-icon-mobile.svg +1 -0
- package/src/assets/heritage.png +0 -0
- package/src/assets/posiden.svg +51 -0
- package/src/components/LoadingTips.js +99 -0
- package/src/components/email.js +467 -0
- package/src/components/feedback.js +114 -0
- package/src/components/header.js +119 -0
- package/src/components/input.js +133 -0
- package/src/components/productCard.js +815 -0
- package/src/components/progressCircle.js +88 -0
- package/src/components/testing.js +60 -0
- package/src/components/voice.js +228 -0
- package/src/components/welcomeButton.js +161 -0
- package/src/components/welcomeInput.js +133 -0
- package/src/contexts/AppContext.js +678 -0
- package/src/hooks/Stream.js +655 -0
- package/src/hooks/useAsyncStorage.js +33 -0
- package/src/index.js +30 -0
- package/src/layout/disclaimer.js +231 -0
- package/src/layout/ex.js +252 -0
- package/src/layout/icon.js +105 -0
- package/src/layout/layout.js +160 -0
- package/src/layout/welcome.js +172 -0
- package/src/layout/window.js +476 -0
- package/src/utils/audioRecorder.js +445 -0
- package/src/utils/cloudinary.js +61 -0
- package/src/utils/storage.ts +89 -0
- package/src/utils/textToSpeech.js +49 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import uuid from 'react-native-uuid';
|
|
9
|
+
// import useAsyncStorage from '../hooks/useAsyncStorage';
|
|
10
|
+
import { loadChat, updateChat, defaultState } from '../utils/storage';
|
|
11
|
+
|
|
12
|
+
export const AppContext = createContext();
|
|
13
|
+
|
|
14
|
+
export const AppProvider = ({
|
|
15
|
+
data,
|
|
16
|
+
onProductCardClick,
|
|
17
|
+
onAddToCartClick,
|
|
18
|
+
uiConfig = {},
|
|
19
|
+
children,
|
|
20
|
+
}) => {
|
|
21
|
+
// Brand-driven configuration
|
|
22
|
+
const version =
|
|
23
|
+
data?.brand_version === 'pool' || data?.brand_version === 'landscape'
|
|
24
|
+
? data.brand_version
|
|
25
|
+
: 'landscape';
|
|
26
|
+
const API_PREFIX = 'https://';
|
|
27
|
+
|
|
28
|
+
const BRAND_CONFIG = (() => {
|
|
29
|
+
const isStage = data?.env === 'stage';
|
|
30
|
+
return {
|
|
31
|
+
pool: {
|
|
32
|
+
primaryColor: '#004687',
|
|
33
|
+
userMessageColor: '#003764',
|
|
34
|
+
cloudName: 'heritageplus',
|
|
35
|
+
// Pool assets live under mobileapp/ in heritageplus cloud
|
|
36
|
+
logoId: 'mobileapp/logos/HPSG_HPlus_Logo_White',
|
|
37
|
+
baseHost: isStage
|
|
38
|
+
? 'pool-stage-external-agent-adk-586731320826.us-east1.run.app'
|
|
39
|
+
: 'pool-prod-external-agent-adk-586731320826.us-east1.run.app',
|
|
40
|
+
// baseHost: isStage
|
|
41
|
+
// ? '0.0.0.0:8080'
|
|
42
|
+
// : 'pool-prod-external-agent-adk-586731320826.us-east1.run.app',
|
|
43
|
+
loggingHost:
|
|
44
|
+
'srs-external-agent-logging-586731320826.us-central1.run.app',
|
|
45
|
+
},
|
|
46
|
+
landscape: {
|
|
47
|
+
primaryColor: '#437D3D',
|
|
48
|
+
userMessageColor: '#437D3D',
|
|
49
|
+
cloudName: 'mktg',
|
|
50
|
+
logoId: 'logos/HLSG_Logo_Lockup_Color',
|
|
51
|
+
baseHost: isStage
|
|
52
|
+
? 'landscape-stage-external-agent-adk-586731320826.us-east1.run.app'
|
|
53
|
+
: 'landscape-prod-external-agent-adk-586731320826.us-east1.run.app',
|
|
54
|
+
loggingHost:
|
|
55
|
+
'srs-external-agent-landscape-logging-586731320826.us-central1.run.app',
|
|
56
|
+
},
|
|
57
|
+
}[version];
|
|
58
|
+
})();
|
|
59
|
+
|
|
60
|
+
const BASE_URL = BRAND_CONFIG.baseHost;
|
|
61
|
+
const LOGGING_URL = BRAND_CONFIG.loggingHost;
|
|
62
|
+
const TRACK_CLICK_URL = `${API_PREFIX}${LOGGING_URL}/track-click`;
|
|
63
|
+
const ADD_TO_CART_URL = `${API_PREFIX}${LOGGING_URL}/add-to-cart`;
|
|
64
|
+
|
|
65
|
+
const theme = {
|
|
66
|
+
userMessage: BRAND_CONFIG.userMessageColor,
|
|
67
|
+
botMessage: BRAND_CONFIG.userMessageColor,
|
|
68
|
+
backgroundColor: '#f6f6f6',
|
|
69
|
+
textColor: '#161616',
|
|
70
|
+
textColorSecondary: '#FFFFFF',
|
|
71
|
+
inlineButtonColor: '#dbd4c8',
|
|
72
|
+
primaryColor: BRAND_CONFIG.primaryColor,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const brandLogo = BRAND_CONFIG.logoId;
|
|
76
|
+
const brandCloudName = BRAND_CONFIG.cloudName;
|
|
77
|
+
|
|
78
|
+
// Default Messages
|
|
79
|
+
const defaultMessage = [
|
|
80
|
+
{
|
|
81
|
+
type: 'ai',
|
|
82
|
+
text: `Hi there 👋 I'm your ${
|
|
83
|
+
version === 'pool' ? 'HeritagePool+' : 'Heritage+'
|
|
84
|
+
} AI Assistant. I'm here to help you with Product and Account information during your online visit. I'm still learning and growing - the more we interact and the more feedback you share, the better I can assist you. How can I help you today?`,
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const maintenanceMessage = [
|
|
88
|
+
{
|
|
89
|
+
type: 'ai',
|
|
90
|
+
text: "Hi there 👋 I'm your Heritage+ AI Agent. I'm currently undergoing maintenance to improve my services. Thank you for your patience and understanding!",
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
// user data variables
|
|
95
|
+
const customerToken = data.customer_token;
|
|
96
|
+
|
|
97
|
+
// Base Variables
|
|
98
|
+
const [showModal, setShowModal] = useState('Icon');
|
|
99
|
+
const [input, setInput] = useState('');
|
|
100
|
+
const [messages, setMessages] = useState(defaultState.messages);
|
|
101
|
+
const [conversationStartTime, setConversationStartTime] = useState(null);
|
|
102
|
+
const [lastUserMessage, setLastUserMessage] = useState('');
|
|
103
|
+
const [lastMessageId, setLastMessageId] = useState('');
|
|
104
|
+
const [sessionId, setSessionId] = useState(null);
|
|
105
|
+
const [isListening, setIsListening] = useState(false);
|
|
106
|
+
|
|
107
|
+
// Message UI
|
|
108
|
+
const [typingIndicator, setTypingIndicator] = useState(false);
|
|
109
|
+
const [ghostMessage, setGhostMessage] = useState(false);
|
|
110
|
+
const [ghostCard, setGhostCard] = useState(false);
|
|
111
|
+
const [stopActivated, setStopActivated] = useState(false);
|
|
112
|
+
const [disclaimer, setDisclaimer] = useState(false);
|
|
113
|
+
const [startStreaming, setStartStreaming] = useState(false);
|
|
114
|
+
|
|
115
|
+
// Load persisted state on mount
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!customerToken) return;
|
|
118
|
+
|
|
119
|
+
const init = async () => {
|
|
120
|
+
try {
|
|
121
|
+
const cachedState = await loadChat(customerToken);
|
|
122
|
+
|
|
123
|
+
// Update all stateful values
|
|
124
|
+
setTypingIndicator(cachedState.typingIndicator);
|
|
125
|
+
setGhostMessage(cachedState.ghostMessage);
|
|
126
|
+
setGhostCard(cachedState.ghostCard);
|
|
127
|
+
setStopActivated(cachedState.stopActivated);
|
|
128
|
+
setDisclaimer(cachedState.disclaimer);
|
|
129
|
+
setStartStreaming(cachedState.startStreaming);
|
|
130
|
+
setMessages(cachedState.messages);
|
|
131
|
+
// setShowModal(cachedState.showModal);
|
|
132
|
+
|
|
133
|
+
if (uiConfig.toggleChat === false) {
|
|
134
|
+
setShowModal('Icon');
|
|
135
|
+
} else {
|
|
136
|
+
if (!disclaimer) {
|
|
137
|
+
setShowModal('Form');
|
|
138
|
+
} else {
|
|
139
|
+
if (
|
|
140
|
+
(uiConfig.showWelcome === true ||
|
|
141
|
+
cachedState.showWelcome === true) &&
|
|
142
|
+
messages.length < 2
|
|
143
|
+
) {
|
|
144
|
+
// console.log("showWelcome", uiConfig.showWelcome, cachedState.showWelcome)
|
|
145
|
+
setShowModal('Welcome');
|
|
146
|
+
} else {
|
|
147
|
+
setShowModal('ChatWindow');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('Error loading chat state:', error);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
init();
|
|
157
|
+
}, [customerToken, uiConfig.toggleChat, uiConfig.showWelcome]);
|
|
158
|
+
|
|
159
|
+
// Persist state changes
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (!customerToken) return;
|
|
162
|
+
|
|
163
|
+
const persistState = async () => {
|
|
164
|
+
const currentState = {
|
|
165
|
+
typingIndicator,
|
|
166
|
+
ghostMessage,
|
|
167
|
+
ghostCard,
|
|
168
|
+
stopActivated,
|
|
169
|
+
disclaimer,
|
|
170
|
+
startStreaming,
|
|
171
|
+
messages,
|
|
172
|
+
showModal,
|
|
173
|
+
showWelcome: uiConfig.showWelcome,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
await updateChat(customerToken, currentState);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
persistState();
|
|
180
|
+
}, [
|
|
181
|
+
customerToken,
|
|
182
|
+
typingIndicator,
|
|
183
|
+
ghostMessage,
|
|
184
|
+
ghostCard,
|
|
185
|
+
stopActivated,
|
|
186
|
+
disclaimer,
|
|
187
|
+
startStreaming,
|
|
188
|
+
messages,
|
|
189
|
+
showModal,
|
|
190
|
+
uiConfig.showWelcome,
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
if (showModal == 'Off') {
|
|
195
|
+
setShowModal('Icon');
|
|
196
|
+
}
|
|
197
|
+
}, [uiConfig.showIcon]);
|
|
198
|
+
|
|
199
|
+
const stopGenerating = () => {
|
|
200
|
+
setTypingIndicator(false);
|
|
201
|
+
setStopActivated(true);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
function handleSend(input) {
|
|
205
|
+
if (maintenance) {
|
|
206
|
+
setMessages([
|
|
207
|
+
...messages,
|
|
208
|
+
{ type: 'user', text: input },
|
|
209
|
+
{
|
|
210
|
+
type: 'ai',
|
|
211
|
+
text: 'The chat is currently unavailable, please come back later!',
|
|
212
|
+
},
|
|
213
|
+
]);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Ensure input is a string and not empty
|
|
218
|
+
const userInput =
|
|
219
|
+
typeof input === 'string' ? input.trim() : String(input || '').trim();
|
|
220
|
+
|
|
221
|
+
if (userInput !== '') {
|
|
222
|
+
// Ensure sessionId exists before starting a conversation
|
|
223
|
+
if (!sessionId) {
|
|
224
|
+
const newSessionId = uuid.v4();
|
|
225
|
+
setSessionId(newSessionId);
|
|
226
|
+
console.log('[AppContext] Generated new sessionId:', newSessionId);
|
|
227
|
+
}
|
|
228
|
+
if (messages.length <= 1) {
|
|
229
|
+
const startTime = new Date().toISOString();
|
|
230
|
+
setConversationStartTime(startTime);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Create user message object with immutable text
|
|
234
|
+
const userMessage = {
|
|
235
|
+
type: 'user',
|
|
236
|
+
text: userInput,
|
|
237
|
+
session_id: data.session_id || data.session || sessionId,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Initiate streaming
|
|
241
|
+
setShowModal('ChatWindow');
|
|
242
|
+
setLastUserMessage(userInput);
|
|
243
|
+
setMessages([...messages, userMessage]);
|
|
244
|
+
setTypingIndicator(true);
|
|
245
|
+
setGhostMessage(true);
|
|
246
|
+
setStartStreaming(true);
|
|
247
|
+
setInput('');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// After form submission
|
|
252
|
+
function setNextMessage(name) {
|
|
253
|
+
setMessages(prevMessages => {
|
|
254
|
+
const updatedMessages = prevMessages.map((message, index) => {
|
|
255
|
+
if (index === prevMessages.length - 1 && message.form) {
|
|
256
|
+
return { ...message, form: false };
|
|
257
|
+
}
|
|
258
|
+
return message;
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const nextMessage = {
|
|
262
|
+
type: 'ai',
|
|
263
|
+
text: [`Nice to meet you ${name}. How can I help you today?`],
|
|
264
|
+
};
|
|
265
|
+
return [...updatedMessages, nextMessage];
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Confirm Disclaimer
|
|
270
|
+
async function confirmDisclaimer() {
|
|
271
|
+
setDisclaimer(true);
|
|
272
|
+
if (!maintenance) {
|
|
273
|
+
setShowModal('Welcome');
|
|
274
|
+
setMessages(defaultMessage);
|
|
275
|
+
} else {
|
|
276
|
+
setShowModal('ChatWindow');
|
|
277
|
+
setMessages(maintenanceMessage);
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
const response = await fetch(
|
|
281
|
+
API_PREFIX + LOGGING_URL + '/log-disclaimer',
|
|
282
|
+
{
|
|
283
|
+
method: 'POST',
|
|
284
|
+
headers: {
|
|
285
|
+
'Content-Type': 'application/json',
|
|
286
|
+
},
|
|
287
|
+
body: JSON.stringify({ email: data.user_email }),
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
if (!response.ok) {
|
|
291
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
292
|
+
}
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error('Error in Log disclaimer:', error);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function handleButtonClick(buttonText) {
|
|
299
|
+
handleSend(buttonText);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const [maintenance, setMaintenance] = useState(false);
|
|
303
|
+
|
|
304
|
+
const handleClearState = async () => {
|
|
305
|
+
if (!maintenance) {
|
|
306
|
+
setShowModal('Welcome');
|
|
307
|
+
}
|
|
308
|
+
if (messages.length > 1) {
|
|
309
|
+
const newState = {
|
|
310
|
+
...defaultState,
|
|
311
|
+
messages: maintenance ? maintenanceMessage : defaultMessage,
|
|
312
|
+
showModal: maintenance ? 'ChatWindow' : 'Welcome',
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Update all state variables
|
|
316
|
+
setMessages(newState.messages);
|
|
317
|
+
setTypingIndicator(false);
|
|
318
|
+
setGhostMessage(false);
|
|
319
|
+
setShowModal(newState.showModal);
|
|
320
|
+
|
|
321
|
+
// Generate new session
|
|
322
|
+
const newSessionId = uuid.v4();
|
|
323
|
+
setSessionId(newSessionId);
|
|
324
|
+
|
|
325
|
+
// Clear persisted state
|
|
326
|
+
if (customerToken) {
|
|
327
|
+
await updateChat(customerToken, newState);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const [feedback, setFeedback] = useState({});
|
|
333
|
+
|
|
334
|
+
async function handleFeedback(feedbackValue, messageId) {
|
|
335
|
+
switchFeedbackOpen(true, messageId, true);
|
|
336
|
+
setFeedback(prevFeedback => {
|
|
337
|
+
const updatedFeedback = { ...prevFeedback };
|
|
338
|
+
|
|
339
|
+
if (!updatedFeedback[messageId]) {
|
|
340
|
+
updatedFeedback[messageId] = 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
updatedFeedback[messageId] = feedbackValue;
|
|
344
|
+
|
|
345
|
+
return updatedFeedback;
|
|
346
|
+
});
|
|
347
|
+
try {
|
|
348
|
+
const response = await fetch(API_PREFIX + LOGGING_URL + '/feedback', {
|
|
349
|
+
method: 'POST',
|
|
350
|
+
headers: {
|
|
351
|
+
'Content-Type': 'application/json',
|
|
352
|
+
},
|
|
353
|
+
body: JSON.stringify({
|
|
354
|
+
message_id: messageId,
|
|
355
|
+
feedback: feedbackValue,
|
|
356
|
+
}),
|
|
357
|
+
});
|
|
358
|
+
if (!response.ok) {
|
|
359
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
360
|
+
}
|
|
361
|
+
//console.log(response)
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error('Error in feedback post:', error);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const [feedbackOpen, setFeedbackOpen] = useState({});
|
|
368
|
+
const [writeFeedback, setWriteFeedback] = useState('');
|
|
369
|
+
const [writeAnswer, setWriteAnswer] = useState('');
|
|
370
|
+
|
|
371
|
+
// Written Feedback ----------------------------
|
|
372
|
+
|
|
373
|
+
function switchFeedbackOpen(newVal, messageId, resetvalue) {
|
|
374
|
+
console.log(newVal, feedbackOpen[messageId], messageId, resetvalue);
|
|
375
|
+
console.log(feedbackOpen);
|
|
376
|
+
if (newVal != feedbackOpen[messageId]) {
|
|
377
|
+
setWriteFeedback('');
|
|
378
|
+
setWriteAnswer('');
|
|
379
|
+
}
|
|
380
|
+
if (resetvalue) {
|
|
381
|
+
setWriteFeedback('');
|
|
382
|
+
setWriteAnswer('');
|
|
383
|
+
}
|
|
384
|
+
if (feedbackOpen[messageId] == 'done') {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
setFeedbackOpen(prevFeedback => {
|
|
388
|
+
const updatedFeedback = { ...prevFeedback };
|
|
389
|
+
updatedFeedback[messageId] = newVal;
|
|
390
|
+
return updatedFeedback;
|
|
391
|
+
});
|
|
392
|
+
console.log(feedbackOpen);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function handleWrittenFeedback(messageId) {
|
|
396
|
+
if (writeFeedback.trim() !== '' || writeAnswer.trim() !== '') {
|
|
397
|
+
//console.log(messageId, writeFeedback)
|
|
398
|
+
switchFeedbackOpen(-1, messageId, true);
|
|
399
|
+
try {
|
|
400
|
+
const response = await fetch(
|
|
401
|
+
API_PREFIX + LOGGING_URL + '/feedback-message',
|
|
402
|
+
{
|
|
403
|
+
method: 'POST',
|
|
404
|
+
headers: {
|
|
405
|
+
'Content-Type': 'application/json',
|
|
406
|
+
},
|
|
407
|
+
body: JSON.stringify({
|
|
408
|
+
message_id: messageId,
|
|
409
|
+
feedback_message: writeFeedback,
|
|
410
|
+
correct_answer: writeAnswer,
|
|
411
|
+
}),
|
|
412
|
+
},
|
|
413
|
+
);
|
|
414
|
+
if (!response.ok) {
|
|
415
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
416
|
+
}
|
|
417
|
+
//console.log(response)
|
|
418
|
+
} catch (error) {
|
|
419
|
+
console.error('Error in feedback_message:', error);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const formatChatHistory = () => {
|
|
425
|
+
// Skip the first message (index 0) which is the welcome message
|
|
426
|
+
return messages.slice(1).map(message => {
|
|
427
|
+
if (Array.isArray(message.text)) {
|
|
428
|
+
return {
|
|
429
|
+
type: message.type,
|
|
430
|
+
content: message.text.join('\n'),
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
type: message.type,
|
|
435
|
+
content: message.text,
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
function handleVoiceSend(audio, transcription) {
|
|
441
|
+
//setReadAloud(true)
|
|
442
|
+
handleSend(transcription);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Wrapper function for onAddToCartClick to add middle message
|
|
446
|
+
const handleAddToCartWithMessage = cartData => {
|
|
447
|
+
// Add middle message indicating item is being added to cart
|
|
448
|
+
setMessages(prevMessages => [
|
|
449
|
+
...prevMessages,
|
|
450
|
+
{
|
|
451
|
+
type: 'middle',
|
|
452
|
+
text: `${cartData.quantity} ${cartData.selectedUom} of item ${cartData.product.product_details.product_name} added to cart`,
|
|
453
|
+
products: [],
|
|
454
|
+
product_cards: 'False',
|
|
455
|
+
},
|
|
456
|
+
]);
|
|
457
|
+
|
|
458
|
+
// Call the original onAddToCartClick function
|
|
459
|
+
if (onAddToCartClick) {
|
|
460
|
+
onAddToCartClick(cartData);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// translations from web: port to mobile in an effort to standardize experiences
|
|
465
|
+
const translations = {
|
|
466
|
+
en: {
|
|
467
|
+
greeting: 'Hi',
|
|
468
|
+
aiAssistantIntro:
|
|
469
|
+
"I'm your Heritage Pool+ AI Assistant. I can help you during your online visit with Product and Account information.",
|
|
470
|
+
askAway: 'Ask away...',
|
|
471
|
+
betaDisclaimer: 'Beta version. AI Assistant is still learning!',
|
|
472
|
+
helpCategories: 'Here are some areas I can help you with:',
|
|
473
|
+
suggestedQuestions: 'Suggested Questions...',
|
|
474
|
+
languageToggle: 'Ayuda en Español',
|
|
475
|
+
products: 'Products',
|
|
476
|
+
orders: 'Orders',
|
|
477
|
+
invoices: 'Invoices',
|
|
478
|
+
branchInfo: 'Branch Information',
|
|
479
|
+
hiMessage:
|
|
480
|
+
"Hi, I'm your Heritage Pool+ AI Assistant. How can I help you?",
|
|
481
|
+
hiMessageMobile:
|
|
482
|
+
"Hi, I'm your Heritage Pool+ AI Assistant! How can I help you?",
|
|
483
|
+
defaultMessage:
|
|
484
|
+
"Hi there 👋 I'm your Heritage Pool+ AI Assistant. I'm here to help you with Product and Account information during your online visit. I'm still learning and growing - the more we interact and the more feedback you share, the better I can assist you. How can I help you today?",
|
|
485
|
+
nameMessage:
|
|
486
|
+
"Hi there 👋 I'm your Heritage Pool+ AI Assistant. I'm here to help you with Product and Account information during your online visit. I'm still learning and growing - the more we interact and the more feedback you share, the better I can assist you. How can I help you today?",
|
|
487
|
+
nextMessage: 'Nice to meet you {name}. How can I help you today?',
|
|
488
|
+
maintenanceMessage:
|
|
489
|
+
"Hi there 👋 I'm your Heritage Pool+ AI Assistant. I'm currently undergoing maintenance to improve my services. Thank you for your patience and understanding!",
|
|
490
|
+
followUpQuestions: {
|
|
491
|
+
products: [
|
|
492
|
+
'Do you have part # JNDDEV48 in stock?',
|
|
493
|
+
'Which grid assembly goes with the Hayward DE4820 filter?',
|
|
494
|
+
"What's the max flow rate for the Tristar VS 950?",
|
|
495
|
+
"What's the difference between the Jandy VS FloPro 2.7 HP and the Jandy VS PlusHP 2.7 HP?",
|
|
496
|
+
],
|
|
497
|
+
branchInfo: [
|
|
498
|
+
'What are the hours of my current branch?',
|
|
499
|
+
'How can I contact my branch?',
|
|
500
|
+
'Branches near me.',
|
|
501
|
+
],
|
|
502
|
+
orders: [
|
|
503
|
+
"Where can I find my PO's?",
|
|
504
|
+
'How do I change my shipping method?',
|
|
505
|
+
'How do I place my early buys?',
|
|
506
|
+
'Can I pick up my open order?',
|
|
507
|
+
'I need to edit a recently placed order.',
|
|
508
|
+
],
|
|
509
|
+
invoices: [
|
|
510
|
+
'How do I pay for invoices?',
|
|
511
|
+
'How do I print past invoices?',
|
|
512
|
+
'What is my current account balance?',
|
|
513
|
+
'How many unpaid invoices do I have?',
|
|
514
|
+
'How do I change my payment on file?',
|
|
515
|
+
],
|
|
516
|
+
},
|
|
517
|
+
loadingTips: [
|
|
518
|
+
'Use 👍👎 next to each response to help us improve the AI Assistant',
|
|
519
|
+
'🗑️ Clear the chat history after each conversation for continued accurate responses',
|
|
520
|
+
],
|
|
521
|
+
addToCart: 'Add to Cart',
|
|
522
|
+
},
|
|
523
|
+
es: {
|
|
524
|
+
greeting: 'Hola',
|
|
525
|
+
aiAssistantIntro:
|
|
526
|
+
'Soy tu Asistente de IA de Heritage Pool+. Puedo ayudarte durante tu visita en línea con información de productos y cuentas.',
|
|
527
|
+
askAway: 'Pregunta lo que quieras...',
|
|
528
|
+
betaDisclaimer: 'Versión beta. ¡Asistente de IA sigue aprendiendo!',
|
|
529
|
+
helpCategories: 'Estas son algunas áreas en las que puedo ayudarte:',
|
|
530
|
+
suggestedQuestions: 'Preguntas Sugeridas...',
|
|
531
|
+
languageToggle: 'Assistance in English',
|
|
532
|
+
products: 'Productos',
|
|
533
|
+
orders: 'Pedidos',
|
|
534
|
+
invoices: 'Facturas',
|
|
535
|
+
branchInfo: 'Información de Sucursal',
|
|
536
|
+
hiMessage:
|
|
537
|
+
'Hola, soy tu Asistente de IA de Heritage Pool+. ¿Cómo puedo ayudarte?',
|
|
538
|
+
hiMessageMobile:
|
|
539
|
+
'¡Hola, soy tu Asistente de IA de Heritage Pool+! ¿Cómo puedo ayudarte?',
|
|
540
|
+
defaultMessage:
|
|
541
|
+
'Hola 👋 Soy tu Asistente de IA de Heritage Pool+. Estoy aquí para ayudarte con información de productos y cuentas durante tu visita en línea. Sigo aprendiendo y creciendo - mientras más interactuemos y más comentarios compartas, mejor podré asistirte. ¿Cómo puedo ayudarte hoy?',
|
|
542
|
+
nameMessage:
|
|
543
|
+
'Hola 👋 Soy tu Asistente de IA de Heritage Pool+. Estoy aquí para ayudarte con información de productos y cuentas durante tu visita en línea. Sigo aprendiendo y creciendo - mientras más interactuemos y más comentarios compartas, mejor podré asistirte. ¿Cómo puedo ayudarte hoy?',
|
|
544
|
+
nextMessage: 'Mucho gusto en conocerte {name}. ¿Cómo puedo ayudarte hoy?',
|
|
545
|
+
maintenanceMessage:
|
|
546
|
+
'Hola 👋 Soy tu Asistente de IA de Heritage Pool+. Actualmente estoy en mantenimiento para mejorar mis servicios. ¡Gracias por tu paciencia y comprensión!',
|
|
547
|
+
followUpQuestions: {
|
|
548
|
+
products: [
|
|
549
|
+
'¿Tienen la pieza # JNDDEV48 en existencia?',
|
|
550
|
+
'¿Qué ensamble de rejilla corresponde al filtro Hayward DE4820?',
|
|
551
|
+
'¿Cuál es el caudal máximo del Tristar VS 950?',
|
|
552
|
+
'¿Cuál es la diferencia entre el Jandy VS FloPro de 2.7 HP y el Jandy VS PlusHP de 2.7 HP?',
|
|
553
|
+
],
|
|
554
|
+
branchInfo: [
|
|
555
|
+
'¿Cuáles son los horarios de mi sucursal actual?',
|
|
556
|
+
'¿Cómo puedo contactar a mi sucursal?',
|
|
557
|
+
'Sucursales cerca de mí.',
|
|
558
|
+
],
|
|
559
|
+
orders: [
|
|
560
|
+
'¿Dónde puedo encontrar mis órdenes de compra?',
|
|
561
|
+
'¿Cómo cambio mi método de envío?',
|
|
562
|
+
'¿Cómo realizo mis compras anticipadas?',
|
|
563
|
+
'¿Puedo recoger mi pedido abierto?',
|
|
564
|
+
'Necesito editar un pedido recién realizado.',
|
|
565
|
+
],
|
|
566
|
+
invoices: [
|
|
567
|
+
'¿Cómo pago las facturas?',
|
|
568
|
+
'¿Cómo imprimo facturas anteriores?',
|
|
569
|
+
'¿Cuál es el saldo actual de mi cuenta?',
|
|
570
|
+
'¿Cuántas facturas sin pagar tengo?',
|
|
571
|
+
'¿Cómo cambio mi método de pago registrado?',
|
|
572
|
+
],
|
|
573
|
+
},
|
|
574
|
+
loadingTips: [
|
|
575
|
+
'Usa 👍👎 junto a cada respuesta para ayudarnos a mejorar el Asistente de IA',
|
|
576
|
+
'🗑️ Borra el historial de chat después de cada conversación para mantener respuestas precisas',
|
|
577
|
+
],
|
|
578
|
+
addToCart: 'Agregar al Carrito',
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const [isSpanish, setIsSpanish] = useState('isSpanish', false);
|
|
583
|
+
const getCurrentTranslations = () => translations[isSpanish ? 'es' : 'en'];
|
|
584
|
+
|
|
585
|
+
const toggleLanguage = () => {
|
|
586
|
+
const newLanguage = !isSpanish;
|
|
587
|
+
setIsSpanish(newLanguage);
|
|
588
|
+
|
|
589
|
+
// Update messages when language changes
|
|
590
|
+
const currentTranslations = translations[newLanguage ? 'es' : 'en'];
|
|
591
|
+
|
|
592
|
+
if (maintenance) {
|
|
593
|
+
setMessages([
|
|
594
|
+
{
|
|
595
|
+
type: 'ai',
|
|
596
|
+
text: [currentTranslations.maintenanceMessage],
|
|
597
|
+
},
|
|
598
|
+
]);
|
|
599
|
+
} else {
|
|
600
|
+
setMessages([
|
|
601
|
+
{
|
|
602
|
+
type: 'ai',
|
|
603
|
+
text: [currentTranslations.defaultMessage],
|
|
604
|
+
},
|
|
605
|
+
]);
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
return (
|
|
610
|
+
<AppContext.Provider
|
|
611
|
+
value={{
|
|
612
|
+
isSpanish,
|
|
613
|
+
getCurrentTranslations,
|
|
614
|
+
toggleLanguage,
|
|
615
|
+
showModal,
|
|
616
|
+
setShowModal,
|
|
617
|
+
messages,
|
|
618
|
+
setMessages,
|
|
619
|
+
handleSend,
|
|
620
|
+
input,
|
|
621
|
+
setInput,
|
|
622
|
+
defaultMessage,
|
|
623
|
+
typingIndicator,
|
|
624
|
+
setTypingIndicator,
|
|
625
|
+
handleClearState,
|
|
626
|
+
theme,
|
|
627
|
+
handleButtonClick,
|
|
628
|
+
conversationStartTime,
|
|
629
|
+
setConversationStartTime,
|
|
630
|
+
lastUserMessage,
|
|
631
|
+
setLastUserMessage,
|
|
632
|
+
ghostMessage,
|
|
633
|
+
setGhostMessage,
|
|
634
|
+
ghostCard,
|
|
635
|
+
setGhostCard,
|
|
636
|
+
stopActivated,
|
|
637
|
+
setStopActivated,
|
|
638
|
+
disclaimer,
|
|
639
|
+
setDisclaimer,
|
|
640
|
+
startStreaming,
|
|
641
|
+
setStartStreaming,
|
|
642
|
+
maintenance,
|
|
643
|
+
setMaintenance,
|
|
644
|
+
feedback,
|
|
645
|
+
setFeedback,
|
|
646
|
+
handleFeedback,
|
|
647
|
+
feedbackOpen,
|
|
648
|
+
setFeedbackOpen,
|
|
649
|
+
writeFeedback,
|
|
650
|
+
setWriteFeedback,
|
|
651
|
+
writeAnswer,
|
|
652
|
+
setWriteAnswer,
|
|
653
|
+
BASE_URL,
|
|
654
|
+
lastMessageId,
|
|
655
|
+
setLastMessageId,
|
|
656
|
+
onProductCardClick,
|
|
657
|
+
onAddToCartClick: handleAddToCartWithMessage,
|
|
658
|
+
data,
|
|
659
|
+
sessionId,
|
|
660
|
+
setSessionId,
|
|
661
|
+
handleWrittenFeedback,
|
|
662
|
+
switchFeedbackOpen,
|
|
663
|
+
confirmDisclaimer,
|
|
664
|
+
formatChatHistory,
|
|
665
|
+
uiConfig,
|
|
666
|
+
handleVoiceSend,
|
|
667
|
+
TRACK_CLICK_URL,
|
|
668
|
+
ADD_TO_CART_URL,
|
|
669
|
+
isListening,
|
|
670
|
+
setIsListening,
|
|
671
|
+
brandLogo,
|
|
672
|
+
brandCloudName,
|
|
673
|
+
version,
|
|
674
|
+
}}>
|
|
675
|
+
{children}
|
|
676
|
+
</AppContext.Provider>
|
|
677
|
+
);
|
|
678
|
+
};
|