shopify-chatbot-widget 1.0.4 → 1.0.5
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 +144 -139
- package/package.json +1 -1
- package/src/Chatbot/Chatbot.tsx +62 -5
- package/src/Chatbot/components/Messages/AssistantMesage.tsx +9 -2
- package/src/Chatbot/components/Messages/Carousel/CardSwiper.tsx +19 -17
- package/src/Chatbot/components/Messages/ChatMessages.tsx +18 -3
- package/src/Chatbot/components/Messages/SallaAssistantMessage.tsx +1 -0
- package/src/Chatbot/components/Messages/Typing.css +32 -111
- package/src/Chatbot/components/Messages/Typing.tsx +29 -18
- package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +2 -0
- package/src/Chatbot/components/Messenger/Messenger.tsx +10 -3
- package/src/Chatbot/components/Sessions/RenderList.tsx +3 -0
- package/src/Chatbot/components/TryOn/View.tsx +408 -0
- package/src/hooks/config.tsx +4 -4
- package/src/hooks/useChatbotAPI.tsx +1 -1
- package/src/hooks/useChatbotData.tsx +4 -1
- package/src/types/chatbot.ts +1 -0
package/package.json
CHANGED
package/src/Chatbot/Chatbot.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect,useCallback } from 'react';
|
|
2
2
|
import { useChatbotData } from '../hooks/useChatbotData';
|
|
3
3
|
import ChatSessions from './components/Sessions/RenderList';
|
|
4
4
|
import CreateSession from './components/Sessions/CreateSession';
|
|
@@ -10,15 +10,18 @@ import { LoadingOutlined } from '@ant-design/icons';
|
|
|
10
10
|
import { Spin } from 'antd';
|
|
11
11
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
12
12
|
import CartLayout from './components/Cart/Index';
|
|
13
|
-
|
|
13
|
+
import TryOnLayout from './components/TryOn/View';
|
|
14
|
+
import { useCart } from '../hooks/useCart';
|
|
14
15
|
const Chatbot: React.FC = () => {
|
|
15
|
-
const [open, setOpen] = useState(
|
|
16
|
-
|
|
16
|
+
const [open, setOpen] = useState(true);
|
|
17
|
+
const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | 'cartView' | 'tryOn'>('sessions');
|
|
17
18
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
18
19
|
const [HasInfo, setHasInfo] = useState(false);
|
|
19
20
|
const [activeAgentName, setActiveAgentName] = useState('');
|
|
20
21
|
const [activeAgentImage, setActiveAgentImage] = useState('');
|
|
21
22
|
const [newSession, setNewSession] = useState(false);
|
|
23
|
+
const [tryOnProduct, setTryOnProduct] = useState<any>(null);
|
|
24
|
+
const [counts, setCounts] = useState<Record<string, number>>({});
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
const {
|
|
@@ -32,7 +35,8 @@ const Chatbot: React.FC = () => {
|
|
|
32
35
|
calendly,
|
|
33
36
|
disbleCheckout,
|
|
34
37
|
chatbotName,
|
|
35
|
-
setMessagesSuggestions
|
|
38
|
+
setMessagesSuggestions,
|
|
39
|
+
enableTryOn
|
|
36
40
|
} = useChatbotData();
|
|
37
41
|
|
|
38
42
|
const [visitorData, setVisitorData] = useState<{ visitorId: string; country: string } | null>(null);
|
|
@@ -141,6 +145,30 @@ const Chatbot: React.FC = () => {
|
|
|
141
145
|
}
|
|
142
146
|
}, [isFetchingDetails]);
|
|
143
147
|
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
const { addToCart, removeFromCart } = useCart(activeSessionId ?? "");
|
|
152
|
+
|
|
153
|
+
const handleToCart = useCallback(
|
|
154
|
+
async (variantId: string, name: string, variantImage: string,price: string ,qty = 1) => {
|
|
155
|
+
try {
|
|
156
|
+
await addToCart(variantId, name, qty, variantImage,price);
|
|
157
|
+
|
|
158
|
+
} catch (err: any) {
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
[addToCart]
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
const handleRemoveFromCart = useCallback(
|
|
166
|
+
async (variantId: string, qty = 1) => {
|
|
167
|
+
await removeFromCart(variantId, qty);
|
|
168
|
+
},
|
|
169
|
+
[removeFromCart]
|
|
170
|
+
);
|
|
171
|
+
|
|
144
172
|
return (
|
|
145
173
|
<div>
|
|
146
174
|
{open && (
|
|
@@ -222,6 +250,29 @@ const Chatbot: React.FC = () => {
|
|
|
222
250
|
|
|
223
251
|
</motion.div>
|
|
224
252
|
)}
|
|
253
|
+
|
|
254
|
+
{viewMode === 'tryOn' && (
|
|
255
|
+
<motion.div
|
|
256
|
+
key="tryOn"
|
|
257
|
+
initial={{ opacity: 0, scale: 0.9 }}
|
|
258
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
259
|
+
exit={{ opacity: 0, scale: 0.9 }}
|
|
260
|
+
transition={{ duration: 0.3 }}
|
|
261
|
+
style={{ height: '100%' }}
|
|
262
|
+
>
|
|
263
|
+
<TryOnLayout
|
|
264
|
+
product={tryOnProduct} // <--- PASS DATA HERE
|
|
265
|
+
colorCode={colorCode}
|
|
266
|
+
setOpen={(v: boolean) => (v ? requestOpen() : requestClose())}
|
|
267
|
+
onBack={() => setViewMode("messenger")}
|
|
268
|
+
handleAddToCart={handleToCart}
|
|
269
|
+
handleRemoveFromCart={handleRemoveFromCart}
|
|
270
|
+
counts={counts}
|
|
271
|
+
setCounts={setCounts}
|
|
272
|
+
/>
|
|
273
|
+
</motion.div>
|
|
274
|
+
)}
|
|
275
|
+
|
|
225
276
|
|
|
226
277
|
|
|
227
278
|
{viewMode === 'messenger' && activeSessionId && (
|
|
@@ -246,9 +297,15 @@ const Chatbot: React.FC = () => {
|
|
|
246
297
|
setNewSession={setNewSession}
|
|
247
298
|
Opened={open}
|
|
248
299
|
setView={setViewMode}
|
|
300
|
+
onTryOnSelect={(product: any) => {
|
|
301
|
+
setTryOnProduct(product);
|
|
302
|
+
setViewMode('tryOn');
|
|
303
|
+
|
|
304
|
+
}}
|
|
249
305
|
userType={userType}
|
|
250
306
|
disbleCheckout={disbleCheckout}
|
|
251
307
|
setMessagesSuggestions={setMessagesSuggestions}
|
|
308
|
+
enableTry={enableTryOn}
|
|
252
309
|
/>
|
|
253
310
|
</motion.div>
|
|
254
311
|
)}
|
|
@@ -24,6 +24,8 @@ interface AssistantMessageProps {
|
|
|
24
24
|
disableCheckout?:boolean;
|
|
25
25
|
handleAddToCart: (variantId: string, name: string,variantImage:string, price:string,qty?: number) => Promise<void>;
|
|
26
26
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
27
|
+
onTryOnSelect: (product: any) => void; // Add this
|
|
28
|
+
enableTry:boolean;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
const AssistantMessage: FC<AssistantMessageProps> = ({
|
|
@@ -31,7 +33,9 @@ const AssistantMessage: FC<AssistantMessageProps> = ({
|
|
|
31
33
|
colorCode,
|
|
32
34
|
disableCheckout,
|
|
33
35
|
handleAddToCart,
|
|
34
|
-
handleRemoveFromCart
|
|
36
|
+
handleRemoveFromCart,
|
|
37
|
+
onTryOnSelect,
|
|
38
|
+
enableTry
|
|
35
39
|
|
|
36
40
|
}) => {
|
|
37
41
|
const [counts, setCounts] = useState<Record<string, number>>({});
|
|
@@ -192,7 +196,10 @@ const AssistantMessage: FC<AssistantMessageProps> = ({
|
|
|
192
196
|
currentSlide={currentSlide}
|
|
193
197
|
totalSlides={totalSlides}
|
|
194
198
|
sliderRef={sliderRef}
|
|
195
|
-
disableCheckout={disableCheckout}
|
|
199
|
+
disableCheckout={disableCheckout}
|
|
200
|
+
onTryOnSelect={onTryOnSelect}
|
|
201
|
+
enableTry={enableTry}
|
|
202
|
+
// hides the – + controls
|
|
196
203
|
|
|
197
204
|
|
|
198
205
|
/>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import type { FC } from "react";
|
|
3
3
|
import "keen-slider/keen-slider.min.css";
|
|
4
4
|
import type { KeenSliderInstance } from "keen-slider/react";
|
|
5
5
|
import "./SallaSwiper.css";
|
|
6
6
|
import { Button } from "antd";
|
|
7
|
-
import TryOnBox from "./TryOn";
|
|
8
7
|
|
|
9
8
|
interface ProductVariant {
|
|
10
9
|
variant_id: string;
|
|
@@ -34,6 +33,8 @@ interface Props {
|
|
|
34
33
|
sliderInstance: React.MutableRefObject<KeenSliderInstance | null>;
|
|
35
34
|
currentSlide: number;
|
|
36
35
|
totalSlides: number;
|
|
36
|
+
onTryOnSelect: (product: ProductVariant) => void;
|
|
37
|
+
enableTry:boolean;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const ProductCarousel: FC<Props> = ({
|
|
@@ -49,11 +50,13 @@ const ProductCarousel: FC<Props> = ({
|
|
|
49
50
|
sliderRef,
|
|
50
51
|
totalSlides,
|
|
51
52
|
disableCheckout = false,
|
|
53
|
+
onTryOnSelect,
|
|
54
|
+
enableTry
|
|
55
|
+
|
|
52
56
|
}) => {
|
|
53
57
|
if (!totalSlides || totalSlides === 0) return null;
|
|
54
58
|
|
|
55
59
|
// track which variant is open for Try On
|
|
56
|
-
const [openTryOn, setOpenTryOn] = useState<string | null>(null);
|
|
57
60
|
|
|
58
61
|
return (
|
|
59
62
|
<section
|
|
@@ -125,7 +128,6 @@ const ProductCarousel: FC<Props> = ({
|
|
|
125
128
|
await handleRemoveFromCart(currentVariant.variant_id, 1);
|
|
126
129
|
};
|
|
127
130
|
|
|
128
|
-
const isTryOnOpen = openTryOn === currentVariant.variant_id;
|
|
129
131
|
|
|
130
132
|
return (
|
|
131
133
|
<article
|
|
@@ -227,24 +229,24 @@ const ProductCarousel: FC<Props> = ({
|
|
|
227
229
|
|
|
228
230
|
{/* Try On toggle button (kept) */}
|
|
229
231
|
|
|
232
|
+
{enableTry?
|
|
230
233
|
<Button
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
234
|
+
style={{ backgroundColor: colorCode, color: "white" }}
|
|
235
|
+
className="px-6 py-2 rounded-lg shadow-md transition"
|
|
236
|
+
onClick={() => {
|
|
237
|
+
onTryOnSelect(currentVariant);
|
|
238
|
+
}}
|
|
239
|
+
>
|
|
240
|
+
Try On
|
|
241
|
+
</Button>
|
|
242
|
+
:<></>
|
|
243
|
+
}
|
|
244
|
+
|
|
239
245
|
|
|
240
246
|
</div>
|
|
241
247
|
|
|
242
248
|
{/* Try On panel */}
|
|
243
|
-
|
|
244
|
-
<div className="mt-4">
|
|
245
|
-
<TryOnBox productID={currentVariant.variant_id} productImage={currentVariant.image} />
|
|
246
|
-
</div>
|
|
247
|
-
)}
|
|
249
|
+
|
|
248
250
|
</div>
|
|
249
251
|
</article>
|
|
250
252
|
);
|
|
@@ -29,6 +29,7 @@ interface Message {
|
|
|
29
29
|
isBookings?: boolean;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
|
|
32
33
|
interface ChatMessagesProps {
|
|
33
34
|
messages: Message[];
|
|
34
35
|
colorCode: string;
|
|
@@ -53,9 +54,11 @@ interface ChatMessagesProps {
|
|
|
53
54
|
calendly: string;
|
|
54
55
|
disableCheckout: boolean;
|
|
55
56
|
isfetchingMessage: boolean;
|
|
56
|
-
|
|
57
57
|
messagesSuggestions: string[];
|
|
58
58
|
sendMessageSuggestion: (message: string) => Promise<void>;
|
|
59
|
+
onTryOnSelect: (product: any) => void; // Add this
|
|
60
|
+
enableTry:boolean;
|
|
61
|
+
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
const InlineSuggestions: React.FC<{
|
|
@@ -120,6 +123,8 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
120
123
|
isfetchingMessage,
|
|
121
124
|
messagesSuggestions,
|
|
122
125
|
sendMessageSuggestion,
|
|
126
|
+
onTryOnSelect,
|
|
127
|
+
enableTry
|
|
123
128
|
}) => {
|
|
124
129
|
const bottomRef = useRef<HTMLDivElement>(null);
|
|
125
130
|
|
|
@@ -173,7 +178,11 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
173
178
|
if (msg.typing) {
|
|
174
179
|
return (
|
|
175
180
|
<div key={i} className="chat-msg typing">
|
|
176
|
-
|
|
181
|
+
<Typing
|
|
182
|
+
dotColor={colorCode}
|
|
183
|
+
bubbleColor="#FFFF"
|
|
184
|
+
size={10}
|
|
185
|
+
/>
|
|
177
186
|
</div>
|
|
178
187
|
);
|
|
179
188
|
}
|
|
@@ -252,6 +261,8 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
252
261
|
disableCheckout={disableCheckout}
|
|
253
262
|
handleAddToCart={handleAddToCart}
|
|
254
263
|
handleRemoveFromCart={handleRemoveFromCart}
|
|
264
|
+
onTryOnSelect={onTryOnSelect}
|
|
265
|
+
enableTry={enableTry}
|
|
255
266
|
/>
|
|
256
267
|
) : userType === "salla" ? (
|
|
257
268
|
<SallaAsssiatnt
|
|
@@ -259,13 +270,17 @@ const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
|
259
270
|
colorCode={colorCode}
|
|
260
271
|
handleAddToCart={handleAddToCart}
|
|
261
272
|
handleRemoveFromCart={handleRemoveFromCart}
|
|
262
|
-
|
|
273
|
+
// enableTry={enableTry}
|
|
274
|
+
|
|
275
|
+
/>
|
|
263
276
|
) : (
|
|
264
277
|
<ZidAssistantChatMessage
|
|
265
278
|
message={msg.content || msg.text || ""}
|
|
266
279
|
colorCode={colorCode}
|
|
267
280
|
handleAddToCart={handleAddToCart}
|
|
268
281
|
handleRemoveFromCart={handleRemoveFromCart}
|
|
282
|
+
// enableTry={enableTry}
|
|
283
|
+
|
|
269
284
|
/>
|
|
270
285
|
)}
|
|
271
286
|
</div>
|
|
@@ -22,6 +22,7 @@ interface AssistantMessageProps {
|
|
|
22
22
|
isBusiness?: boolean;
|
|
23
23
|
handleAddToCart: (variantId: string, name: string,variantImage:string,price:string, qty?: number) => Promise<void>;
|
|
24
24
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
25
|
+
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const SallaAsssiatnt: FC<AssistantMessageProps> = ({
|
|
@@ -1,134 +1,55 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
1
|
+
/* Typing.css */
|
|
2
|
+
|
|
3
|
+
.typing-container {
|
|
4
|
+
/* Layout */
|
|
3
5
|
display: inline-flex;
|
|
4
|
-
gap: 6px;
|
|
5
6
|
align-items: center;
|
|
6
|
-
justify-content:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
justify-content: center;
|
|
8
|
+
gap: var(--typing-gap);
|
|
9
|
+
|
|
10
|
+
/* Appearance */
|
|
11
|
+
background-color: var(--typing-bg);
|
|
12
|
+
padding: var(--typing-padding);
|
|
13
|
+
border-radius: 9999px; /* Pill shape */
|
|
14
|
+
|
|
15
|
+
/* Optional: Add a subtle shadow for depth */
|
|
16
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
17
|
+
|
|
18
|
+
/* Prevent layout shift */
|
|
19
|
+
will-change: transform;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
.typing-dot {
|
|
23
|
+
width: var(--typing-dot-size);
|
|
24
|
+
height: var(--typing-dot-size);
|
|
25
|
+
background-color: var(--typing-dot-color);
|
|
15
26
|
border-radius: 50%;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/* ===== DOTS VARIANT (Default - Smooth bounce) ===== */
|
|
20
|
-
.typing-dots .typing-dot {
|
|
21
|
-
animation: bounceDot 1.4s infinite ease-in-out both;
|
|
27
|
+
|
|
28
|
+
/* Animation definition */
|
|
29
|
+
animation: typingBounce 1.4s infinite ease-in-out both;
|
|
22
30
|
}
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
/* Stagger the animations */
|
|
33
|
+
.typing-dot:nth-child(1) {
|
|
25
34
|
animation-delay: -0.32s;
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
.typing-
|
|
37
|
+
.typing-dot:nth-child(2) {
|
|
29
38
|
animation-delay: -0.16s;
|
|
30
39
|
}
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
0%, 80%, 100% {
|
|
34
|
-
transform: translateY(0) scale(1);
|
|
35
|
-
opacity: 0.7;
|
|
36
|
-
}
|
|
37
|
-
40% {
|
|
38
|
-
transform: translateY(-10px) scale(1.1);
|
|
39
|
-
opacity: 1;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/* ===== BOXES VARIANT (Pulsing squares) ===== */
|
|
44
|
-
.typing-boxes .typing-dot {
|
|
45
|
-
border-radius: 3px;
|
|
46
|
-
animation: pulseBox 1s infinite ease-in-out both;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.typing-boxes .typing-dot:nth-child(1) {
|
|
41
|
+
.typing-dot:nth-child(3) {
|
|
50
42
|
animation-delay: 0s;
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.typing-boxes .typing-dot:nth-child(3) {
|
|
58
|
-
animation-delay: 0.4s;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@keyframes pulseBox {
|
|
62
|
-
0%, 100% {
|
|
45
|
+
/* The Animation Keyframes */
|
|
46
|
+
@keyframes typingBounce {
|
|
47
|
+
0%, 80%, 100% {
|
|
63
48
|
transform: scale(0.8);
|
|
64
49
|
opacity: 0.5;
|
|
65
50
|
}
|
|
66
|
-
|
|
67
|
-
transform: scale(1
|
|
68
|
-
opacity: 1;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* ===== WAVE VARIANT (Wave motion) ===== */
|
|
73
|
-
.typing-wave .typing-dot {
|
|
74
|
-
animation: wave 1.2s infinite ease-in-out;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.typing-wave .typing-dot:nth-child(1) {
|
|
78
|
-
animation-delay: 0s;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.typing-wave .typing-dot:nth-child(2) {
|
|
82
|
-
animation-delay: 0.15s;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.typing-wave .typing-dot:nth-child(3) {
|
|
86
|
-
animation-delay: 0.3s;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@keyframes wave {
|
|
90
|
-
0%, 60%, 100% {
|
|
91
|
-
transform: translateY(0);
|
|
92
|
-
opacity: 0.6;
|
|
93
|
-
}
|
|
94
|
-
30% {
|
|
95
|
-
transform: translateY(-12px);
|
|
51
|
+
40% {
|
|
52
|
+
transform: scale(1) translateY(-4px); /* Slight lift */
|
|
96
53
|
opacity: 1;
|
|
97
54
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/* ===== MINIMAL VARIANT (No background bubble) ===== */
|
|
101
|
-
.typing-indicator.minimal {
|
|
102
|
-
background: transparent;
|
|
103
|
-
padding: 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/* ===== DARK MODE SUPPORT ===== */
|
|
107
|
-
.typing-indicator.dark {
|
|
108
|
-
background: #374151;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/* ===== SIZE VARIANTS ===== */
|
|
112
|
-
.typing-indicator.small {
|
|
113
|
-
padding: 6px 10px;
|
|
114
|
-
gap: 4px;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.typing-indicator.large {
|
|
118
|
-
padding: 10px 16px;
|
|
119
|
-
gap: 8px;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/* ===== RESPONSIVE ===== */
|
|
123
|
-
@media (max-width: 600px) {
|
|
124
|
-
.typing-indicator {
|
|
125
|
-
padding: 6px 10px;
|
|
126
|
-
gap: 5px;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/* ===== SMOOTH PERFORMANCE ===== */
|
|
131
|
-
.typing-dot {
|
|
132
|
-
will-change: transform, opacity;
|
|
133
|
-
backface-visibility: hidden;
|
|
134
55
|
}
|
|
@@ -2,30 +2,41 @@ import React from 'react';
|
|
|
2
2
|
import './Typing.css';
|
|
3
3
|
|
|
4
4
|
interface TypingProps {
|
|
5
|
-
|
|
5
|
+
/** Color of the moving dots */
|
|
6
|
+
dotColor?: string;
|
|
7
|
+
/** Color of the bubble background */
|
|
8
|
+
bubbleColor?: string;
|
|
9
|
+
/** Size of the dots in pixels */
|
|
6
10
|
size?: number;
|
|
7
|
-
|
|
11
|
+
/** Optional class name for the container */
|
|
12
|
+
className?: string;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
const Typing: React.FC<TypingProps> = ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
dotColor = '#6b7280', // Default dark grey
|
|
17
|
+
bubbleColor = '#e5e7eb', // Default light grey bubble
|
|
18
|
+
size = 8,
|
|
19
|
+
className = ''
|
|
14
20
|
}) => {
|
|
21
|
+
// We pass dynamic values as CSS variables for better performance
|
|
22
|
+
const style = {
|
|
23
|
+
'--typing-dot-color': dotColor,
|
|
24
|
+
'--typing-bg': bubbleColor,
|
|
25
|
+
'--typing-dot-size': `${size}px`,
|
|
26
|
+
'--typing-gap': `${size * 0.5}px`, // Gap scales with size
|
|
27
|
+
'--typing-padding': `${size * 1.25}px ${size * 1.5}px` // Padding scales with size
|
|
28
|
+
} as React.CSSProperties;
|
|
29
|
+
|
|
15
30
|
return (
|
|
16
|
-
<div
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/>
|
|
25
|
-
<span
|
|
26
|
-
className="typing-dot"
|
|
27
|
-
style={{ backgroundColor: color, width: size, height: size }}
|
|
28
|
-
/>
|
|
31
|
+
<div
|
|
32
|
+
className={`typing-container ${className}`}
|
|
33
|
+
style={style}
|
|
34
|
+
role="status"
|
|
35
|
+
aria-label="Typing..."
|
|
36
|
+
>
|
|
37
|
+
<span className="typing-dot" />
|
|
38
|
+
<span className="typing-dot" />
|
|
39
|
+
<span className="typing-dot" />
|
|
29
40
|
</div>
|
|
30
41
|
);
|
|
31
42
|
};
|
|
@@ -22,6 +22,7 @@ interface AssistantMessageProps {
|
|
|
22
22
|
isBusiness?: boolean;
|
|
23
23
|
handleAddToCart: (variantId: string, name: string,variantImage:string,price:string, qty?: number) => Promise<void>;
|
|
24
24
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
25
|
+
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const SallaAsssiatnt: FC<AssistantMessageProps> = ({
|
|
@@ -29,6 +30,7 @@ const SallaAsssiatnt: FC<AssistantMessageProps> = ({
|
|
|
29
30
|
colorCode,
|
|
30
31
|
handleAddToCart,
|
|
31
32
|
handleRemoveFromCart,
|
|
33
|
+
|
|
32
34
|
}) => {
|
|
33
35
|
const [counts, setCounts] = useState<Record<string, number>>({});
|
|
34
36
|
const [selectedVariants, setSelectedVariants] = useState<Record<string, string>>({});
|
|
@@ -27,11 +27,14 @@ type MessengerProps = {
|
|
|
27
27
|
disableForm: boolean;
|
|
28
28
|
sessionId:string;
|
|
29
29
|
setOpen: (v: boolean) => void;
|
|
30
|
-
setView: React.Dispatch<React.SetStateAction<'sessions' | 'create' | 'messenger' | 'cartView'>>;
|
|
30
|
+
setView: React.Dispatch<React.SetStateAction<'sessions' | 'create' | 'messenger' | 'cartView' | 'tryOn'>>;
|
|
31
31
|
setNewSession: (v: boolean) => void;
|
|
32
32
|
Opened: boolean;
|
|
33
33
|
calendly:string;
|
|
34
34
|
disbleCheckout:boolean;
|
|
35
|
+
onTryOnSelect: (product: any) => void;
|
|
36
|
+
enableTry: boolean;
|
|
37
|
+
|
|
35
38
|
};
|
|
36
39
|
|
|
37
40
|
interface UploadImageResponse {
|
|
@@ -59,7 +62,9 @@ const Messenger: React.FC<MessengerProps> = ({
|
|
|
59
62
|
Opened,
|
|
60
63
|
setView,
|
|
61
64
|
calendly,
|
|
62
|
-
disbleCheckout
|
|
65
|
+
disbleCheckout,
|
|
66
|
+
onTryOnSelect,
|
|
67
|
+
enableTry
|
|
63
68
|
}) => {
|
|
64
69
|
const [input, setInput] = useState('');
|
|
65
70
|
const [selectedImage, setSelectedImage] = useState<File | null>(null);
|
|
@@ -550,7 +555,9 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
550
555
|
isfetchingMessage={isFetchingMessages}
|
|
551
556
|
messagesSuggestions={messagesSuggestions}
|
|
552
557
|
sendMessageSuggestion={sendMessageSuggestion}
|
|
553
|
-
|
|
558
|
+
onTryOnSelect={onTryOnSelect}
|
|
559
|
+
enableTry={enableTry}
|
|
560
|
+
/>
|
|
554
561
|
|
|
555
562
|
|
|
556
563
|
|
|
@@ -45,6 +45,7 @@ const ChatSessions: React.FC<ChatSessionsProps> = ({
|
|
|
45
45
|
`${config.apiUrl}get-chatlogs-by-ip/?visitor_id=${visitorId}&company_username=${localStorage.getItem('company_username')}`
|
|
46
46
|
);
|
|
47
47
|
const data = await res.json();
|
|
48
|
+
|
|
48
49
|
|
|
49
50
|
const mapped: ChatSession[] = data.map((item: any) => {
|
|
50
51
|
const createdDate = new Date(item.date);
|
|
@@ -63,6 +64,7 @@ const ChatSessions: React.FC<ChatSessionsProps> = ({
|
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
setSessions(mapped);
|
|
67
|
+
|
|
66
68
|
} catch (err) {
|
|
67
69
|
console.error('Failed to load sessions:', err);
|
|
68
70
|
} finally {
|
|
@@ -70,6 +72,7 @@ const ChatSessions: React.FC<ChatSessionsProps> = ({
|
|
|
70
72
|
}
|
|
71
73
|
};
|
|
72
74
|
|
|
75
|
+
|
|
73
76
|
useEffect(() => {
|
|
74
77
|
if (visitorId) {
|
|
75
78
|
fetchSessions();
|