shopify-chatbot-widget 1.0.9 → 1.0.10

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 (44) hide show
  1. package/.env.development +2 -0
  2. package/dist/jaweb-chatbot.css +1 -1
  3. package/dist/jaweb-chatbot.iife.js +192 -599
  4. package/package.json +1 -1
  5. package/src/App.tsx +9 -0
  6. package/src/Chatbot/Chatbot.css +193 -113
  7. package/src/Chatbot/Chatbot.tsx +26 -19
  8. package/src/Chatbot/components/Cart/Index.tsx +10 -8
  9. package/src/Chatbot/components/ChatInput.tsx +11 -11
  10. package/src/Chatbot/components/Home/Home.css +456 -0
  11. package/src/Chatbot/components/Home/Home.tsx +293 -0
  12. package/src/Chatbot/components/Messages/Addcart.tsx +1 -1
  13. package/src/Chatbot/components/Messages/AssistantMesage.tsx +78 -37
  14. package/src/Chatbot/components/Messages/CardSwiper.tsx +1 -1
  15. package/src/Chatbot/components/Messages/Carousel/CardSwiper.tsx +87 -92
  16. package/src/Chatbot/components/Messages/Carousel/ProductSwiper.css +361 -0
  17. package/src/Chatbot/components/Messages/Carousel/TryOn.tsx +2 -2
  18. package/src/Chatbot/components/Messages/ChatMessages.css +32 -1
  19. package/src/Chatbot/components/Messages/ChatMessages.tsx +21 -38
  20. package/src/Chatbot/components/Messages/OrderCard.css +177 -0
  21. package/src/Chatbot/components/Messages/OrderCard.tsx +120 -0
  22. package/src/Chatbot/components/Messages/ProductCard.css +1 -1
  23. package/src/Chatbot/components/Messages/Support.tsx +311 -118
  24. package/src/Chatbot/components/Messages/Typing.css +31 -35
  25. package/src/Chatbot/components/Messages/Typing.tsx +19 -18
  26. package/src/Chatbot/components/Messenger/Messenger.tsx +67 -14
  27. package/src/Chatbot/components/Powered.css +26 -28
  28. package/src/Chatbot/components/Powered.tsx +1 -1
  29. package/src/Chatbot/components/Sessions/CreateSession.tsx +12 -10
  30. package/src/Chatbot/components/TryOn/View.tsx +2 -2
  31. package/src/Chatbot/components/VoiceMode.css +247 -0
  32. package/src/Chatbot/components/VoiceMode.tsx +613 -0
  33. package/src/hooks/config.tsx +9 -10
  34. package/src/hooks/useChatbotAPI.tsx +146 -61
  35. package/src/hooks/useChatbotData.tsx +8 -5
  36. package/src/hooks/useSessionMessages.tsx +0 -1
  37. package/src/hooks/useWebsocke.tsx +40 -18
  38. package/src/i18n.tsx +384 -10
  39. package/src/Chatbot/components/Messages/Carousel/CardSwiperSalla.tsx +0 -187
  40. package/src/Chatbot/components/Messages/Carousel/CardSwiperZid.tsx +0 -208
  41. package/src/Chatbot/components/Messages/Carousel/SallaSwiper.css +0 -245
  42. package/src/Chatbot/components/Messages/SallaAssistantMessage.tsx +0 -202
  43. package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +0 -203
  44. package/src/Chatbot/components/Sessions/RenderList.tsx +0 -519
@@ -1,6 +1,7 @@
1
1
  import React, { useState,useCallback } from 'react';
2
2
  import { Button, Avatar } from 'antd';
3
3
  import { CloseOutlined,ArrowLeftOutlined,ShoppingCartOutlined} from '@ant-design/icons';
4
+ import VoiceMode from '../VoiceMode';
4
5
  import '../../Chatbot.css';
5
6
  import { useChatbotMessage } from '../../../hooks/useSessionMessages';
6
7
  import axios from 'axios';
@@ -8,6 +9,7 @@ import ChatInput from '../../components/ChatInput';
8
9
  import Powered from '../../components/Powered';
9
10
  import ChatMessages from '../../components/Messages/ChatMessages';
10
11
  import config from '../../../hooks/config';
12
+ import { useTranslation } from 'react-i18next';
11
13
  import { useChatbotAPI } from '../../../hooks/useChatbotAPI';
12
14
  import type { ChatMessage } from '../../../types/chatbot';
13
15
  import { getCookie } from '../../../utils/cookies';
@@ -18,7 +20,6 @@ import { useCart } from '../../../hooks/useCart';
18
20
 
19
21
  type MessengerProps = {
20
22
  chatbotLogo: string;
21
- userType:string;
22
23
  agentName:string;
23
24
  colorCode: string;
24
25
  newSession: boolean;
@@ -27,7 +28,7 @@ type MessengerProps = {
27
28
  disableForm: boolean;
28
29
  sessionId:string;
29
30
  setOpen: (v: boolean) => void;
30
- setView: React.Dispatch<React.SetStateAction<'sessions' | 'create' | 'messenger' | 'cartView' | 'tryOn'>>;
31
+ setView: React.Dispatch<React.SetStateAction<'home' | 'create' | 'messenger' | 'cartView' | 'tryOn'>>;
31
32
  setNewSession: (v: boolean) => void;
32
33
  Opened: boolean;
33
34
  calendly:string;
@@ -52,7 +53,6 @@ const Messenger: React.FC<MessengerProps> = ({
52
53
  agentName,
53
54
  colorCode,
54
55
  newSession,
55
- userType,
56
56
  setNewSession,
57
57
  messagesSuggestions,
58
58
  setMessagesSuggestions,
@@ -70,6 +70,8 @@ const Messenger: React.FC<MessengerProps> = ({
70
70
  const [selectedImage, setSelectedImage] = useState<File | null>(null);
71
71
  const [audioBlob, setAudioBlob] = useState<Blob | null>(null);
72
72
  const [isSending, setIsSending] = useState(false);
73
+ const { t } = useTranslation();
74
+ const [voiceOpen, setVoiceOpen] = useState(false);
73
75
 
74
76
 
75
77
 
@@ -450,18 +452,59 @@ const socket = useChatSocket(sessionId, handleSocketData);
450
452
  <Button
451
453
  type="text"
452
454
  style={{ marginRight: 8 }}
453
- onClick={() => setView('sessions')} // or use a callback like `goBack()` if needed
455
+ onClick={() => setView('home')}
454
456
  icon={<ArrowLeftOutlined />}
455
457
  />
456
458
 
457
459
  {/* Avatar + Title */}
458
- <Avatar
459
- src={
460
- chatbotLogo ||
461
- 'https://t4.ftcdn.net/jpg/08/90/53/75/360_F_890537572_m7EIYLzXgeTYGDNoIr7KEDOElFUTzFy0.jpg'
462
- }
463
- />
464
- <span className="chatbot-title">{agentName || 'Ai Assistant'}</span>
460
+ <span className="chatbot-avatarWrap">
461
+ <Avatar
462
+ src={
463
+ chatbotLogo ||
464
+ 'https://t4.ftcdn.net/jpg/08/90/53/75/360_F_890537572_m7EIYLzXgeTYGDNoIr7KEDOElFUTzFy0.jpg'
465
+ }
466
+ />
467
+ <span className="chatbot-avatarDot" />
468
+ </span>
469
+ <span className="chatbot-title">
470
+ <span className="chatbot-title-name">{agentName || t('messenger.aiAssistant')}</span>
471
+ <span className="chatbot-title-status">{t('messenger.onlineNow')}</span>
472
+ </span>
473
+
474
+ {/* Voice mode — filled accent button with equalizer motif */}
475
+ <button
476
+ onClick={() => setVoiceOpen(true)}
477
+ aria-label={t('messenger.voiceMode')}
478
+ title={t('messenger.talkToUs')}
479
+ style={{
480
+ marginLeft: 'auto',
481
+ marginRight: 10,
482
+ width: 34,
483
+ height: 34,
484
+ borderRadius: '50%',
485
+ border: 'none',
486
+ background: colorCode || '#111111',
487
+ display: 'flex',
488
+ alignItems: 'center',
489
+ justifyContent: 'center',
490
+ gap: 2.5,
491
+ cursor: 'pointer',
492
+ boxShadow: `0 4px 12px ${colorCode || '#111111'}55`,
493
+ flexShrink: 0,
494
+ }}
495
+ >
496
+ {[9, 15, 11].map((h, i) => (
497
+ <span
498
+ key={i}
499
+ style={{
500
+ width: 3,
501
+ height: h,
502
+ borderRadius: 2,
503
+ background: '#ffffff',
504
+ }}
505
+ />
506
+ ))}
507
+ </button>
465
508
 
466
509
  {items.length > 0 && (
467
510
  <div
@@ -470,7 +513,6 @@ const socket = useChatSocket(sessionId, handleSocketData);
470
513
  display: "flex",
471
514
  alignItems: "center",
472
515
  justifyContent: "center",
473
- marginLeft: "auto",
474
516
  marginRight: 12,
475
517
  }}
476
518
  >
@@ -498,7 +540,7 @@ const socket = useChatSocket(sessionId, handleSocketData);
498
540
  position: "absolute",
499
541
  top: 2,
500
542
  right: 2,
501
- backgroundColor: colorCode || "#7F28F8",
543
+ backgroundColor: colorCode || "#111111",
502
544
  color: "white",
503
545
  fontSize: 10,
504
546
  fontWeight: 600,
@@ -549,7 +591,6 @@ const socket = useChatSocket(sessionId, handleSocketData);
549
591
  agentStatus={agentStatus}
550
592
  sessionId={sessionId}
551
593
  handleRemoveFromCart={handleRemoveFromCart}
552
- userType={userType}
553
594
  calendly={calendly}
554
595
  disableCheckout={disbleCheckout}
555
596
  isfetchingMessage={isFetchingMessages}
@@ -580,6 +621,18 @@ const socket = useChatSocket(sessionId, handleSocketData);
580
621
 
581
622
  <Powered />
582
623
  </div>
624
+
625
+ {voiceOpen && (
626
+ <VoiceMode
627
+ sessionId={sessionId}
628
+ colorCode={colorCode}
629
+ agentName={agentName}
630
+ newSession={newSession}
631
+ setNewSession={setNewSession}
632
+ onClose={() => setVoiceOpen(false)}
633
+ setMessages={setMessages}
634
+ />
635
+ )}
583
636
  </div>
584
637
  );
585
638
  };
@@ -1,29 +1,27 @@
1
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
-
2
+ display: inline-flex;
3
+ margin-top: 8px;
4
+ align-items: center;
5
+ gap: 4px;
6
+ font-family: 'Inter', -apple-system, sans-serif;
7
+ font-size: 10px;
8
+ letter-spacing: 0.08em;
9
+ text-transform: uppercase;
10
+ color: #9ca3af;
11
+ padding: 2px 8px;
12
+ }
13
+
14
+ .jawebcss-powered-label {
15
+ font-weight: 500;
16
+ }
17
+
18
+ .jawebcss-powered-link {
19
+ color: #111827;
20
+ font-weight: 700;
21
+ text-decoration: none;
22
+ transition: opacity 0.2s ease;
23
+ }
24
+
25
+ .jawebcss-powered-link:hover {
26
+ opacity: 0.6;
27
+ }
@@ -2,7 +2,7 @@ import './Powered.css';
2
2
 
3
3
  export default function Powered() {
4
4
  return (
5
- <div className="jawebcss-powered-container">
5
+ <div dir="ltr" className="jawebcss-powered-container">
6
6
  <span className="jawebcss-powered-label">Powered by</span>
7
7
  <a href="https://jaweb.me/" target="_blank" className="jawebcss-powered-link" rel="noopener noreferrer">
8
8
  Jaweb
@@ -3,6 +3,7 @@ import { Button, Input, Typography, Spin } from 'antd';
3
3
  import { ArrowLeftOutlined, LoadingOutlined } from '@ant-design/icons';
4
4
  import { motion } from 'framer-motion';
5
5
  import config from '../../../hooks/config';
6
+ import { useTranslation } from 'react-i18next';
6
7
 
7
8
  const { Title, Text } = Typography;
8
9
 
@@ -51,6 +52,7 @@ const CreateSession: React.FC<CreateSessionProps> = ({
51
52
  visitorId,
52
53
  country,
53
54
  }) => {
55
+ const { t } = useTranslation();
54
56
  const [name, setName] = useState('');
55
57
  const [email, setEmail] = useState('');
56
58
  const [phone, setPhone] = useState('');
@@ -109,7 +111,7 @@ const CreateSession: React.FC<CreateSessionProps> = ({
109
111
  onClick={onBack}
110
112
  style={{ marginBottom: '1rem' }}
111
113
  >
112
- Back
114
+ {t('form.back')}
113
115
  </Button>
114
116
  </motion.div>
115
117
 
@@ -121,10 +123,10 @@ const CreateSession: React.FC<CreateSessionProps> = ({
121
123
  style={{ textAlign: 'center' }}
122
124
  >
123
125
  <Title level={3} style={{ marginBottom: '2rem' }}>
124
- Welcome 👋
126
+ {t('form.welcome')}
125
127
  </Title>
126
128
  <Text type="secondary">
127
- Please share a few details so we can get started.
129
+ {t('form.shareDetails')}
128
130
  </Text>
129
131
  </motion.div>
130
132
 
@@ -148,23 +150,23 @@ const CreateSession: React.FC<CreateSessionProps> = ({
148
150
  }}
149
151
  >
150
152
  <FormField
151
- label="Your Name"
153
+ label={t("form.yourName")}
152
154
  value={name}
153
155
  setter={setName}
154
- placeholder="Enter your full name"
156
+ placeholder={t("form.namePh")}
155
157
  />
156
158
  <FormField
157
- label="Email Address"
159
+ label={t("form.emailAddress")}
158
160
  value={email}
159
161
  setter={setEmail}
160
162
  type="email"
161
- placeholder="Enter your email"
163
+ placeholder={t("form.emailPh")}
162
164
  />
163
165
  <FormField
164
- label="Phone Number"
166
+ label={t("form.phoneNumber")}
165
167
  value={phone}
166
168
  setter={setPhone}
167
- placeholder="Enter your phone number"
169
+ placeholder={t("form.phonePh")}
168
170
  />
169
171
 
170
172
  {/* Submit Button */}
@@ -188,7 +190,7 @@ const CreateSession: React.FC<CreateSessionProps> = ({
188
190
  }
189
191
  />
190
192
  ) : (
191
- 'Start Chat'
193
+ t('form.startChat')
192
194
  )}
193
195
  </Button>
194
196
  </motion.div>
@@ -69,7 +69,7 @@ const TryOnLayout: React.FC<TryOnProps> = ({
69
69
 
70
70
  const fileInputRef = useRef<HTMLInputElement>(null);
71
71
 
72
- const themeColor = colorCode || '#7F28F8';
72
+ const themeColor = colorCode || '#111111';
73
73
 
74
74
  const activeProduct = product || {
75
75
  variant_id: '0',
@@ -302,7 +302,7 @@ const TryOnLayout: React.FC<TryOnProps> = ({
302
302
  </>
303
303
  );
304
304
 
305
- const hideCart = ['zid', 'email', 'salla'].includes(userType);
305
+ const hideCart = userType === 'email';
306
306
  const showCartControls = !addToCart && !hideCart && currentState === 'generated' && !isGenerating;
307
307
 
308
308
 
@@ -0,0 +1,247 @@
1
+ /* Voice mode — full-widget overlay with premium equalizer bars */
2
+ .jwbV-overlay {
3
+ --jwbV-accent: #111111;
4
+ position: absolute;
5
+ inset: 0;
6
+ z-index: 40;
7
+ background: rgba(255, 255, 255, 0.97);
8
+ backdrop-filter: blur(12px);
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ justify-content: center;
13
+ gap: 16px;
14
+ padding: 24px;
15
+ font-family: 'Inter', -apple-system, sans-serif;
16
+ overflow-y: auto;
17
+ }
18
+
19
+ .jwbV-close {
20
+ position: absolute;
21
+ top: 14px;
22
+ right: 14px;
23
+ width: 32px;
24
+ height: 32px;
25
+ border-radius: 50%;
26
+ border: 1px solid #ececec;
27
+ background: #ffffff;
28
+ color: #6b7280;
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ cursor: pointer;
33
+ transition: all 0.15s ease;
34
+ z-index: 2;
35
+ }
36
+
37
+ .jwbV-close:hover {
38
+ color: #111827;
39
+ border-color: #d1d5db;
40
+ }
41
+
42
+ /* ===== Equalizer bars ===== */
43
+ .jwbV-bars {
44
+ border: none;
45
+ background: transparent;
46
+ cursor: pointer;
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ gap: 9px;
51
+ height: 130px;
52
+ padding: 20px 28px;
53
+ border-radius: 28px;
54
+ transition: background 0.2s ease;
55
+ flex-shrink: 0;
56
+ }
57
+
58
+ .jwbV-bars:hover {
59
+ background: rgba(17, 24, 39, 0.03);
60
+ }
61
+
62
+ .jwbV-bar {
63
+ width: 12px;
64
+ height: 46px;
65
+ border-radius: 8px;
66
+ background: linear-gradient(
67
+ 180deg,
68
+ color-mix(in srgb, var(--jwbV-accent) 65%, #ffffff),
69
+ var(--jwbV-accent)
70
+ );
71
+ box-shadow: 0 8px 22px color-mix(in srgb, var(--jwbV-accent) 25%, transparent);
72
+ transform-origin: center;
73
+ }
74
+
75
+ /* Listening: gentle rolling wave */
76
+ .jwbV-bars--listening .jwbV-bar {
77
+ animation: jwbV-wave 1.5s ease-in-out infinite;
78
+ }
79
+
80
+ /* Thinking: soft dim pulse, bars settle small */
81
+ .jwbV-bars--thinking .jwbV-bar {
82
+ animation: jwbV-ponder 1.2s ease-in-out infinite;
83
+ filter: saturate(0.55);
84
+ }
85
+
86
+ /* Speaking: energetic dance — varied rhythm per bar */
87
+ .jwbV-bars--speaking .jwbV-bar {
88
+ animation: jwbV-dance 0.62s ease-in-out infinite;
89
+ }
90
+ .jwbV-bars--speaking .jwbV-bar:nth-child(2n) {
91
+ animation-duration: 0.48s;
92
+ }
93
+ .jwbV-bars--speaking .jwbV-bar:nth-child(3n) {
94
+ animation-duration: 0.72s;
95
+ }
96
+
97
+ /* Error: flat + grey */
98
+ .jwbV-bars--error .jwbV-bar {
99
+ filter: grayscale(1);
100
+ opacity: 0.45;
101
+ transform: scaleY(0.4);
102
+ }
103
+
104
+ @keyframes jwbV-wave {
105
+ 0%, 100% { transform: scaleY(0.55); }
106
+ 50% { transform: scaleY(1); }
107
+ }
108
+
109
+ @keyframes jwbV-ponder {
110
+ 0%, 100% { transform: scaleY(0.35); opacity: 0.7; }
111
+ 50% { transform: scaleY(0.5); opacity: 1; }
112
+ }
113
+
114
+ @keyframes jwbV-dance {
115
+ 0%, 100% { transform: scaleY(0.35); }
116
+ 35% { transform: scaleY(1.25); }
117
+ 70% { transform: scaleY(0.7); }
118
+ }
119
+
120
+ /* ===== Text ===== */
121
+ .jwbV-status {
122
+ margin: 0;
123
+ font-size: 12px;
124
+ font-weight: 700;
125
+ letter-spacing: 0.08em;
126
+ text-transform: uppercase;
127
+ color: #111827;
128
+ }
129
+
130
+ .jwbV-transcript {
131
+ margin: 0;
132
+ font-size: 14px;
133
+ color: #6b7280;
134
+ text-align: center;
135
+ max-width: 300px;
136
+ }
137
+
138
+ .jwbV-reply {
139
+ margin: 0;
140
+ font-size: 13px;
141
+ color: #374151;
142
+ text-align: center;
143
+ max-width: 300px;
144
+ line-height: 1.5;
145
+ }
146
+
147
+ .jwbV-hint {
148
+ margin: 0;
149
+ font-size: 12px;
150
+ color: #9ca3af;
151
+ text-align: center;
152
+ }
153
+
154
+ /* ===== Cards inside voice mode ===== */
155
+ .jwbV-payload {
156
+ width: 100%;
157
+ display: flex;
158
+ justify-content: center;
159
+ flex-shrink: 0;
160
+ }
161
+
162
+ .jwbV-products {
163
+ gap: 10px;
164
+ overflow-x: auto;
165
+ justify-content: flex-start;
166
+ padding: 4px 2px;
167
+ scrollbar-width: none;
168
+ }
169
+
170
+ .jwbV-products::-webkit-scrollbar {
171
+ display: none;
172
+ }
173
+
174
+ .jwbV-product {
175
+ flex: 0 0 auto;
176
+ width: 132px;
177
+ background: #ffffff;
178
+ border: 1px solid #ececec;
179
+ border-radius: 14px;
180
+ padding: 8px;
181
+ display: flex;
182
+ flex-direction: column;
183
+ gap: 6px;
184
+ box-shadow: 0 1px 2px rgba(17, 24, 39, 0.05);
185
+ transition: border-color 0.15s ease, transform 0.15s ease;
186
+ }
187
+
188
+ .jwbV-product:hover {
189
+ border-color: #d1d5db;
190
+ transform: translateY(-2px);
191
+ }
192
+
193
+ .jwbV-productLink {
194
+ text-decoration: none;
195
+ display: flex;
196
+ flex-direction: column;
197
+ gap: 5px;
198
+ min-width: 0;
199
+ }
200
+
201
+ .jwbV-add {
202
+ display: flex;
203
+ align-items: center;
204
+ justify-content: center;
205
+ gap: 5px;
206
+ border: none;
207
+ background: var(--jwbV-accent);
208
+ color: #ffffff;
209
+ font-size: 11px;
210
+ font-weight: 700;
211
+ height: 30px;
212
+ border-radius: 9px;
213
+ cursor: pointer;
214
+ transition: opacity 0.15s ease;
215
+ }
216
+
217
+ .jwbV-add:hover {
218
+ opacity: 0.85;
219
+ }
220
+
221
+ .jwbV-add--done {
222
+ background: #16a34a;
223
+ cursor: default;
224
+ }
225
+
226
+ .jwbV-productImg {
227
+ width: 100%;
228
+ aspect-ratio: 1 / 1;
229
+ object-fit: cover;
230
+ border-radius: 9px;
231
+ background: #f6f7f9;
232
+ }
233
+
234
+ .jwbV-productName {
235
+ font-size: 11.5px;
236
+ font-weight: 600;
237
+ color: #111827;
238
+ white-space: nowrap;
239
+ overflow: hidden;
240
+ text-overflow: ellipsis;
241
+ }
242
+
243
+ .jwbV-productPrice {
244
+ font-size: 11.5px;
245
+ font-weight: 700;
246
+ color: #111827;
247
+ }