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.
- package/.env.development +2 -0
- package/dist/jaweb-chatbot.css +1 -1
- package/dist/jaweb-chatbot.iife.js +192 -599
- package/package.json +1 -1
- package/src/App.tsx +9 -0
- package/src/Chatbot/Chatbot.css +193 -113
- package/src/Chatbot/Chatbot.tsx +26 -19
- package/src/Chatbot/components/Cart/Index.tsx +10 -8
- package/src/Chatbot/components/ChatInput.tsx +11 -11
- package/src/Chatbot/components/Home/Home.css +456 -0
- package/src/Chatbot/components/Home/Home.tsx +293 -0
- package/src/Chatbot/components/Messages/Addcart.tsx +1 -1
- package/src/Chatbot/components/Messages/AssistantMesage.tsx +78 -37
- package/src/Chatbot/components/Messages/CardSwiper.tsx +1 -1
- package/src/Chatbot/components/Messages/Carousel/CardSwiper.tsx +87 -92
- package/src/Chatbot/components/Messages/Carousel/ProductSwiper.css +361 -0
- package/src/Chatbot/components/Messages/Carousel/TryOn.tsx +2 -2
- package/src/Chatbot/components/Messages/ChatMessages.css +32 -1
- package/src/Chatbot/components/Messages/ChatMessages.tsx +21 -38
- package/src/Chatbot/components/Messages/OrderCard.css +177 -0
- package/src/Chatbot/components/Messages/OrderCard.tsx +120 -0
- package/src/Chatbot/components/Messages/ProductCard.css +1 -1
- package/src/Chatbot/components/Messages/Support.tsx +311 -118
- package/src/Chatbot/components/Messages/Typing.css +31 -35
- package/src/Chatbot/components/Messages/Typing.tsx +19 -18
- package/src/Chatbot/components/Messenger/Messenger.tsx +67 -14
- package/src/Chatbot/components/Powered.css +26 -28
- package/src/Chatbot/components/Powered.tsx +1 -1
- package/src/Chatbot/components/Sessions/CreateSession.tsx +12 -10
- package/src/Chatbot/components/TryOn/View.tsx +2 -2
- package/src/Chatbot/components/VoiceMode.css +247 -0
- package/src/Chatbot/components/VoiceMode.tsx +613 -0
- package/src/hooks/config.tsx +9 -10
- package/src/hooks/useChatbotAPI.tsx +146 -61
- package/src/hooks/useChatbotData.tsx +8 -5
- package/src/hooks/useSessionMessages.tsx +0 -1
- package/src/hooks/useWebsocke.tsx +40 -18
- package/src/i18n.tsx +384 -10
- package/src/Chatbot/components/Messages/Carousel/CardSwiperSalla.tsx +0 -187
- package/src/Chatbot/components/Messages/Carousel/CardSwiperZid.tsx +0 -208
- package/src/Chatbot/components/Messages/Carousel/SallaSwiper.css +0 -245
- package/src/Chatbot/components/Messages/SallaAssistantMessage.tsx +0 -202
- package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +0 -203
- package/src/Chatbot/components/Sessions/RenderList.tsx +0 -519
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import Chatbot from './Chatbot/Chatbot';
|
|
2
2
|
import 'antd/dist/reset.css'; // global AntD styles
|
|
3
3
|
|
|
4
|
+
// Dev harness only (src/main.tsx) — production mounts via chatbot-widget/index.tsx
|
|
5
|
+
// inside the iframe proxy, which sets company_username itself.
|
|
6
|
+
if (import.meta.env.DEV && !localStorage.getItem('company_username')) {
|
|
7
|
+
localStorage.setItem(
|
|
8
|
+
'company_username',
|
|
9
|
+
import.meta.env.VITE_DEV_COMPANY || 'blackbellex.myshopify.com'
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
4
13
|
function App() {
|
|
5
14
|
return (
|
|
6
15
|
<div className='jaweb-reset'>
|
package/src/Chatbot/Chatbot.css
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
Jaweb widget — design tokens
|
|
3
|
+
Monochrome structure; the merchant accent
|
|
4
|
+
color arrives as --jaweb-accent (set from
|
|
5
|
+
chatbot_color on .chatbot-box).
|
|
6
|
+
============================================ */
|
|
7
|
+
.jaweb-chatbot-container,
|
|
8
|
+
.chatbot-box {
|
|
9
|
+
--jaweb-accent: #111111;
|
|
10
|
+
--jaweb-ink: #111827;
|
|
11
|
+
--jaweb-ink-soft: #6b7280;
|
|
12
|
+
--jaweb-ink-faint: #9ca3af;
|
|
13
|
+
--jaweb-line: #ececec;
|
|
14
|
+
--jaweb-line-soft: #f3f4f6;
|
|
15
|
+
--jaweb-surface: #ffffff;
|
|
16
|
+
--jaweb-surface-alt: #fafafa;
|
|
17
|
+
--jaweb-radius: 20px;
|
|
18
|
+
}
|
|
19
|
+
|
|
1
20
|
.jaweb-chatbot-container {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
21
|
+
position: fixed;
|
|
22
|
+
bottom: 20px;
|
|
23
|
+
right: 20px;
|
|
24
|
+
z-index: 9999;
|
|
25
|
+
}
|
|
8
26
|
|
|
9
|
-
|
|
27
|
+
/* Tailwind global CSS file */
|
|
10
28
|
.scrollbar-hide::-webkit-scrollbar {
|
|
11
29
|
display: none;
|
|
12
30
|
}
|
|
@@ -15,50 +33,146 @@
|
|
|
15
33
|
scrollbar-width: none;
|
|
16
34
|
}
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
36
|
.chatbot-box {
|
|
22
37
|
position: fixed;
|
|
23
38
|
bottom: 20px;
|
|
24
39
|
right: 20px;
|
|
25
40
|
width: 420px;
|
|
26
41
|
max-width: 28%;
|
|
27
|
-
height: 85vh;
|
|
28
|
-
max-height: 800px;
|
|
42
|
+
height: 85vh;
|
|
43
|
+
max-height: 800px;
|
|
29
44
|
z-index: 9999;
|
|
30
|
-
background:
|
|
31
|
-
color:
|
|
32
|
-
border
|
|
45
|
+
background: var(--jaweb-surface);
|
|
46
|
+
color: var(--jaweb-ink);
|
|
47
|
+
border: 1px solid var(--jaweb-line);
|
|
48
|
+
border-radius: var(--jaweb-radius);
|
|
33
49
|
overflow: hidden;
|
|
34
50
|
display: flex;
|
|
35
51
|
flex-direction: column;
|
|
36
|
-
box-shadow: 0
|
|
37
|
-
font-family: 'Inter', sans-serif;
|
|
52
|
+
box-shadow: 0 24px 64px rgba(17, 24, 39, 0.16), 0 4px 16px rgba(17, 24, 39, 0.08);
|
|
53
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
38
54
|
}
|
|
39
55
|
|
|
56
|
+
/* ===== Header — glass, floats over the messages ===== */
|
|
40
57
|
.chatbot-header {
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
position: absolute;
|
|
59
|
+
top: 0;
|
|
60
|
+
left: 0;
|
|
61
|
+
right: 0;
|
|
62
|
+
z-index: 30;
|
|
63
|
+
background: rgba(255, 255, 255, 0.72);
|
|
64
|
+
backdrop-filter: blur(18px) saturate(180%);
|
|
65
|
+
-webkit-backdrop-filter: blur(18px) saturate(180%);
|
|
66
|
+
padding: 14px 16px;
|
|
67
|
+
min-height: 68px;
|
|
43
68
|
display: flex;
|
|
44
69
|
align-items: center;
|
|
45
70
|
gap: 12px;
|
|
71
|
+
border-bottom: 1px solid rgba(17, 24, 39, 0.06);
|
|
72
|
+
box-shadow: 0 6px 24px rgba(17, 24, 39, 0.04);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* messages scroll underneath the glass */
|
|
76
|
+
.chatbot-messages {
|
|
77
|
+
padding-top: 84px !important;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.chatbot-avatarWrap {
|
|
81
|
+
position: relative;
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
flex-shrink: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.chatbot-avatarWrap .ant-avatar {
|
|
87
|
+
width: 40px;
|
|
88
|
+
height: 40px;
|
|
89
|
+
border: 1px solid rgba(17, 24, 39, 0.08);
|
|
90
|
+
box-shadow: 0 2px 8px rgba(17, 24, 39, 0.08);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.chatbot-avatarDot {
|
|
94
|
+
position: absolute;
|
|
95
|
+
right: -1px;
|
|
96
|
+
bottom: -1px;
|
|
97
|
+
width: 11px;
|
|
98
|
+
height: 11px;
|
|
99
|
+
border-radius: 50%;
|
|
100
|
+
background: #22c55e;
|
|
101
|
+
border: 2.5px solid #ffffff;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.chatbot-title {
|
|
105
|
+
flex-grow: 1;
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: column;
|
|
108
|
+
gap: 2px;
|
|
109
|
+
min-width: 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.chatbot-title-name {
|
|
113
|
+
font-size: 15px;
|
|
114
|
+
font-weight: 800;
|
|
115
|
+
color: var(--jaweb-ink);
|
|
116
|
+
letter-spacing: -0.02em;
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
text-overflow: ellipsis;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.chatbot-title-status {
|
|
123
|
+
font-size: 11.5px;
|
|
124
|
+
color: var(--jaweb-ink-soft);
|
|
125
|
+
font-weight: 500;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ghost-circle header buttons (back / close / cart) */
|
|
129
|
+
.chatbot-header .ant-btn {
|
|
130
|
+
width: 34px;
|
|
131
|
+
height: 34px;
|
|
132
|
+
border-radius: 50% !important;
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
color: var(--jaweb-ink-soft);
|
|
137
|
+
transition: all 0.15s ease;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.chatbot-header .ant-btn:hover {
|
|
141
|
+
background: rgba(17, 24, 39, 0.05) !important;
|
|
142
|
+
color: var(--jaweb-ink);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.chatbot-header .chatbot-close {
|
|
146
|
+
width: 34px;
|
|
147
|
+
height: 34px;
|
|
148
|
+
border-radius: 50%;
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
transition: all 0.15s ease;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.chatbot-header .chatbot-close:hover {
|
|
156
|
+
background: rgba(17, 24, 39, 0.05);
|
|
157
|
+
color: var(--jaweb-ink);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.chatbot-close {
|
|
161
|
+
cursor: pointer;
|
|
46
162
|
font-size: 16px;
|
|
47
|
-
|
|
48
|
-
border-bottom: 1px solid #e0e0e0;
|
|
49
|
-
flex-shrink: 0; /* Prevent header from shrinking */
|
|
163
|
+
color: var(--jaweb-ink-faint);
|
|
50
164
|
}
|
|
51
165
|
|
|
166
|
+
/* ===== Messages area ===== */
|
|
52
167
|
.chatbot-messages {
|
|
53
|
-
flex: 1;
|
|
54
|
-
padding: 16px;
|
|
168
|
+
flex: 1;
|
|
169
|
+
padding: 16px 14px;
|
|
55
170
|
overflow-y: auto;
|
|
56
171
|
display: flex;
|
|
57
172
|
flex-direction: column;
|
|
58
|
-
gap:
|
|
59
|
-
background-color:
|
|
60
|
-
min-height: 0;
|
|
61
|
-
|
|
173
|
+
gap: 10px;
|
|
174
|
+
background-color: var(--jaweb-surface-alt);
|
|
175
|
+
min-height: 0;
|
|
62
176
|
scrollbar-width: none;
|
|
63
177
|
-ms-overflow-style: none;
|
|
64
178
|
}
|
|
@@ -67,15 +181,43 @@
|
|
|
67
181
|
display: none;
|
|
68
182
|
}
|
|
69
183
|
|
|
184
|
+
/* ===== Bubbles ===== */
|
|
185
|
+
.chat-msg {
|
|
186
|
+
max-width: 85%;
|
|
187
|
+
padding: 10px 14px;
|
|
188
|
+
font-size: 14px;
|
|
189
|
+
line-height: 1.5;
|
|
190
|
+
word-break: break-word;
|
|
191
|
+
letter-spacing: -0.005em;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.chat-msg.assistant {
|
|
195
|
+
background: var(--jaweb-surface);
|
|
196
|
+
border: 1px solid var(--jaweb-line);
|
|
197
|
+
border-radius: 16px 16px 16px 5px;
|
|
198
|
+
align-self: flex-start;
|
|
199
|
+
color: var(--jaweb-ink);
|
|
200
|
+
box-shadow: 0 1px 2px rgba(17, 24, 39, 0.04);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.chat-msg.user {
|
|
204
|
+
background: var(--jaweb-accent);
|
|
205
|
+
border-radius: 16px 16px 5px 16px;
|
|
206
|
+
align-self: flex-end;
|
|
207
|
+
color: #ffffff;
|
|
208
|
+
box-shadow: 0 2px 6px rgba(17, 24, 39, 0.12);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* ===== Input area shell ===== */
|
|
70
212
|
.chatbot-input-area {
|
|
71
213
|
width: 100%;
|
|
72
214
|
display: flex;
|
|
73
215
|
flex-direction: column;
|
|
74
216
|
align-items: center;
|
|
75
|
-
background:
|
|
76
|
-
border-top: 1px solid
|
|
77
|
-
padding: 12px;
|
|
78
|
-
flex-shrink: 0;
|
|
217
|
+
background: var(--jaweb-surface);
|
|
218
|
+
border-top: 1px solid var(--jaweb-line-soft);
|
|
219
|
+
padding: 10px 12px 8px;
|
|
220
|
+
flex-shrink: 0;
|
|
79
221
|
}
|
|
80
222
|
|
|
81
223
|
@media (max-width: 768px) {
|
|
@@ -90,92 +232,30 @@
|
|
|
90
232
|
top: 0;
|
|
91
233
|
left: 0;
|
|
92
234
|
border-radius: 0;
|
|
235
|
+
border: none;
|
|
93
236
|
}
|
|
94
|
-
|
|
237
|
+
|
|
95
238
|
.chat-launcher.chatbot-open {
|
|
96
239
|
display: none;
|
|
97
240
|
}
|
|
98
241
|
}
|
|
99
242
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
.chatbot-close {
|
|
108
|
-
cursor: pointer;
|
|
109
|
-
font-size: 16px;
|
|
110
|
-
color: #888;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
.chat-msg {
|
|
115
|
-
max-width: 85%;
|
|
116
|
-
padding: 10px 14px;
|
|
117
|
-
border-radius: 16px;
|
|
118
|
-
font-size: 14px;
|
|
119
|
-
line-height: 1.4;
|
|
120
|
-
word-break: break-word;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.chat-msg.assistant {
|
|
124
|
-
background: #f0f0f0;
|
|
125
|
-
align-self: flex-start;
|
|
126
|
-
color: #000;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.chat-msg.user {
|
|
130
|
-
background: #7f28f8;
|
|
131
|
-
align-self: flex-end;
|
|
132
|
-
color: white;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
.typing-animation span {
|
|
138
|
-
display: inline-block;
|
|
139
|
-
animation: blink 1.2s infinite;
|
|
140
|
-
font-size: 18px;
|
|
141
|
-
margin: 0 2px;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.typing-animation span:nth-child(2) {
|
|
145
|
-
animation-delay: 0.2s;
|
|
146
|
-
}
|
|
147
|
-
.typing-animation span:nth-child(3) {
|
|
148
|
-
animation-delay: 0.4s;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
@keyframes blink {
|
|
152
|
-
0% { opacity: 0.2; }
|
|
153
|
-
20% { opacity: 1; }
|
|
154
|
-
100% { opacity: 0.2; }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/* brand color hook (optional) */
|
|
158
|
-
.jaweb-chatbot-container { --jaweb-color: #7f28f8; }
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
/*
|
|
167
|
-
@media (max-width: 600px) {
|
|
168
|
-
.chatbot-box {
|
|
169
|
-
position: fixed;
|
|
170
|
-
width: 100%;
|
|
171
|
-
height: 100%;
|
|
172
|
-
right: 0;
|
|
173
|
-
bottom: 0;
|
|
174
|
-
top: 0;
|
|
243
|
+
.typing-animation span {
|
|
244
|
+
display: inline-block;
|
|
245
|
+
animation: blink 1.2s infinite;
|
|
246
|
+
font-size: 18px;
|
|
247
|
+
margin: 0 2px;
|
|
248
|
+
}
|
|
175
249
|
|
|
250
|
+
.typing-animation span:nth-child(2) {
|
|
251
|
+
animation-delay: 0.2s;
|
|
252
|
+
}
|
|
253
|
+
.typing-animation span:nth-child(3) {
|
|
254
|
+
animation-delay: 0.4s;
|
|
255
|
+
}
|
|
176
256
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
257
|
+
@keyframes blink {
|
|
258
|
+
0% { opacity: 0.2; }
|
|
259
|
+
20% { opacity: 1; }
|
|
260
|
+
100% { opacity: 0.2; }
|
|
261
|
+
}
|
package/src/Chatbot/Chatbot.tsx
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React, { useState, useEffect,useCallback } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { widgetDir } from '../i18n';
|
|
4
|
+
import '../i18n';
|
|
2
5
|
import { useChatbotData } from '../hooks/useChatbotData';
|
|
3
|
-
import ChatSessions from './components/Sessions/RenderList';
|
|
4
6
|
import CreateSession from './components/Sessions/CreateSession';
|
|
7
|
+
import Home from './components/Home/Home';
|
|
5
8
|
import Messenger from './components/Messenger/Messenger';
|
|
6
9
|
import './Chatbot.css';
|
|
7
10
|
import config from '../hooks/config';
|
|
@@ -14,7 +17,7 @@ import TryOnLayout from './components/TryOn/View';
|
|
|
14
17
|
import { useCart } from '../hooks/useCart';
|
|
15
18
|
const Chatbot: React.FC = () => {
|
|
16
19
|
const [open, setOpen] = useState(true);
|
|
17
|
-
const [viewMode, setViewMode] = useState<'
|
|
20
|
+
const [viewMode, setViewMode] = useState<'home' | 'create' | 'messenger' | 'cartView' | 'tryOn'>('home');
|
|
18
21
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
19
22
|
const [HasInfo, setHasInfo] = useState(false);
|
|
20
23
|
const [activeAgentName, setActiveAgentName] = useState('');
|
|
@@ -40,7 +43,7 @@ const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | '
|
|
|
40
43
|
} = useChatbotData();
|
|
41
44
|
|
|
42
45
|
const [visitorData, setVisitorData] = useState<{ visitorId: string; country: string } | null>(null);
|
|
43
|
-
const antIcon = <LoadingOutlined style={{ fontSize: 24, color: colorCode||'#
|
|
46
|
+
const antIcon = <LoadingOutlined style={{ fontSize: 24, color: colorCode||'#111111' }} spin />;
|
|
44
47
|
|
|
45
48
|
const handleNewSessionClick = () => {
|
|
46
49
|
// console.log(disableForm)
|
|
@@ -73,7 +76,6 @@ const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | '
|
|
|
73
76
|
const backendRes = await fetch(`${config.apiUrl}register-ip/`, {
|
|
74
77
|
method: 'POST',
|
|
75
78
|
headers: { 'Content-Type': 'application/json' },
|
|
76
|
-
credentials: 'include',
|
|
77
79
|
body: JSON.stringify({ visitor_id: visitorId }),
|
|
78
80
|
});
|
|
79
81
|
|
|
@@ -148,6 +150,8 @@ const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | '
|
|
|
148
150
|
|
|
149
151
|
|
|
150
152
|
|
|
153
|
+
const { i18n: i18nInstance } = useTranslation();
|
|
154
|
+
const boxDir = widgetDir(i18nInstance.language);
|
|
151
155
|
const { addToCart, removeFromCart } = useCart(activeSessionId ?? "");
|
|
152
156
|
|
|
153
157
|
const handleToCart = useCallback(
|
|
@@ -172,29 +176,33 @@ const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | '
|
|
|
172
176
|
return (
|
|
173
177
|
<div>
|
|
174
178
|
{open && (
|
|
175
|
-
<div
|
|
179
|
+
<div
|
|
180
|
+
className="chatbot-box"
|
|
181
|
+
dir={boxDir}
|
|
182
|
+
style={{ '--jaweb-accent': colorCode || '#111111' } as React.CSSProperties}
|
|
183
|
+
>
|
|
176
184
|
{isFetchingDetails ? (
|
|
177
185
|
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: 300 }}>
|
|
178
186
|
<Spin indicator={antIcon} tip="..." />
|
|
179
187
|
</div>
|
|
180
188
|
) : (
|
|
181
189
|
<AnimatePresence mode="sync">
|
|
182
|
-
{viewMode === '
|
|
190
|
+
{viewMode === 'home' && (
|
|
183
191
|
<motion.div
|
|
184
|
-
key="
|
|
185
|
-
initial={{ opacity: 0,
|
|
186
|
-
animate={{ opacity: 1,
|
|
187
|
-
exit={{ opacity: 0,
|
|
188
|
-
transition={{ duration: 0.
|
|
189
|
-
style={{height:'100%'}}
|
|
192
|
+
key="home"
|
|
193
|
+
initial={{ opacity: 0, y: 20 }}
|
|
194
|
+
animate={{ opacity: 1, y: 0 }}
|
|
195
|
+
exit={{ opacity: 0, y: -20 }}
|
|
196
|
+
transition={{ duration: 0.25 }}
|
|
197
|
+
style={{ height: '100%' }}
|
|
190
198
|
>
|
|
191
|
-
<
|
|
199
|
+
<Home
|
|
192
200
|
chatbotLogo={chatbotLogo}
|
|
193
|
-
|
|
201
|
+
chatbotName={chatbotName}
|
|
194
202
|
colorCode={colorCode}
|
|
195
203
|
visitorId={visitorData?.visitorId || ''}
|
|
196
|
-
|
|
197
|
-
|
|
204
|
+
merchantPhone={merchantPhone}
|
|
205
|
+
onClose={() => requestClose()}
|
|
198
206
|
onNewSession={handleNewSessionClick}
|
|
199
207
|
onSessionSelect={handleSessionSelect}
|
|
200
208
|
/>
|
|
@@ -214,7 +222,7 @@ const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | '
|
|
|
214
222
|
colorCode={colorCode}
|
|
215
223
|
visitorId={visitorData?.visitorId || ''}
|
|
216
224
|
country={visitorData?.country || ''}
|
|
217
|
-
onBack={() => setViewMode('
|
|
225
|
+
onBack={() => setViewMode('home')}
|
|
218
226
|
onCreate={(userData) => {
|
|
219
227
|
setActiveSessionId(userData.session_id);
|
|
220
228
|
setHasInfo(userData.hasInfo);
|
|
@@ -302,9 +310,8 @@ const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | '
|
|
|
302
310
|
onTryOnSelect={(product: any) => {
|
|
303
311
|
setTryOnProduct(product);
|
|
304
312
|
setViewMode('tryOn');
|
|
305
|
-
|
|
313
|
+
|
|
306
314
|
}}
|
|
307
|
-
userType={userType}
|
|
308
315
|
disbleCheckout={disbleCheckout}
|
|
309
316
|
setMessagesSuggestions={setMessagesSuggestions}
|
|
310
317
|
enableTry={enableTryOn}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useState, useMemo } from "react";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
2
3
|
import {
|
|
3
4
|
CloseOutlined,
|
|
4
5
|
ArrowLeftOutlined,
|
|
@@ -28,6 +29,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
28
29
|
}) => {
|
|
29
30
|
const { items, loading, removeFromCart, refresh } = useCart(sessionId || "");
|
|
30
31
|
const { checkOut } = useCartAPI();
|
|
32
|
+
const { t } = useTranslation();
|
|
31
33
|
const [isLoading, setIsLoading] = useState(false);
|
|
32
34
|
|
|
33
35
|
/** ✅ Parse price from strings like "$2,000" or "USD 49.99" */
|
|
@@ -64,7 +66,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
64
66
|
try {
|
|
65
67
|
await checkOut(sessionId);
|
|
66
68
|
} catch (err: any) {
|
|
67
|
-
message.error("
|
|
69
|
+
message.error(t("cart.checkoutFailed"));
|
|
68
70
|
console.error("Checkout error:", err);
|
|
69
71
|
} finally {
|
|
70
72
|
setIsLoading(false);
|
|
@@ -73,7 +75,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
73
75
|
|
|
74
76
|
const loadingIcon = (
|
|
75
77
|
<LoadingOutlined
|
|
76
|
-
style={{ fontSize: 24, color: colorCode || "#
|
|
78
|
+
style={{ fontSize: 24, color: colorCode || "#111111" }}
|
|
77
79
|
spin
|
|
78
80
|
/>
|
|
79
81
|
);
|
|
@@ -130,7 +132,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
130
132
|
}}
|
|
131
133
|
onMouseEnter={(e) =>
|
|
132
134
|
((e.currentTarget as HTMLElement).style.color =
|
|
133
|
-
colorCode || "#
|
|
135
|
+
colorCode || "#111111")
|
|
134
136
|
}
|
|
135
137
|
onMouseLeave={(e) =>
|
|
136
138
|
((e.currentTarget as HTMLElement).style.color = "#555")
|
|
@@ -150,7 +152,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
150
152
|
>
|
|
151
153
|
<h3 style={{ fontWeight: 600, marginBottom: 16 }}>
|
|
152
154
|
<ShoppingOutlined style={{ marginRight: 8 }} />
|
|
153
|
-
|
|
155
|
+
{t('cart.yourCart', { count: items.length })}
|
|
154
156
|
</h3>
|
|
155
157
|
|
|
156
158
|
{loading ? (
|
|
@@ -159,7 +161,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
159
161
|
</div>
|
|
160
162
|
) : items.length === 0 ? (
|
|
161
163
|
<div style={{ textAlign: "center", color: "#888", marginTop: 60 }}>
|
|
162
|
-
|
|
164
|
+
{t('cart.empty')}
|
|
163
165
|
</div>
|
|
164
166
|
) : (
|
|
165
167
|
<div
|
|
@@ -215,7 +217,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
215
217
|
{item.name}
|
|
216
218
|
</p>
|
|
217
219
|
<p style={{ fontSize: 13, color: "#777", margin: 0 }}>
|
|
218
|
-
|
|
220
|
+
{t('cart.qty', { count: item.quantity })}
|
|
219
221
|
</p>
|
|
220
222
|
</div>
|
|
221
223
|
</div>
|
|
@@ -271,7 +273,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
271
273
|
color: "#222",
|
|
272
274
|
}}
|
|
273
275
|
>
|
|
274
|
-
<span>
|
|
276
|
+
<span>{t('cart.subtotal')}</span>
|
|
275
277
|
<span>{subtotal.toLocaleString()}</span>
|
|
276
278
|
</div>
|
|
277
279
|
|
|
@@ -289,7 +291,7 @@ const CartLayout: React.FC<CartSessionProps> = ({
|
|
|
289
291
|
fontWeight: 600,
|
|
290
292
|
}}
|
|
291
293
|
>
|
|
292
|
-
<span style={{ color: "white" }}>
|
|
294
|
+
<span style={{ color: "white" }}>{t("cart.checkout")}</span>
|
|
293
295
|
</Button>
|
|
294
296
|
</div>
|
|
295
297
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useRef, useEffect } from "react";
|
|
2
2
|
import type { ChangeEvent } from "react";
|
|
3
|
-
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { message as antMessage } from "antd";
|
|
5
5
|
import {
|
|
6
6
|
PictureOutlined,
|
|
@@ -42,7 +42,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
42
42
|
isSending,
|
|
43
43
|
colorCode,
|
|
44
44
|
}) => {
|
|
45
|
-
|
|
45
|
+
const { t } = useTranslation();
|
|
46
46
|
const [isRecording, setIsRecording] = useState(false);
|
|
47
47
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
48
48
|
const [recordingDuration, setRecordingDuration] = useState(0);
|
|
@@ -87,7 +87,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
87
87
|
const validateImage = (file: File): boolean => {
|
|
88
88
|
const maxSize = 5 * 1024 * 1024; // 5MB
|
|
89
89
|
if (file.size > maxSize) {
|
|
90
|
-
antMessage.warning("
|
|
90
|
+
antMessage.warning(t("input.imageTooBig"));
|
|
91
91
|
return false;
|
|
92
92
|
}
|
|
93
93
|
return true;
|
|
@@ -143,7 +143,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
143
143
|
|
|
144
144
|
const handleVoiceRecord = async () => {
|
|
145
145
|
if (!navigator.mediaDevices?.getUserMedia) {
|
|
146
|
-
antMessage.error("
|
|
146
|
+
antMessage.error(t("input.recordingUnsupported"));
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -183,7 +183,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
183
183
|
setIsRecording(true);
|
|
184
184
|
} catch (err) {
|
|
185
185
|
console.error(err);
|
|
186
|
-
antMessage.error("
|
|
186
|
+
antMessage.error(t("input.micDenied"));
|
|
187
187
|
}
|
|
188
188
|
} else {
|
|
189
189
|
mediaRecorderRef.current?.stop();
|
|
@@ -316,7 +316,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
316
316
|
<button
|
|
317
317
|
className="audio-cancel-button"
|
|
318
318
|
onClick={handleCancelRecording}
|
|
319
|
-
aria-label="
|
|
319
|
+
aria-label={t("input.cancelRecording")}
|
|
320
320
|
>
|
|
321
321
|
<CloseCircleOutlined />
|
|
322
322
|
</button>
|
|
@@ -338,7 +338,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
338
338
|
ref={textareaRef}
|
|
339
339
|
value={userMessage}
|
|
340
340
|
onChange={handleTextareaChange}
|
|
341
|
-
placeholder="
|
|
341
|
+
placeholder={t("input.placeholder")}
|
|
342
342
|
className="text-input"
|
|
343
343
|
disabled={isSending}
|
|
344
344
|
rows={1}
|
|
@@ -386,7 +386,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
386
386
|
className="action-icon-button"
|
|
387
387
|
onClick={() => fileInputRef.current?.click()}
|
|
388
388
|
disabled={isSending || isRecording}
|
|
389
|
-
aria-label="
|
|
389
|
+
aria-label={t("input.uploadImage")}
|
|
390
390
|
>
|
|
391
391
|
<PictureOutlined />
|
|
392
392
|
</button>
|
|
@@ -395,7 +395,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
395
395
|
onClick={handleVoiceRecord}
|
|
396
396
|
className={`action-icon-button ${isRecording ? "recording" : ""}`}
|
|
397
397
|
disabled={isSending}
|
|
398
|
-
aria-label={isRecording ? "
|
|
398
|
+
aria-label={isRecording ? t("input.stopRecording") : t("input.recordVoice")}
|
|
399
399
|
>
|
|
400
400
|
{isRecording ? <StopOutlined /> : <AudioOutlined />}
|
|
401
401
|
</button>
|
|
@@ -428,7 +428,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
428
428
|
}
|
|
429
429
|
}}
|
|
430
430
|
className={`send-button ${hasContent && !isSending ? "active" : ""}`}
|
|
431
|
-
aria-label="
|
|
431
|
+
aria-label={t("input.sendMessage")}
|
|
432
432
|
>
|
|
433
433
|
{isSending ? (
|
|
434
434
|
<LoadingOutlined className="send-icon spin" />
|
|
@@ -442,7 +442,7 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
442
442
|
{isDragging && (
|
|
443
443
|
<div className="drag-overlay">
|
|
444
444
|
<PictureOutlined className="drag-icon" />
|
|
445
|
-
<span>
|
|
445
|
+
<span>{t("input.dropImage")}</span>
|
|
446
446
|
</div>
|
|
447
447
|
)}
|
|
448
448
|
</div>
|