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