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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shopify-chatbot-widget",
3
3
  "private": false,
4
- "version": "1.0.9",
4
+ "version": "1.0.10",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
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'>
@@ -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
- position: fixed;
3
- bottom: 20px;
4
- right: 20px;
5
- z-index: 9999;
6
-
7
- }
21
+ position: fixed;
22
+ bottom: 20px;
23
+ right: 20px;
24
+ z-index: 9999;
25
+ }
8
26
 
9
- /* Tailwind global CSS file */
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; /* 85% of viewport height */
28
- max-height: 800px; /* Even taller max height */
42
+ height: 85vh;
43
+ max-height: 800px;
29
44
  z-index: 9999;
30
- background: #ffffff;
31
- color: #000000;
32
- border-radius: 12px;
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 8px 24px rgba(0, 0, 0, 0.2);
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
- background: #f7f7f7;
42
- padding: 16px;
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
- font-weight: bold;
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; /* Take remaining space */
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: 12px;
59
- background-color: #fafafa;
60
- min-height: 0; /* Important for flex overflow */
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: #ffffff;
76
- border-top: 1px solid #e0e0e0;
77
- padding: 12px;
78
- flex-shrink: 0; /* Prevent input area from shrinking */
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
- .chatbot-title {
104
- flex-grow: 1;
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
- .chat-launcher.chatbot-open {
179
- display: none;
180
- }
181
- } */
257
+ @keyframes blink {
258
+ 0% { opacity: 0.2; }
259
+ 20% { opacity: 1; }
260
+ 100% { opacity: 0.2; }
261
+ }
@@ -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<'sessions' | 'create' | 'messenger' | 'cartView' | 'tryOn'>('sessions');
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||'#7F28F8' }} spin />;
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 className="chatbot-box">
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 === 'sessions' && (
190
+ {viewMode === 'home' && (
183
191
  <motion.div
184
- key="sessions"
185
- initial={{ opacity: 0, x: -30 }}
186
- animate={{ opacity: 1, x: 0 }}
187
- exit={{ opacity: 0, x: 30 }}
188
- transition={{ duration: 0.3 }}
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
- <ChatSessions
199
+ <Home
192
200
  chatbotLogo={chatbotLogo}
193
- Phone={merchantPhone || ''}
201
+ chatbotName={chatbotName}
194
202
  colorCode={colorCode}
195
203
  visitorId={visitorData?.visitorId || ''}
196
- Opened={open}
197
- setOpen={(v: boolean) => (v ? requestOpen() : requestClose())}
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('sessions')}
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("Checkout failed. Please try again.");
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 || "#7F28F8" }}
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 || "#7F28F8")
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
- Your Cart ({items.length})
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
- Your cart is empty 🛒
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
- Qty: {item.quantity}
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>Subtotal:</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" }}>CHECKOUT</span>
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
- // import { useTranslation } from "react-i18next";
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
- // const { t } = useTranslation();
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("Image size should be less than 5MB");
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("Voice recording is not supported in your browser");
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("Microphone permission denied");
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="Cancel recording"
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="Enter Message ..."
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="Upload image"
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 ? "Stop recording" : "Record voice"}
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="Send message"
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>Drop image here</span>
445
+ <span>{t("input.dropImage")}</span>
446
446
  </div>
447
447
  )}
448
448
  </div>