andy-widget-react 2.0.5 → 2.0.6
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/dist/index.cjs +199 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +199 -10
- package/dist/index.esm.js.map +1 -1
- package/dist/types/types/index.d.ts +9 -1
- package/package.json +2 -2
- package/src/AndyChat.tsx +38 -5
- package/src/hooks/useAndyChat.ts +3 -2
- package/src/types/index.ts +9 -1
package/dist/index.esm.js
CHANGED
|
@@ -869,7 +869,7 @@ const _mapApiDataToChatbotConfig = (apiData, defaultConfig) => {
|
|
|
869
869
|
throw new Error('Invalid chatbot configuration response.')
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
-
const { chatbot, faqs: rawFaqs } = apiData;
|
|
872
|
+
const { chatbot, faqs: rawFaqs, bootstrap } = apiData;
|
|
873
873
|
const appearance = chatbot.appearance || {};
|
|
874
874
|
const behavior = chatbot.behavior || {};
|
|
875
875
|
|
|
@@ -939,6 +939,7 @@ const _mapApiDataToChatbotConfig = (apiData, defaultConfig) => {
|
|
|
939
939
|
)
|
|
940
940
|
.map(faq => ({ question: faq.question.trim(), answer: faq.answer.trim() }));
|
|
941
941
|
}
|
|
942
|
+
config.bootstrap = bootstrap;
|
|
942
943
|
|
|
943
944
|
return config
|
|
944
945
|
};
|
|
@@ -988,7 +989,7 @@ const fetchChatbotConfig = async embedId => {
|
|
|
988
989
|
* @param {string} message - The user's message
|
|
989
990
|
* @returns {Promise<string>} The bot's response text
|
|
990
991
|
*/
|
|
991
|
-
const
|
|
992
|
+
const sendMessageForWidget = async (embedId, sessionId, message) => {
|
|
992
993
|
try {
|
|
993
994
|
const apiBaseUrl = getApiBaseUrl();
|
|
994
995
|
const response = await fetch(`${apiBaseUrl}/messages`, {
|
|
@@ -1009,10 +1010,13 @@ const sendMessage = async (embedId, sessionId, message) => {
|
|
|
1009
1010
|
if (data.error || data.limitStatus) {
|
|
1010
1011
|
throw new Error(data.error || 'Chat is temporarily unavailable.')
|
|
1011
1012
|
}
|
|
1013
|
+
if (data.handoffStatus === 'paused') {
|
|
1014
|
+
return { status: 'waiting' }
|
|
1015
|
+
}
|
|
1012
1016
|
if (typeof data.botResponse !== 'string' || !data.botResponse.trim()) {
|
|
1013
1017
|
throw new Error('Unexpected response format from server.')
|
|
1014
1018
|
}
|
|
1015
|
-
return data.botResponse
|
|
1019
|
+
return { status: 'agent', text: data.botResponse }
|
|
1016
1020
|
} else {
|
|
1017
1021
|
// Handle HTTP errors (e.g., 500, 404)
|
|
1018
1022
|
let errorData;
|
|
@@ -3694,12 +3698,167 @@ const isMobileDevice = () => {
|
|
|
3694
3698
|
return (hasTouch && isSmallScreen) || isMobileUserAgent || isMobileMediaQuery
|
|
3695
3699
|
};
|
|
3696
3700
|
|
|
3701
|
+
const identityKey = chatbotId => `andy-browser:${chatbotId}`;
|
|
3702
|
+
|
|
3703
|
+
const randomIdentity = () => ({
|
|
3704
|
+
sessionId: window.crypto.randomUUID(),
|
|
3705
|
+
capability: btoa(String.fromCharCode(...(() => {
|
|
3706
|
+
const bytes = new Uint8Array(32);
|
|
3707
|
+
window.crypto.getRandomValues(bytes);
|
|
3708
|
+
return bytes
|
|
3709
|
+
})()))
|
|
3710
|
+
});
|
|
3711
|
+
|
|
3712
|
+
const callConvex = async (convexUrl, kind, path, wireArgs, keepalive = false) => {
|
|
3713
|
+
const response = await fetch(`${convexUrl}/api/${kind}`, {
|
|
3714
|
+
method: 'POST',
|
|
3715
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3716
|
+
body: JSON.stringify({ path, format: 'convex_encoded_json', args: [wireArgs] }),
|
|
3717
|
+
keepalive
|
|
3718
|
+
});
|
|
3719
|
+
if (!response.ok) throw new Error(`Browser handoff ${kind} failed: ${response.status}`)
|
|
3720
|
+
const data = await response.json();
|
|
3721
|
+
if (data.status !== 'success') throw new Error(data.errorMessage || `Browser handoff ${path} failed`)
|
|
3722
|
+
return data.value
|
|
3723
|
+
};
|
|
3724
|
+
|
|
3725
|
+
const sortMessages = messages => [...messages].sort((left, right) =>
|
|
3726
|
+
left.createdAt - right.createdAt || String(left.id).localeCompare(String(right.id)));
|
|
3727
|
+
|
|
3728
|
+
const createBrowserHandoff = (chatbotId, bootstrap, onProjection) => {
|
|
3729
|
+
if (!bootstrap?.convexUrl || !bootstrap.surfaceKey || !bootstrap.surfaceProof) return null
|
|
3730
|
+
const storageKey = identityKey(chatbotId);
|
|
3731
|
+
let identity;
|
|
3732
|
+
try {
|
|
3733
|
+
const stored = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null');
|
|
3734
|
+
identity = stored?.sessionId && stored?.capability && stored.surfaceKey === bootstrap.surfaceKey
|
|
3735
|
+
? { sessionId: stored.sessionId, capability: stored.capability }
|
|
3736
|
+
: randomIdentity();
|
|
3737
|
+
window.sessionStorage.setItem(storageKey, JSON.stringify({ ...identity, surfaceKey: bootstrap.surfaceKey }));
|
|
3738
|
+
} catch {
|
|
3739
|
+
identity = randomIdentity();
|
|
3740
|
+
}
|
|
3741
|
+
const wireArgs = () => ({ chatbotId, sessionId: identity.sessionId, capability: identity.capability, surfaceProof: bootstrap.surfaceProof });
|
|
3742
|
+
let active = true;
|
|
3743
|
+
let visible = document.visibilityState === 'visible';
|
|
3744
|
+
let cursor;
|
|
3745
|
+
let timer;
|
|
3746
|
+
const pending = new Set();
|
|
3747
|
+
const acknowledged = new Set();
|
|
3748
|
+
const acknowledging = new Set();
|
|
3749
|
+
const localMessages = new Map();
|
|
3750
|
+
const emit = projection => {
|
|
3751
|
+
if (!active) return
|
|
3752
|
+
if (projection?.ok === false && projection.code === 'browser_presence_unavailable') {
|
|
3753
|
+
onProjection({ offline: true, refreshBootstrap: Date.now() >= bootstrap.surfaceProofExpiresAt, identity, messages: [] });
|
|
3754
|
+
return
|
|
3755
|
+
}
|
|
3756
|
+
if (!projection?.ok) return
|
|
3757
|
+
cursor = projection.cursor || cursor;
|
|
3758
|
+
const transcript = projection.transcript || [];
|
|
3759
|
+
const projected = (projection.messages || []).map(message => ({ ...message, role: 'bot', sentByHuman: true }));
|
|
3760
|
+
const byId = new Map(transcript.map(message => [String(message.id), message]));
|
|
3761
|
+
for (const message of localMessages.values()) {
|
|
3762
|
+
const persisted = transcript.some(candidate => candidate.role === 'user' && candidate.content === message.content && Math.abs(candidate.createdAt - message.createdAt) < 30000);
|
|
3763
|
+
if (persisted) localMessages.delete(message.id);
|
|
3764
|
+
else byId.set(message.id, message);
|
|
3765
|
+
}
|
|
3766
|
+
projected.forEach(message => byId.set(String(message.id), message));
|
|
3767
|
+
const messages = sortMessages([...byId.values()]);
|
|
3768
|
+
const pendingIds = projected.map(message => String(message.id)).filter(id => !acknowledged.has(id));
|
|
3769
|
+
pendingIds.forEach(id => pending.add(id));
|
|
3770
|
+
onProjection({ identity, control: projection.control, messages, pendingProjectionIds: pendingIds });
|
|
3771
|
+
};
|
|
3772
|
+
const project = async () => {
|
|
3773
|
+
if (!active || !visible) return
|
|
3774
|
+
if (Date.now() >= bootstrap.surfaceProofExpiresAt) {
|
|
3775
|
+
emit({ ok: false, code: 'browser_presence_unavailable' });
|
|
3776
|
+
return
|
|
3777
|
+
}
|
|
3778
|
+
try {
|
|
3779
|
+
const result = await callConvex(bootstrap.convexUrl, 'query', 'browserHandoff:project', { ...wireArgs(), ...(cursor ? { cursor } : {}) });
|
|
3780
|
+
emit(result);
|
|
3781
|
+
} catch {
|
|
3782
|
+
if (active) onProjection({ offline: true, identity, messages: [] });
|
|
3783
|
+
}
|
|
3784
|
+
};
|
|
3785
|
+
const connect = async () => {
|
|
3786
|
+
if (!active || Date.now() >= bootstrap.surfaceProofExpiresAt) {
|
|
3787
|
+
emit({ ok: false, code: 'browser_presence_unavailable' });
|
|
3788
|
+
return
|
|
3789
|
+
}
|
|
3790
|
+
try {
|
|
3791
|
+
const result = await callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:connect', wireArgs());
|
|
3792
|
+
if (result?.ok === false) return emit(result)
|
|
3793
|
+
await project();
|
|
3794
|
+
} catch {
|
|
3795
|
+
if (active) onProjection({ offline: true, identity, messages: [] });
|
|
3796
|
+
}
|
|
3797
|
+
};
|
|
3798
|
+
const release = () => callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:release', wireArgs(), true).catch(() => undefined);
|
|
3799
|
+
const onVisibility = () => {
|
|
3800
|
+
visible = document.visibilityState === 'visible';
|
|
3801
|
+
if (visible) void connect();
|
|
3802
|
+
else void release();
|
|
3803
|
+
};
|
|
3804
|
+
document.addEventListener('visibilitychange', onVisibility);
|
|
3805
|
+
window.addEventListener('pagehide', release);
|
|
3806
|
+
void connect();
|
|
3807
|
+
timer = window.setInterval(async () => {
|
|
3808
|
+
if (!active || !visible) return
|
|
3809
|
+
if (Date.now() >= bootstrap.surfaceProofExpiresAt) {
|
|
3810
|
+
window.clearInterval(timer);
|
|
3811
|
+
emit({ ok: false, code: 'browser_presence_unavailable' });
|
|
3812
|
+
return
|
|
3813
|
+
}
|
|
3814
|
+
try {
|
|
3815
|
+
const result = await callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:heartbeat', wireArgs());
|
|
3816
|
+
if (result?.ok === false) emit(result);
|
|
3817
|
+
else await project();
|
|
3818
|
+
} catch {
|
|
3819
|
+
if (active) onProjection({ offline: true, identity, messages: [] });
|
|
3820
|
+
}
|
|
3821
|
+
}, 15000);
|
|
3822
|
+
return {
|
|
3823
|
+
identity: { sessionId: identity.sessionId },
|
|
3824
|
+
recordVisitorMessage: content => {
|
|
3825
|
+
const id = `local-${window.crypto.randomUUID()}`;
|
|
3826
|
+
localMessages.set(id, { id, role: 'user', content, createdAt: Date.now(), sentByHuman: false });
|
|
3827
|
+
},
|
|
3828
|
+
markRendered: async messageId => {
|
|
3829
|
+
if (!active || !pending.has(messageId) || acknowledged.has(messageId) || acknowledging.has(messageId)) return
|
|
3830
|
+
acknowledging.add(messageId);
|
|
3831
|
+
try {
|
|
3832
|
+
const result = await callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:acknowledge', { ...wireArgs(), messageId });
|
|
3833
|
+
if (result?.ok) {
|
|
3834
|
+
acknowledged.add(messageId);
|
|
3835
|
+
pending.delete(messageId);
|
|
3836
|
+
}
|
|
3837
|
+
} catch {
|
|
3838
|
+
if (active) onProjection({ offline: true, identity, messages: [] });
|
|
3839
|
+
} finally {
|
|
3840
|
+
acknowledging.delete(messageId);
|
|
3841
|
+
}
|
|
3842
|
+
},
|
|
3843
|
+
stop: () => {
|
|
3844
|
+
if (!active) return
|
|
3845
|
+
active = false;
|
|
3846
|
+
window.clearInterval(timer);
|
|
3847
|
+
document.removeEventListener('visibilitychange', onVisibility);
|
|
3848
|
+
window.removeEventListener('pagehide', release);
|
|
3849
|
+
void release();
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
};
|
|
3853
|
+
|
|
3697
3854
|
const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessage, bubbleLabel, theme, orientation, brandColor, bubbleStyle, useBrandColor, logoUrl, previewMode = false, initialOpen = false }) => {
|
|
3698
3855
|
const [config, setConfig] = useState(null);
|
|
3699
3856
|
const [isOpen, setIsOpen] = useState(initialOpen);
|
|
3700
3857
|
const [messages, setMessages] = useState([]);
|
|
3701
3858
|
const [isLoading, setIsLoading] = useState(false);
|
|
3702
3859
|
const [sessionId, setSessionId] = useState(() => generateSessionId());
|
|
3860
|
+
const handoffRef = useRef(null);
|
|
3861
|
+
const pendingHandoffIdsRef = useRef([]);
|
|
3703
3862
|
const [shadowReady, setShadowReady] = useState(false);
|
|
3704
3863
|
const [showPredefinedQuestions, setShowPredefinedQuestions] = useState(true);
|
|
3705
3864
|
const [userInfoCollected, setUserInfoCollected] = useState(false);
|
|
@@ -3713,6 +3872,29 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
|
|
|
3713
3872
|
const messagesContainerRef = useRef(null);
|
|
3714
3873
|
const welcomePopupShownRef = useRef(false);
|
|
3715
3874
|
const welcomePopupTimeoutRef = useRef(null);
|
|
3875
|
+
useEffect(() => {
|
|
3876
|
+
if (!config?.bootstrap || !config.id)
|
|
3877
|
+
return;
|
|
3878
|
+
handoffRef.current?.stop();
|
|
3879
|
+
const handoff = createBrowserHandoff(config.id, config.bootstrap, projection => {
|
|
3880
|
+
if (projection.offline)
|
|
3881
|
+
return;
|
|
3882
|
+
setSessionId(projection.identity.sessionId);
|
|
3883
|
+
pendingHandoffIdsRef.current = projection.pendingProjectionIds || [];
|
|
3884
|
+
const projectedMessages = projection.messages.map(message => ({ id: message.id, content: message.content, sender: message.role === 'user' ? 'user' : 'bot', timestamp: new Date(message.createdAt).toISOString(), isHuman: message.sentByHuman }));
|
|
3885
|
+
setMessages(projection.control === 'requested' || projection.control === 'human'
|
|
3886
|
+
? [...projectedMessages, { id: 'handoff-waiting', content: config.defaultLang === 'es' ? 'Un miembro del equipo te responderá pronto.' : 'A team member will reply soon.', sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }]
|
|
3887
|
+
: projectedMessages);
|
|
3888
|
+
});
|
|
3889
|
+
handoffRef.current = handoff;
|
|
3890
|
+
return () => {
|
|
3891
|
+
handoff?.stop();
|
|
3892
|
+
handoffRef.current = null;
|
|
3893
|
+
};
|
|
3894
|
+
}, [config?.bootstrap, config?.id]);
|
|
3895
|
+
useEffect(() => {
|
|
3896
|
+
pendingHandoffIdsRef.current.forEach(messageId => void handoffRef.current?.markRendered(messageId));
|
|
3897
|
+
}, [messages]);
|
|
3716
3898
|
// Create shadow DOM container on mount
|
|
3717
3899
|
useEffect(() => {
|
|
3718
3900
|
if (!containerRef.current)
|
|
@@ -4039,16 +4221,21 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
|
|
|
4039
4221
|
sender: 'user',
|
|
4040
4222
|
timestamp: new Date().toISOString()
|
|
4041
4223
|
};
|
|
4224
|
+
handoffRef.current?.recordVisitorMessage(messageText);
|
|
4042
4225
|
setMessages(prev => [...prev, userMessage]);
|
|
4043
4226
|
if (hidePredefined) {
|
|
4044
4227
|
setShowPredefinedQuestions(false);
|
|
4045
4228
|
}
|
|
4046
4229
|
try {
|
|
4047
4230
|
setIsLoading(true);
|
|
4048
|
-
const response = await
|
|
4231
|
+
const response = await sendMessageForWidget(embedId, sessionId, messageText);
|
|
4232
|
+
if (response.status === 'waiting') {
|
|
4233
|
+
setMessages(prev => prev.some(message => message.isWaiting) ? prev : [...prev, { id: 'handoff-waiting', content: config?.defaultLang === 'es' ? 'Un miembro del equipo te responderá pronto.' : 'A team member will reply soon.', sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }]);
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4049
4236
|
const botMessage = {
|
|
4050
4237
|
id: timestamp + 1,
|
|
4051
|
-
content: response,
|
|
4238
|
+
content: response.text,
|
|
4052
4239
|
sender: 'bot',
|
|
4053
4240
|
timestamp: new Date().toISOString()
|
|
4054
4241
|
};
|
|
@@ -4241,7 +4428,7 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
|
|
|
4241
4428
|
if (message.isLoading) {
|
|
4242
4429
|
return (jsx("div", { className: 'andy-message-container andy-message-container--bot andy-message-container--loading', children: jsx("div", { className: 'andy-message andy-message--bot', children: jsxs("div", { className: 'andy-typing-indicator', children: [jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' })] }) }) }, message.id));
|
|
4243
4430
|
}
|
|
4244
|
-
return (jsxs("div", { className: `andy-message-container andy-message-container--${message.sender}`, children: [message.sender === 'bot' && (jsxs("div", { className: `andy-message andy-message--bot ${message.isError ? 'andy-message--error' : ''}`, children: [jsxs("div", { className: 'andy-bot-header', children: [jsx("div", { className: 'andy-bot-header__icon', children: config?.logoUrl ? (jsx("img", { src: config.logoUrl, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image' })) : (jsx("img", { src: CHAT_BUBBLE_DEFAULT_ICON_URL, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image andy-bot-header__icon-image--default' })) }), jsx("span", { className: 'andy-bot-header__name', children: config?.name || 'Andy bot' })] }), jsx("div", { className: 'andy-message__content', children: jsx("div", { dangerouslySetInnerHTML: { __html: parseMarkdown(message.content) } }) })] })), message.sender === 'user' && (jsx("div", { className: 'andy-message andy-message--user', children: jsx("div", { className: 'andy-message__content', children: jsx("p", { children: message.content }) }) }))] }, message.id));
|
|
4431
|
+
return (jsxs("div", { className: `andy-message-container andy-message-container--${message.sender} ${message.isWaiting ? 'andy-message-container--waiting' : ''}`, children: [message.sender === 'bot' && (jsxs("div", { className: `andy-message andy-message--bot ${message.isHuman ? 'andy-message--operator' : ''} ${message.isWaiting ? 'andy-message--waiting' : ''} ${message.isError ? 'andy-message--error' : ''}`, children: [jsxs("div", { className: 'andy-bot-header', children: [jsx("div", { className: 'andy-bot-header__icon', children: config?.logoUrl ? (jsx("img", { src: config.logoUrl, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image' })) : (jsx("img", { src: CHAT_BUBBLE_DEFAULT_ICON_URL, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image andy-bot-header__icon-image--default' })) }), jsx("span", { className: 'andy-bot-header__name', children: message.isHuman ? (config?.defaultLang === 'es' ? 'Equipo' : 'Team') : config?.name || 'Andy bot' })] }), jsx("div", { className: 'andy-message__content', children: jsx("div", { dangerouslySetInnerHTML: { __html: parseMarkdown(message.content) } }) })] })), message.sender === 'user' && (jsx("div", { className: 'andy-message andy-message--user', children: jsx("div", { className: 'andy-message__content', children: jsx("p", { children: message.content }) }) }))] }, message.id));
|
|
4245
4432
|
}), isLoading && (jsx("div", { className: 'andy-message-container andy-message-container--bot andy-message-container--typing', children: jsx("div", { className: 'andy-message andy-message--bot andy-message--typing', children: jsxs("div", { className: 'andy-typing-indicator', children: [jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' })] }) }) }))] }), jsxs("div", { className: 'andy-chat-window__bottom', children: [config?.predefinedQuestions &&
|
|
4246
4433
|
config.predefinedQuestions.length > 0 &&
|
|
4247
4434
|
showPredefinedQuestions && (jsxs("div", { className: 'andy-predefined-questions', id: 'andy-predefined-questions', children: [jsx("div", { className: 'andy-predefined-questions__header', children: jsx("p", { className: 'andy-predefined-questions__title', children: "What would you like to know?" }) }), jsx("div", { className: 'andy-predefined-questions__list', children: config.predefinedQuestions.map((q, index) => (jsxs("button", { type: 'button', className: 'andy-predefined-question', "data-question": q.question, disabled: isCollectingUserInfo || isLoading || !config, children: [jsx("span", { className: 'andy-predefined-question__icon', children: "\uD83D\uDCAC" }), jsx("span", { className: 'andy-predefined-question__text', children: q.question })] }, index))) })] })), jsx("div", { className: 'andy-chat-window__input', children: jsx("div", { className: 'andy-input-container', children: jsxs("div", { className: 'andy-input', children: [jsx("input", { type: 'text', className: 'andy-input__field', id: 'andy-message-input', placeholder: config ? 'Type your message...' : '...', "aria-label": 'Message input', onKeyDown: e => {
|
|
@@ -4346,7 +4533,7 @@ const useAndyChat = (embedId) => {
|
|
|
4346
4533
|
return newIsOpen;
|
|
4347
4534
|
});
|
|
4348
4535
|
}, [config, welcomePopupShown, userInfoCollected]);
|
|
4349
|
-
const sendMessage
|
|
4536
|
+
const sendMessage = useCallback(async (messageText) => {
|
|
4350
4537
|
if (!messageText.trim() || !config || !sessionId) {
|
|
4351
4538
|
return;
|
|
4352
4539
|
}
|
|
@@ -4358,10 +4545,12 @@ const useAndyChat = (embedId) => {
|
|
|
4358
4545
|
};
|
|
4359
4546
|
setMessages(prev => [...prev, userMessage]);
|
|
4360
4547
|
try {
|
|
4361
|
-
const response = await
|
|
4548
|
+
const response = await sendMessageForWidget(embedId, sessionId, messageText);
|
|
4549
|
+
if (response.status === 'waiting')
|
|
4550
|
+
return response;
|
|
4362
4551
|
const botMessage = {
|
|
4363
4552
|
id: Date.now() + 1,
|
|
4364
|
-
content: response || 'I apologize, but I encountered an error processing your request.',
|
|
4553
|
+
content: response.text || 'I apologize, but I encountered an error processing your request.',
|
|
4365
4554
|
sender: 'bot',
|
|
4366
4555
|
timestamp: new Date().toISOString()
|
|
4367
4556
|
};
|
|
@@ -4458,7 +4647,7 @@ const useAndyChat = (embedId) => {
|
|
|
4458
4647
|
error,
|
|
4459
4648
|
initializeChatbot,
|
|
4460
4649
|
toggleChat,
|
|
4461
|
-
sendMessage
|
|
4650
|
+
sendMessage,
|
|
4462
4651
|
showFaq,
|
|
4463
4652
|
hideFaq,
|
|
4464
4653
|
showWelcomePopup,
|