shopify-chatbot-widget 0.7.9 → 0.8.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.
- package/dist/jaweb-chatbot.css +1 -1
- package/dist/jaweb-chatbot.iife.js +3 -3
- package/package.json +1 -1
- package/src/App.css +42 -0
- package/src/App.tsx +13 -0
- package/src/Chatbot/Chatbot.css +270 -0
- package/src/Chatbot/Chatbot.tsx +214 -0
- package/src/Chatbot/ChatbotTransition.css +19 -0
- package/src/Chatbot/OLDCHAT.tsx +259 -0
- package/src/Chatbot/components/ChatInput.css +127 -0
- package/src/Chatbot/components/ChatInput.tsx +354 -0
- package/src/Chatbot/components/Messages/Addcart.tsx +70 -0
- package/src/Chatbot/components/Messages/AgentStatus.tsx +37 -0
- package/src/Chatbot/components/Messages/AssistantMesage.tsx +206 -0
- package/src/Chatbot/components/Messages/AudioMessage.tsx +123 -0
- package/src/Chatbot/components/Messages/CalendlyEmbeds.tsx +66 -0
- package/src/Chatbot/components/Messages/CardSwiper.tsx +194 -0
- package/src/Chatbot/components/Messages/Carousel/CardSwiper.tsx +264 -0
- package/src/Chatbot/components/Messages/Carousel/CardSwiperSalla.tsx +208 -0
- package/src/Chatbot/components/Messages/Carousel/CardSwiperZid.tsx +208 -0
- package/src/Chatbot/components/Messages/Carousel/SallaSwiper.css +245 -0
- package/src/Chatbot/components/Messages/Carousel/TryOn.tsx +111 -0
- package/src/Chatbot/components/Messages/CartWidget.tsx +151 -0
- package/src/Chatbot/components/Messages/ChatImage.tsx +46 -0
- package/src/Chatbot/components/Messages/ChatImageText.tsx +74 -0
- package/src/Chatbot/components/Messages/ChatImageTextAssis.tsx +72 -0
- package/src/Chatbot/components/Messages/ChatMessages.css +31 -0
- package/src/Chatbot/components/Messages/ChatMessages.tsx +334 -0
- package/src/Chatbot/components/Messages/ProductCard.css +149 -0
- package/src/Chatbot/components/Messages/SallaAssistantMessage.tsx +196 -0
- package/src/Chatbot/components/Messages/Support.tsx +177 -0
- package/src/Chatbot/components/Messages/Typing.css +31 -0
- package/src/Chatbot/components/Messages/Typing.tsx +19 -0
- package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +196 -0
- package/src/Chatbot/components/Messages/cartwidget.css +123 -0
- package/src/Chatbot/components/Messenger/Messenger.tsx +531 -0
- package/src/Chatbot/components/Notification.tsx +28 -0
- package/src/Chatbot/components/Powered.css +29 -0
- package/src/Chatbot/components/Powered.tsx +12 -0
- package/src/Chatbot/components/Sessions/CreateSession.tsx +201 -0
- package/src/Chatbot/components/Sessions/RenderList.tsx +212 -0
- package/src/Chatbot/components/Suggestions.css +35 -0
- package/src/Chatbot/components/Suggestions.tsx +57 -0
- package/src/Chatbot/notification.css +17 -0
- package/src/assets/react.svg +1 -0
- package/src/chatbot-widget/index.tsx +14 -0
- package/src/hooks/config.tsx +13 -0
- package/src/hooks/useCart.tsx +107 -0
- package/src/hooks/useChatbotAPI.tsx +167 -0
- package/src/hooks/useChatbotData.tsx +132 -0
- package/src/hooks/useSessionMessages.tsx +50 -0
- package/src/hooks/useWebsocke.tsx +45 -0
- package/src/i18n.tsx +30 -0
- package/src/index.css +2 -0
- package/src/main.tsx +10 -0
- package/src/types/chatbot.ts +40 -0
- package/src/utils/cookies.tsx +14 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import React, { useState,useCallback } from 'react';
|
|
2
|
+
import { Button, Avatar } from 'antd';
|
|
3
|
+
import { CloseOutlined,ArrowLeftOutlined } from '@ant-design/icons';
|
|
4
|
+
import '../../Chatbot.css';
|
|
5
|
+
import { useChatbotMessage } from '../../../hooks/useSessionMessages';
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import ChatInput from '../../components/ChatInput';
|
|
8
|
+
import Suggestions from '../../components/Suggestions';
|
|
9
|
+
import Powered from '../../components/Powered';
|
|
10
|
+
import ChatMessages from '../../components/Messages/ChatMessages';
|
|
11
|
+
import config from '../../../hooks/config';
|
|
12
|
+
import { useChatbotAPI } from '../../../hooks/useChatbotAPI';
|
|
13
|
+
import type { ChatMessage } from '../../../types/chatbot';
|
|
14
|
+
import { getCookie } from '../../../utils/cookies';
|
|
15
|
+
import useChatSocket from '../../../hooks/useWebsocke';
|
|
16
|
+
import Notification from '../Notification';
|
|
17
|
+
import CartWidget from '../Messages/CartWidget';
|
|
18
|
+
import { useCart } from '../../../hooks/useCart';
|
|
19
|
+
|
|
20
|
+
type MessengerProps = {
|
|
21
|
+
chatbotLogo: string;
|
|
22
|
+
userType:string;
|
|
23
|
+
agentName:string;
|
|
24
|
+
colorCode: string;
|
|
25
|
+
newSession: boolean;
|
|
26
|
+
messagesSuggestions: string[];
|
|
27
|
+
disableForm: boolean;
|
|
28
|
+
sessionId:string;
|
|
29
|
+
setOpen: (v: boolean) => void;
|
|
30
|
+
setView: (v: string) => void;
|
|
31
|
+
setNewSession: (v: boolean) => void;
|
|
32
|
+
Opened: boolean;
|
|
33
|
+
calendly:string;
|
|
34
|
+
disbleCheckout:boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface UploadImageResponse {
|
|
38
|
+
image_url: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
interface UploadAudioResponse {
|
|
43
|
+
audio_url: string;
|
|
44
|
+
transcription_text:string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const Messenger: React.FC<MessengerProps> = ({
|
|
48
|
+
chatbotLogo,
|
|
49
|
+
agentName,
|
|
50
|
+
colorCode,
|
|
51
|
+
newSession,
|
|
52
|
+
userType,
|
|
53
|
+
setNewSession,
|
|
54
|
+
messagesSuggestions,
|
|
55
|
+
disableForm,
|
|
56
|
+
sessionId,
|
|
57
|
+
setOpen,
|
|
58
|
+
Opened,
|
|
59
|
+
setView,
|
|
60
|
+
calendly,
|
|
61
|
+
disbleCheckout
|
|
62
|
+
}) => {
|
|
63
|
+
const [input, setInput] = useState('');
|
|
64
|
+
const [selectedImage, setSelectedImage] = useState<File | null>(null);
|
|
65
|
+
const [audioBlob, setAudioBlob] = useState<Blob | null>(null);
|
|
66
|
+
const [isSending, setIsSending] = useState(false);
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
const [, setNewUser] = useState(() => {
|
|
72
|
+
const cookieValue = getCookie('new_user');
|
|
73
|
+
return cookieValue === 'true';
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const [isChatlogCreated, setIsChatlogCreated] = useState(false);
|
|
78
|
+
const [, setChatLogWaiting] = useState(false);
|
|
79
|
+
const [createNew, setCreateNew] = useState(false);
|
|
80
|
+
const [, setSessionCreated] = useState(false);
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
const { messages, setMessages,isFetchingMessages } = useChatbotMessage(sessionId);
|
|
84
|
+
;
|
|
85
|
+
const [agentStatus, setAgentStatus] = useState<{ status?: string; typing?: boolean; user?: string }>({});
|
|
86
|
+
const [notification, setNotification] = useState<string | null>(null);
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
const handleSocketData = useCallback(
|
|
93
|
+
(data: any) => {
|
|
94
|
+
if (data.status) {
|
|
95
|
+
setAgentStatus({
|
|
96
|
+
status: data.status,
|
|
97
|
+
user: data.user || 'Support',
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const { additionalData = {}, message, image_url } = data;
|
|
102
|
+
if (!additionalData.isBusiness && additionalData.sender !== 'AI') {
|
|
103
|
+
setMessages((prev) => [
|
|
104
|
+
...prev,
|
|
105
|
+
{
|
|
106
|
+
role: 'assistant',
|
|
107
|
+
content: message,
|
|
108
|
+
isBusiness: false,
|
|
109
|
+
sender: additionalData.sender,
|
|
110
|
+
image_url,
|
|
111
|
+
} as ChatMessage,
|
|
112
|
+
]);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
[setMessages]
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const socket = useChatSocket(sessionId, handleSocketData);
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
const { apiCall } = useChatbotAPI({
|
|
123
|
+
disableChatbot: false,
|
|
124
|
+
disableChatbotGlobally: false,
|
|
125
|
+
isChatlogCreated,
|
|
126
|
+
createNew,
|
|
127
|
+
disableForm: disableForm,
|
|
128
|
+
calendlyLink: calendly,
|
|
129
|
+
socket:socket,
|
|
130
|
+
newSession:newSession,
|
|
131
|
+
setMessages,
|
|
132
|
+
setIsChatlogCreated,
|
|
133
|
+
setChatLogWaiting,
|
|
134
|
+
setNewUser,
|
|
135
|
+
setCreateNew,
|
|
136
|
+
setSessionCreated,
|
|
137
|
+
setNewSession:setNewSession
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
const sendMessage = async () => {
|
|
143
|
+
if (!input.trim()) return;
|
|
144
|
+
|
|
145
|
+
let message=input
|
|
146
|
+
setIsSending(true); // 🔒 Disable input
|
|
147
|
+
const userMsg: ChatMessage = { role: 'user', content: message };
|
|
148
|
+
setMessages(prev => [...prev, userMsg]);
|
|
149
|
+
|
|
150
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
151
|
+
const additionalData = { isBusiness: true, SessionId: sessionId };
|
|
152
|
+
const messagePayload = JSON.stringify({
|
|
153
|
+
message: message,
|
|
154
|
+
additionalData: additionalData,
|
|
155
|
+
});
|
|
156
|
+
socket.send(messagePayload);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
await apiCall(input, newSession, sessionId);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.error(err);
|
|
163
|
+
} finally {
|
|
164
|
+
setIsSending(false); // 🔓 Re-enable input
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const sendMessageSuggestion = async (message: string): Promise<void> => {
|
|
169
|
+
if (!message.trim()) return;
|
|
170
|
+
|
|
171
|
+
setIsSending(true); // 🔒 Disable input
|
|
172
|
+
|
|
173
|
+
const userMsg: ChatMessage = { role: 'user', content: message };
|
|
174
|
+
setMessages(prev => [...prev, userMsg]);
|
|
175
|
+
|
|
176
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
177
|
+
const additionalData = { isBusiness: true, SessionId: sessionId };
|
|
178
|
+
const messagePayload = JSON.stringify({
|
|
179
|
+
message,
|
|
180
|
+
additionalData,
|
|
181
|
+
});
|
|
182
|
+
socket.send(messagePayload);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
await apiCall(message, newSession, sessionId);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error("Error sending suggestion:", error);
|
|
189
|
+
} finally {
|
|
190
|
+
setIsSending(false); // 🔓 Re-enable input
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async function handleSendImage (image: File, userMessage:string) {
|
|
197
|
+
const tempMessageId = Date.now();
|
|
198
|
+
const tempImageUrl = URL.createObjectURL(image);
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
if (!image) return;
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
const tempJson: ChatMessage = {
|
|
209
|
+
id: tempMessageId,
|
|
210
|
+
role: 'user', // ✅ satisfies union
|
|
211
|
+
content: userMessage || '',
|
|
212
|
+
image_url: tempImageUrl,
|
|
213
|
+
isBusiness: true,
|
|
214
|
+
status: 'uploading',
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
setMessages((prevMessages) => {
|
|
221
|
+
const updatedMessages = [...prevMessages, tempJson];
|
|
222
|
+
return updatedMessages;
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
let finalImageUrl: string | null = null;
|
|
227
|
+
|
|
228
|
+
if (image) {
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const formData = new FormData();
|
|
232
|
+
formData.append("image", image);
|
|
233
|
+
|
|
234
|
+
const uploadResponse = await axios.post<UploadImageResponse>(
|
|
235
|
+
`${config.apiUrl}upload-image/`,
|
|
236
|
+
formData
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
if (uploadResponse.status === 201 && uploadResponse.data?.image_url) {
|
|
241
|
+
finalImageUrl = uploadResponse.data.image_url;
|
|
242
|
+
setMessages((prevMessages) => {
|
|
243
|
+
const updatedMessages = prevMessages.map((msg) =>
|
|
244
|
+
msg.id === tempMessageId && finalImageUrl
|
|
245
|
+
? { ...msg, image_url: finalImageUrl } // ✅ only if finalImageUrl is not null
|
|
246
|
+
: msg
|
|
247
|
+
);
|
|
248
|
+
return updatedMessages;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error("Error uploading image:", error);
|
|
253
|
+
setMessages((messageList) =>
|
|
254
|
+
messageList.map((msg) =>
|
|
255
|
+
msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
266
|
+
const additionalData = { isBusiness: true, SessionId: sessionId };
|
|
267
|
+
const messagePayload = JSON.stringify({
|
|
268
|
+
image_url: finalImageUrl || null,
|
|
269
|
+
message: input.trim()||"",
|
|
270
|
+
additionalData: additionalData,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
socket.send(messagePayload);
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
if (finalImageUrl) {
|
|
279
|
+
setMessages((messageList) =>
|
|
280
|
+
messageList.map((msg) =>
|
|
281
|
+
msg.id === tempMessageId ? { ...msg, status: 'delivered' } : msg
|
|
282
|
+
)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
apiCall(userMessage.trim() || '',newSession,sessionId,finalImageUrl,true)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.error('Error sending Image message:', error);
|
|
293
|
+
|
|
294
|
+
setMessages((messageList) =>
|
|
295
|
+
messageList.map((msg) =>
|
|
296
|
+
msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
|
|
297
|
+
)
|
|
298
|
+
);
|
|
299
|
+
} finally {
|
|
300
|
+
setSelectedImage(null);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
const handleSendVoice = async (audio: Blob) => {
|
|
308
|
+
if (!audio) return;
|
|
309
|
+
|
|
310
|
+
const tempAudioUrl = URL.createObjectURL(audio);
|
|
311
|
+
const tempMessageId = Date.now();
|
|
312
|
+
|
|
313
|
+
// Step 1: Add temporary audio message
|
|
314
|
+
const tempMessage: ChatMessage = {
|
|
315
|
+
id: tempMessageId,
|
|
316
|
+
role: 'user',
|
|
317
|
+
content: '',
|
|
318
|
+
audio_url: tempAudioUrl,
|
|
319
|
+
isBusiness: true,
|
|
320
|
+
status: 'uploading',
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
setMessages(prev => [...prev, tempMessage]);
|
|
324
|
+
|
|
325
|
+
let finalAudioUrl: string | undefined = undefined;
|
|
326
|
+
let transcriptionText: string = '';
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
// Step 2: Upload audio to backend
|
|
330
|
+
const formData = new FormData();
|
|
331
|
+
formData.append('audio', audio);
|
|
332
|
+
|
|
333
|
+
const uploadResponse = await axios.post<UploadAudioResponse>(
|
|
334
|
+
`${config.apiUrl}upload-audio/`,
|
|
335
|
+
formData
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
if (uploadResponse.status === 201) {
|
|
344
|
+
finalAudioUrl = uploadResponse.data.audio_url;
|
|
345
|
+
transcriptionText = uploadResponse.data.transcription_text || '[Audio]';
|
|
346
|
+
|
|
347
|
+
// Step 3: Replace temp audio URL with final URL
|
|
348
|
+
setMessages(prev =>
|
|
349
|
+
prev.map(msg =>
|
|
350
|
+
msg.id === tempMessageId
|
|
351
|
+
? {
|
|
352
|
+
...msg,
|
|
353
|
+
audio_url: finalAudioUrl,
|
|
354
|
+
content: transcriptionText,
|
|
355
|
+
status: 'delivered',
|
|
356
|
+
}
|
|
357
|
+
: msg
|
|
358
|
+
)
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
} catch (error) {
|
|
362
|
+
console.error('Audio upload failed:', error);
|
|
363
|
+
setMessages(prev =>
|
|
364
|
+
prev.map(msg =>
|
|
365
|
+
msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
// Step 4: Send to WebSocket
|
|
373
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
374
|
+
const payload = JSON.stringify({
|
|
375
|
+
audio_url: finalAudioUrl,
|
|
376
|
+
message: transcriptionText,
|
|
377
|
+
additionalData: {
|
|
378
|
+
isBusiness: true,
|
|
379
|
+
SessionId: sessionId,
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
socket.send(payload);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Step 5: Send to API
|
|
386
|
+
if (finalAudioUrl) {
|
|
387
|
+
apiCall(transcriptionText,newSession ,sessionId, finalAudioUrl, false);
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
console.error('Sending audio to API/WebSocket failed:', error);
|
|
391
|
+
setMessages(prev =>
|
|
392
|
+
prev.map(msg =>
|
|
393
|
+
msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
|
|
394
|
+
)
|
|
395
|
+
);
|
|
396
|
+
} finally {
|
|
397
|
+
setAudioBlob(null); // Step 6: cleanup
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const { items, loading, addToCart, removeFromCart, refresh } = useCart(sessionId);
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
// replace your old handleToCart:
|
|
405
|
+
function shorten(str: string, maxLen = 20) {
|
|
406
|
+
return str.length > maxLen ? `${str.slice(0, maxLen)}…` : str;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const handleToCart = useCallback(
|
|
410
|
+
async (variantId: string, name: string, qty = 1) => {
|
|
411
|
+
try {
|
|
412
|
+
await addToCart(variantId, name, qty);
|
|
413
|
+
|
|
414
|
+
const shortName = shorten(name, 20);
|
|
415
|
+
setNotification(`✅ ${qty}× ${shortName} added to cart`);
|
|
416
|
+
} catch (err: any) {
|
|
417
|
+
setNotification(`❌ ${err.message || "Failed to add item"}`);
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
[addToCart]
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const handleRemoveFromCart = useCallback(
|
|
424
|
+
async (variantId: string, qty = 1) => {
|
|
425
|
+
await removeFromCart(variantId, qty);
|
|
426
|
+
setNotification(`❌ Removed ${qty} item(s) from cart`);
|
|
427
|
+
},
|
|
428
|
+
[removeFromCart]
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
return (
|
|
435
|
+
<div className="chatbot-box">
|
|
436
|
+
<div className="chatbot-header">
|
|
437
|
+
{/* Back Button */}
|
|
438
|
+
<Button
|
|
439
|
+
type="text"
|
|
440
|
+
style={{ marginRight: 8 }}
|
|
441
|
+
onClick={() => setView('sessions')} // or use a callback like `goBack()` if needed
|
|
442
|
+
icon={<ArrowLeftOutlined />}
|
|
443
|
+
/>
|
|
444
|
+
|
|
445
|
+
{/* Avatar + Title */}
|
|
446
|
+
<Avatar
|
|
447
|
+
src={
|
|
448
|
+
chatbotLogo ||
|
|
449
|
+
'https://t4.ftcdn.net/jpg/08/90/53/75/360_F_890537572_m7EIYLzXgeTYGDNoIr7KEDOElFUTzFy0.jpg'
|
|
450
|
+
}
|
|
451
|
+
/>
|
|
452
|
+
<span className="chatbot-title">{agentName || 'Ai Assistant'}</span>
|
|
453
|
+
|
|
454
|
+
{items.length > 0 && (
|
|
455
|
+
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
456
|
+
<CartWidget
|
|
457
|
+
items={items}
|
|
458
|
+
loading={loading}
|
|
459
|
+
sessionId={sessionId}
|
|
460
|
+
onRefresh={refresh}
|
|
461
|
+
onRemove={removeFromCart}
|
|
462
|
+
colorCode={colorCode}
|
|
463
|
+
sendMessage={sendMessageSuggestion}
|
|
464
|
+
/>
|
|
465
|
+
</div>
|
|
466
|
+
)}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
{/* Close Button */}
|
|
470
|
+
<CloseOutlined
|
|
471
|
+
onClick={() => setOpen(!Opened)}
|
|
472
|
+
className="chatbot-close"
|
|
473
|
+
style={{ marginLeft: 'auto' }}
|
|
474
|
+
/>
|
|
475
|
+
</div>
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
{notification && (
|
|
479
|
+
<Notification
|
|
480
|
+
message={notification}
|
|
481
|
+
onClose={() => setNotification(null)}
|
|
482
|
+
/>
|
|
483
|
+
)}
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
<ChatMessages
|
|
492
|
+
messages={messages}
|
|
493
|
+
colorCode={colorCode}
|
|
494
|
+
isLoading={false}
|
|
495
|
+
handleAddToCart={handleToCart}
|
|
496
|
+
agentStatus={agentStatus}
|
|
497
|
+
sessionId={sessionId}
|
|
498
|
+
handleRemoveFromCart={handleRemoveFromCart}
|
|
499
|
+
userType={userType}
|
|
500
|
+
calendly={calendly}
|
|
501
|
+
disableCheckout={disbleCheckout}
|
|
502
|
+
isfetchingMessage={isFetchingMessages}
|
|
503
|
+
/>
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
<div className="chatbot-input-area">
|
|
509
|
+
<Suggestions sendMessageSuggestion={sendMessageSuggestion} colorCode={colorCode} suggestions={messagesSuggestions} />
|
|
510
|
+
|
|
511
|
+
<ChatInput
|
|
512
|
+
userMessage={input}
|
|
513
|
+
setUserMessage={setInput}
|
|
514
|
+
handleSendMessage={sendMessage}
|
|
515
|
+
handleSendImage={handleSendImage}
|
|
516
|
+
handleSendVoice={handleSendVoice}
|
|
517
|
+
selectedImage={selectedImage}
|
|
518
|
+
setSelectedImage={setSelectedImage}
|
|
519
|
+
audioBlob={audioBlob}
|
|
520
|
+
setAudioBlob={setAudioBlob}
|
|
521
|
+
isSending={isSending}
|
|
522
|
+
|
|
523
|
+
/>
|
|
524
|
+
|
|
525
|
+
<Powered />
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
);
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
export default Messenger;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/components/Notification.tsx
|
|
2
|
+
import React, { useEffect } from 'react';
|
|
3
|
+
import '../notification.css'; // Import the CSS
|
|
4
|
+
|
|
5
|
+
interface NotificationProps {
|
|
6
|
+
message: string;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Notification: React.FC<NotificationProps> = ({
|
|
12
|
+
message,
|
|
13
|
+
onClose,
|
|
14
|
+
duration = 1000,
|
|
15
|
+
}) => {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const timer = setTimeout(onClose, duration);
|
|
18
|
+
return () => clearTimeout(timer);
|
|
19
|
+
}, [onClose, duration]);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="jawebcss-notification">
|
|
23
|
+
{message}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default Notification;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
.jawebcss-powered-container {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
margin-top: 10px;
|
|
4
|
+
align-items: center;
|
|
5
|
+
gap: 6px;
|
|
6
|
+
font-family: Arial, sans-serif;
|
|
7
|
+
font-size: 14px;
|
|
8
|
+
color: #555;
|
|
9
|
+
padding: 4px 8px;
|
|
10
|
+
border-radius: 6px;
|
|
11
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.jawebcss-powered-label {
|
|
15
|
+
font-weight: 400;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.jawebcss-powered-link {
|
|
19
|
+
color: #7f28f8;
|
|
20
|
+
font-weight: 600;
|
|
21
|
+
text-decoration: none;
|
|
22
|
+
transition: color 0.2s ease;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.jawebcss-powered-link:hover {
|
|
26
|
+
color: #5e1dc3;
|
|
27
|
+
text-decoration: underline;
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import './Powered.css';
|
|
2
|
+
|
|
3
|
+
export default function Powered() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="jawebcss-powered-container">
|
|
6
|
+
<span className="jawebcss-powered-label">Powered by</span>
|
|
7
|
+
<a href="https://jaweb.me/" target="_blank" className="jawebcss-powered-link" rel="noopener noreferrer">
|
|
8
|
+
Jaweb
|
|
9
|
+
</a>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
}
|