shopify-chatbot-widget 0.0.4 β†’ 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/jaweb-chatbot.css +1 -0
  2. package/dist/jaweb-chatbot.iife.js +685 -0
  3. package/dist/vite.svg +1 -0
  4. package/package.json +1 -1
  5. package/src/App.css +0 -42
  6. package/src/App.tsx +0 -13
  7. package/src/Chatbot/Chatbot.css +0 -154
  8. package/src/Chatbot/Chatbot.tsx +0 -207
  9. package/src/Chatbot/components/ChatInput.css +0 -127
  10. package/src/Chatbot/components/ChatInput.tsx +0 -262
  11. package/src/Chatbot/components/Messages/Addcart.tsx +0 -70
  12. package/src/Chatbot/components/Messages/AgentStatus.tsx +0 -37
  13. package/src/Chatbot/components/Messages/AssistantMesage.tsx +0 -316
  14. package/src/Chatbot/components/Messages/AudioMessage.tsx +0 -123
  15. package/src/Chatbot/components/Messages/CartWidget.tsx +0 -116
  16. package/src/Chatbot/components/Messages/ChatImage.tsx +0 -46
  17. package/src/Chatbot/components/Messages/ChatImageText.tsx +0 -74
  18. package/src/Chatbot/components/Messages/ChatImageTextAssis.tsx +0 -72
  19. package/src/Chatbot/components/Messages/ChatMessages.css +0 -5
  20. package/src/Chatbot/components/Messages/ChatMessages.tsx +0 -284
  21. package/src/Chatbot/components/Messages/Support.tsx +0 -177
  22. package/src/Chatbot/components/Messages/Typing.css +0 -27
  23. package/src/Chatbot/components/Messages/Typing.tsx +0 -14
  24. package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +0 -142
  25. package/src/Chatbot/components/Messenger/Messenger.tsx +0 -521
  26. package/src/Chatbot/components/Notification.tsx +0 -27
  27. package/src/Chatbot/components/Powered.css +0 -16
  28. package/src/Chatbot/components/Powered.tsx +0 -13
  29. package/src/Chatbot/components/Sessions/CreateSession.tsx +0 -139
  30. package/src/Chatbot/components/Sessions/RenderList.tsx +0 -202
  31. package/src/Chatbot/components/Suggestions.css +0 -34
  32. package/src/Chatbot/components/Suggestions.tsx +0 -28
  33. package/src/assets/react.svg +0 -1
  34. package/src/chatbot-widget/index.tsx +0 -14
  35. package/src/hooks/config.tsx +0 -13
  36. package/src/hooks/useCart.tsx +0 -99
  37. package/src/hooks/useChatbotAPI.tsx +0 -160
  38. package/src/hooks/useChatbotData.tsx +0 -129
  39. package/src/hooks/useSessionMessages.tsx +0 -51
  40. package/src/hooks/useWebsocke.tsx +0 -45
  41. package/src/i18n.tsx +0 -30
  42. package/src/index.css +0 -1
  43. package/src/main.tsx +0 -10
  44. package/src/types/chatbot.ts +0 -38
  45. package/src/utils/cookies.tsx +0 -14
  46. package/src/vite-env.d.ts +0 -1
@@ -1,521 +0,0 @@
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
-
33
-
34
- Opened: 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
- }) => {
61
- const [input, setInput] = useState('');
62
- const [selectedImage, setSelectedImage] = useState<File | null>(null);
63
- const [audioBlob, setAudioBlob] = useState<Blob | null>(null);
64
- const [isSending, setIsSending] = useState(false);
65
-
66
-
67
-
68
-
69
- const [, setNewUser] = useState(() => {
70
- const cookieValue = getCookie('new_user');
71
- return cookieValue === 'true';
72
- });
73
-
74
-
75
- const [isChatlogCreated, setIsChatlogCreated] = useState(false);
76
- const [, setChatLogWaiting] = useState(false);
77
- const [createNew, setCreateNew] = useState(false);
78
- const [, setSessionCreated] = useState(false);
79
-
80
-
81
- const { messages, setMessages } = useChatbotMessage(sessionId);
82
- ;
83
- const [agentStatus, setAgentStatus] = useState<{ status?: string; typing?: boolean; user?: string }>({});
84
- const [notification, setNotification] = useState<string | null>(null);
85
-
86
-
87
-
88
-
89
-
90
- const handleSocketData = useCallback(
91
- (data: any) => {
92
- if (data.status) {
93
- setAgentStatus({
94
- status: data.status,
95
- user: data.user || 'Support',
96
- });
97
- return;
98
- }
99
- const { additionalData = {}, message, image_url } = data;
100
- if (!additionalData.isBusiness && additionalData.sender !== 'AI') {
101
- setMessages((prev) => [
102
- ...prev,
103
- {
104
- role: 'assistant',
105
- content: message,
106
- isBusiness: false,
107
- sender: additionalData.sender,
108
- image_url,
109
- } as ChatMessage,
110
- ]);
111
- }
112
- },
113
- [setMessages]
114
- );
115
-
116
- const socket = useChatSocket(sessionId, handleSocketData);
117
-
118
-
119
-
120
- const { apiCall } = useChatbotAPI({
121
- disableChatbot: false,
122
- disableChatbotGlobally: false,
123
- isChatlogCreated,
124
- createNew,
125
- disableForm: disableForm,
126
- calendlyLink: null,
127
- socket:socket,
128
- newSession:newSession,
129
- setMessages,
130
- setIsChatlogCreated,
131
- setChatLogWaiting,
132
- setNewUser,
133
- setCreateNew,
134
- setSessionCreated,
135
- setNewSession:setNewSession
136
- });
137
-
138
-
139
-
140
- const sendMessage = async () => {
141
- if (!input.trim()) return;
142
-
143
- let message=input
144
- setInput('');
145
- setIsSending(true); // πŸ”’ Disable input
146
- const userMsg: ChatMessage = { role: 'user', content: message };
147
- setMessages(prev => [...prev, userMsg]);
148
-
149
- if (socket && socket.readyState === WebSocket.OPEN) {
150
- const additionalData = { isBusiness: true, SessionId: sessionId };
151
- const messagePayload = JSON.stringify({
152
- message: message,
153
- additionalData: additionalData,
154
- });
155
- socket.send(messagePayload);
156
- }
157
-
158
- try {
159
- await apiCall(input, newSession, sessionId);
160
- } catch (err) {
161
- console.error(err);
162
- } finally {
163
- setInput('');
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
- setInput('');
191
- setIsSending(false); // πŸ”“ Re-enable input
192
- }
193
- };
194
-
195
-
196
-
197
- async function handleSendImage (image: File, userMessage:string) {
198
- const tempMessageId = Date.now();
199
- const tempImageUrl = URL.createObjectURL(image);
200
-
201
-
202
-
203
- if (!image) return;
204
-
205
-
206
-
207
-
208
-
209
- const tempJson: ChatMessage = {
210
- id: tempMessageId,
211
- role: 'user', // βœ… satisfies union
212
- content: userMessage || '',
213
- image_url: tempImageUrl,
214
- isBusiness: true,
215
- status: 'uploading',
216
-
217
-
218
- };
219
-
220
-
221
- setMessages((prevMessages) => {
222
- const updatedMessages = [...prevMessages, tempJson];
223
- return updatedMessages;
224
- });
225
-
226
-
227
- let finalImageUrl: string | null = null;
228
-
229
- if (image) {
230
-
231
- try {
232
- const formData = new FormData();
233
- formData.append("image", image);
234
-
235
- const uploadResponse = await axios.post<UploadImageResponse>(
236
- `${config.apiUrl}upload-image/`,
237
- formData
238
- );
239
-
240
-
241
- if (uploadResponse.status === 201 && uploadResponse.data?.image_url) {
242
- finalImageUrl = uploadResponse.data.image_url;
243
- setMessages((prevMessages) => {
244
- const updatedMessages = prevMessages.map((msg) =>
245
- msg.id === tempMessageId && finalImageUrl
246
- ? { ...msg, image_url: finalImageUrl } // βœ… only if finalImageUrl is not null
247
- : msg
248
- );
249
- return updatedMessages;
250
- });
251
- }
252
- } catch (error) {
253
- console.error("Error uploading image:", error);
254
- setMessages((messageList) =>
255
- messageList.map((msg) =>
256
- msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
257
- )
258
- );
259
- return;
260
- }
261
- }
262
-
263
- try {
264
-
265
-
266
- if (socket && socket.readyState === WebSocket.OPEN) {
267
- const additionalData = { isBusiness: true, SessionId: sessionId };
268
- const messagePayload = JSON.stringify({
269
- image_url: finalImageUrl || null,
270
- message: input.trim()||"",
271
- additionalData: additionalData,
272
- });
273
-
274
- socket.send(messagePayload);
275
-
276
- }
277
-
278
-
279
- if (finalImageUrl) {
280
- setMessages((messageList) =>
281
- messageList.map((msg) =>
282
- msg.id === tempMessageId ? { ...msg, status: 'delivered' } : msg
283
- )
284
- );
285
-
286
- apiCall(userMessage.trim() || '',newSession,sessionId,finalImageUrl,true)
287
- }
288
-
289
-
290
-
291
-
292
- } catch (error) {
293
- console.error('Error sending Image message:', error);
294
-
295
- setMessages((messageList) =>
296
- messageList.map((msg) =>
297
- msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
298
- )
299
- );
300
- } finally {
301
- setSelectedImage(null);
302
- }
303
- };
304
-
305
-
306
-
307
-
308
- const handleSendVoice = async (audio: Blob) => {
309
- if (!audio) return;
310
-
311
- const tempAudioUrl = URL.createObjectURL(audio);
312
- const tempMessageId = Date.now();
313
-
314
- // Step 1: Add temporary audio message
315
- const tempMessage: ChatMessage = {
316
- id: tempMessageId,
317
- role: 'user',
318
- content: '',
319
- audio_url: tempAudioUrl,
320
- isBusiness: true,
321
- status: 'uploading',
322
- };
323
-
324
- setMessages(prev => [...prev, tempMessage]);
325
-
326
- let finalAudioUrl: string | undefined = undefined;
327
- let transcriptionText: string = '';
328
-
329
- try {
330
- // Step 2: Upload audio to backend
331
- const formData = new FormData();
332
- formData.append('audio', audio);
333
-
334
- const uploadResponse = await axios.post<UploadAudioResponse>(
335
- `${config.apiUrl}upload-audio/`,
336
- formData
337
- );
338
-
339
-
340
-
341
-
342
-
343
-
344
- if (uploadResponse.status === 201) {
345
- finalAudioUrl = uploadResponse.data.audio_url;
346
- transcriptionText = uploadResponse.data.transcription_text || '[Audio]';
347
-
348
- // Step 3: Replace temp audio URL with final URL
349
- setMessages(prev =>
350
- prev.map(msg =>
351
- msg.id === tempMessageId
352
- ? {
353
- ...msg,
354
- audio_url: finalAudioUrl,
355
- content: transcriptionText,
356
- status: 'delivered',
357
- }
358
- : msg
359
- )
360
- );
361
- }
362
- } catch (error) {
363
- console.error('Audio upload failed:', error);
364
- setMessages(prev =>
365
- prev.map(msg =>
366
- msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
367
- )
368
- );
369
- return;
370
- }
371
-
372
- try {
373
- // Step 4: Send to WebSocket
374
- if (socket && socket.readyState === WebSocket.OPEN) {
375
- const payload = JSON.stringify({
376
- audio_url: finalAudioUrl,
377
- message: transcriptionText,
378
- additionalData: {
379
- isBusiness: true,
380
- SessionId: sessionId,
381
- },
382
- });
383
- socket.send(payload);
384
- }
385
-
386
- // Step 5: Send to API
387
- if (finalAudioUrl) {
388
- apiCall(transcriptionText,newSession ,sessionId, finalAudioUrl, false);
389
- }
390
- } catch (error) {
391
- console.error('Sending audio to API/WebSocket failed:', error);
392
- setMessages(prev =>
393
- prev.map(msg =>
394
- msg.id === tempMessageId ? { ...msg, status: 'failed' } : msg
395
- )
396
- );
397
- } finally {
398
- setAudioBlob(null); // Step 6: cleanup
399
- }
400
- };
401
-
402
- const { items, loading, addToCart, removeFromCart, refresh } = useCart(sessionId);
403
-
404
-
405
- // replace your old handleToCart:
406
- function shorten(str: string, maxLen = 20) {
407
- return str.length > maxLen ? `${str.slice(0, maxLen)}…` : str;
408
- }
409
-
410
- const handleToCart = useCallback(
411
- async (variantId: string, name: string, qty = 1) => {
412
- await addToCart(variantId, name, qty);
413
-
414
- // truncate the name for display
415
- const shortName = shorten(name, 20);
416
-
417
- setNotification(`βœ… ${qty}Γ— ${shortName} added to cart`);
418
- },
419
- [addToCart]
420
- );
421
-
422
- const handleRemoveFromCart = useCallback(
423
- async (variantId: string, qty = 1) => {
424
- await removeFromCart(variantId, qty);
425
- setNotification(`❌ Removed ${qty} item(s) from cart`);
426
- },
427
- [removeFromCart]
428
- );
429
-
430
-
431
-
432
-
433
- return (
434
- <div className="chatbot-box">
435
- <div className="chatbot-header">
436
- {/* Back Button */}
437
- <Button
438
- type="text"
439
- style={{ marginRight: 8 }}
440
- onClick={() => setView('sessions')} // or use a callback like `goBack()` if needed
441
- icon={<ArrowLeftOutlined />}
442
- />
443
-
444
- {/* Avatar + Title */}
445
- <Avatar
446
- src={
447
- chatbotLogo ||
448
- 'https://t4.ftcdn.net/jpg/08/90/53/75/360_F_890537572_m7EIYLzXgeTYGDNoIr7KEDOElFUTzFy0.jpg'
449
- }
450
- />
451
- <span className="chatbot-title">{agentName || 'Ai Assistant'}</span>
452
-
453
- {items.length > 0 && (
454
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
455
- <CartWidget
456
- items={items}
457
- loading={loading}
458
- onRefresh={refresh}
459
- onRemove={removeFromCart}
460
- colorCode={colorCode}
461
- sendMessage={sendMessageSuggestion}
462
- />
463
- </div>
464
- )}
465
-
466
-
467
- {/* Close Button */}
468
- <CloseOutlined
469
- onClick={() => setOpen(!Opened)}
470
- className="chatbot-close"
471
- style={{ marginLeft: 'auto' }}
472
- />
473
- </div>
474
-
475
-
476
- {notification && (
477
- <Notification
478
- message={notification}
479
- onClose={() => setNotification(null)}
480
- />
481
- )}
482
-
483
-
484
-
485
-
486
-
487
- <ChatMessages
488
- messages={messages}
489
- colorCode={colorCode}
490
- isLoading={false}
491
- handleAddToCart={handleToCart}
492
- agentStatus={agentStatus}
493
- sessionId={sessionId}
494
- handleRemoveFromCart={handleRemoveFromCart}
495
- userType={userType}
496
- />
497
-
498
- <div className="chatbot-input-area">
499
- <Suggestions sendMessageSuggestion={sendMessageSuggestion} colorCode={colorCode} suggestions={messagesSuggestions} />
500
-
501
- <ChatInput
502
- userMessage={input}
503
- setUserMessage={setInput}
504
- handleSendMessage={sendMessage}
505
- handleSendImage={handleSendImage}
506
- handleSendVoice={handleSendVoice}
507
- selectedImage={selectedImage}
508
- setSelectedImage={setSelectedImage}
509
- audioBlob={audioBlob}
510
- setAudioBlob={setAudioBlob}
511
- isSending={isSending}
512
-
513
- />
514
-
515
- <Powered />
516
- </div>
517
- </div>
518
- );
519
- };
520
-
521
- export default Messenger;
@@ -1,27 +0,0 @@
1
- // src/components/Notification.tsx
2
- import React, { useEffect } from 'react';
3
-
4
- interface NotificationProps {
5
- message: string;
6
- onClose: () => void;
7
- duration?: number; // milliseconds before auto-close
8
- }
9
-
10
- const Notification: React.FC<NotificationProps> = ({
11
- message,
12
- onClose,
13
- duration = 1000,
14
- }) => {
15
- useEffect(() => {
16
- const timer = setTimeout(onClose, duration);
17
- return () => clearTimeout(timer);
18
- }, [onClose, duration]);
19
-
20
- return (
21
- <div className="fixed top-5 right-4 bg-gray-400 text-white px-4 py-2 rounded-lg shadow-lg z-50">
22
- {message}
23
- </div>
24
- );
25
- };
26
-
27
- export default Notification;
@@ -1,16 +0,0 @@
1
- a{
2
- color: gray;
3
- text-decoration: none;
4
-
5
- }
6
-
7
- a:hover{
8
- color:rgb(63, 153, 238);
9
- cursor: pointer;
10
- }
11
-
12
- .powered-container{
13
- margin-top: 6px;
14
-
15
- }
16
-
@@ -1,13 +0,0 @@
1
- import './Powered.css'
2
- export default function Powered() {
3
- return (
4
- <div>
5
- <div className='powered-container'>
6
- <a href='https://jaweb.me/' target="_blank">
7
- Powered by Jaweb
8
- </a>
9
- </div>
10
-
11
- </div>
12
- )
13
- }
@@ -1,139 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Button, Input, Typography, Spin } from 'antd';
3
- import { ArrowLeftOutlined, LoadingOutlined } from '@ant-design/icons';
4
- import config from '../../../hooks/config';
5
- import { getCookie } from '../../../utils/cookies';
6
-
7
- const { Title, Text } = Typography;
8
-
9
- type CreateSessionProps = {
10
- onBack: () => void;
11
- onCreate: (data: {
12
- name: string;
13
- email: string;
14
- phone: string;
15
- session_id: string;
16
- messages: any[];
17
- hasInfo:boolean;
18
- }) => void;
19
- colorCode: string;
20
- visitorId: string;
21
- country: string;
22
- };
23
-
24
- const CreateSession: React.FC<CreateSessionProps> = ({ onBack, onCreate, colorCode, visitorId, country }) => {
25
- const [name, setName] = useState('');
26
- const [email, setEmail] = useState('');
27
- const [phone, setPhone] = useState('');
28
- const [isLoading, setIsLoading] = useState(false);
29
-
30
- const handleSubmit = async () => {
31
- const payload = {
32
- visitor_id:visitorId,
33
- country,
34
- name,
35
- email,
36
- phone,
37
- company_username: getCookie('company_username'),
38
- };
39
-
40
- try {
41
- setIsLoading(true);
42
-
43
- const res = await fetch(`${config.apiUrl}chatlog-create/`, {
44
- method: 'POST',
45
- headers: {
46
- 'Content-Type': 'application/json',
47
- },
48
- body: JSON.stringify(payload),
49
- });
50
-
51
- const data = await res.json();
52
-
53
-
54
- if (res.ok ) {
55
-
56
- onCreate({
57
- name,
58
- email,
59
- phone,
60
- session_id: data?.user_session_id,
61
- messages: data.messages,
62
- hasInfo:true
63
- });
64
-
65
- } else {
66
- console.error('Failed to register:', data);
67
- }
68
- } catch (err) {
69
- console.error('Error submitting session data:', err);
70
- } finally {
71
- setIsLoading(false);
72
- }
73
- };
74
-
75
- return (
76
- <div style={{ padding: '1.5rem' }}>
77
- <Button
78
- type="text"
79
- icon={<ArrowLeftOutlined />}
80
- onClick={onBack}
81
- style={{ marginBottom: '1rem' }}
82
- >
83
- Back
84
- </Button>
85
-
86
- <Title level={4} style={{ marginBottom: '2rem' }}>
87
- Let's create a session
88
- </Title>
89
-
90
- <div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
91
- <div>
92
- <Text strong>πŸ‘‹ Please, can I have your name?</Text>
93
- <Input
94
- size="large"
95
- placeholder="John Doe"
96
- value={name}
97
- onChange={(e) => setName(e.target.value)}
98
- style={{ marginTop: '0.5rem' }}
99
- />
100
- </div>
101
-
102
- <div>
103
- <Text strong>πŸ“§ What’s your email address?</Text>
104
- <Input
105
- size="large"
106
- type="email"
107
- placeholder="you@example.com"
108
- value={email}
109
- onChange={(e) => setEmail(e.target.value)}
110
- style={{ marginTop: '0.5rem' }}
111
- />
112
- </div>
113
-
114
- <div>
115
- <Text strong>πŸ“± May I have your phone number?</Text>
116
- <Input
117
- size="large"
118
- placeholder="+1 234 567 8900"
119
- value={phone}
120
- onChange={(e) => setPhone(e.target.value)}
121
- style={{ marginTop: '0.5rem' }}
122
- />
123
- </div>
124
-
125
- <Button
126
- type="primary"
127
- size="large"
128
- style={{ backgroundColor: colorCode, color: 'white' }}
129
- onClick={handleSubmit}
130
- disabled={!name || !email || !phone}
131
- >
132
- {isLoading ? <Spin indicator={<LoadingOutlined style={{ fontSize: 20, color: 'white' }} spin />} /> : 'Create Session'}
133
- </Button>
134
- </div>
135
- </div>
136
- );
137
- };
138
-
139
- export default CreateSession;