shopify-chatbot-widget 0.8.7 → 0.8.9
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 +169 -151
- package/package.json +1 -1
- package/src/Chatbot/Chatbot.tsx +35 -2
- package/src/Chatbot/components/Cart/Index.tsx +287 -0
- package/src/Chatbot/components/ChatInput.tsx +8 -3
- package/src/Chatbot/components/Messages/AssistantMesage.tsx +1 -1
- package/src/Chatbot/components/Messages/CardSwiper.tsx +2 -2
- package/src/Chatbot/components/Messages/Carousel/CardSwiper.tsx +2 -5
- package/src/Chatbot/components/Messages/Carousel/CardSwiperSalla.tsx +1 -1
- package/src/Chatbot/components/Messages/Carousel/CardSwiperZid.tsx +1 -1
- package/src/Chatbot/components/Messages/ChatMessages.tsx +5 -1
- package/src/Chatbot/components/Messages/SallaAssistantMessage.tsx +1 -1
- package/src/Chatbot/components/Messages/Support.tsx +2 -2
- package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +1 -1
- package/src/Chatbot/components/Messenger/Messenger.tsx +69 -20
- package/src/Chatbot/components/Sessions/RenderList.tsx +2 -2
- package/src/hooks/useCart.tsx +37 -28
- package/src/hooks/useChatbotData.tsx +1 -0
- package/src/hooks/useCheckout.tsx +45 -0
- package/src/Chatbot/OLDCHAT.tsx +0 -259
package/package.json
CHANGED
package/src/Chatbot/Chatbot.tsx
CHANGED
|
@@ -9,10 +9,11 @@ import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
|
|
9
9
|
import { LoadingOutlined } from '@ant-design/icons';
|
|
10
10
|
import { Spin } from 'antd';
|
|
11
11
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
12
|
+
import CartLayout from './components/Cart/Index';
|
|
12
13
|
|
|
13
14
|
const Chatbot: React.FC = () => {
|
|
14
15
|
const [open, setOpen] = useState(false);
|
|
15
|
-
const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger'>('sessions');
|
|
16
|
+
const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger' | 'cartView' >('sessions');
|
|
16
17
|
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
17
18
|
const [HasInfo, setHasInfo] = useState(false);
|
|
18
19
|
const [activeAgentName, setActiveAgentName] = useState('');
|
|
@@ -31,6 +32,7 @@ const Chatbot: React.FC = () => {
|
|
|
31
32
|
calendly,
|
|
32
33
|
disbleCheckout,
|
|
33
34
|
chatbotName,
|
|
35
|
+
setMessagesSuggestions
|
|
34
36
|
} = useChatbotData();
|
|
35
37
|
|
|
36
38
|
const [visitorData, setVisitorData] = useState<{ visitorId: string; country: string } | null>(null);
|
|
@@ -177,6 +179,34 @@ const Chatbot: React.FC = () => {
|
|
|
177
179
|
</motion.div>
|
|
178
180
|
)}
|
|
179
181
|
|
|
182
|
+
{viewMode === 'cartView' && (
|
|
183
|
+
<motion.div
|
|
184
|
+
key="create"
|
|
185
|
+
initial={{ opacity: 0, y: 30 }}
|
|
186
|
+
animate={{ opacity: 1, y: 0 }}
|
|
187
|
+
exit={{ opacity: 0, y: -30 }}
|
|
188
|
+
transition={{ duration: 0.3 }}
|
|
189
|
+
style={{height:'100%'}}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
>
|
|
193
|
+
<CartLayout
|
|
194
|
+
setOpen={(v: boolean) => (v ? requestOpen() : requestClose())}
|
|
195
|
+
sessionId={activeSessionId}
|
|
196
|
+
Opened={open}
|
|
197
|
+
chatbotLogo={chatbotLogo}
|
|
198
|
+
colorCode={colorCode}
|
|
199
|
+
onBack={() => {
|
|
200
|
+
setNewSession(false);
|
|
201
|
+
setViewMode("messenger");
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
</motion.div>
|
|
207
|
+
)}
|
|
208
|
+
|
|
209
|
+
|
|
180
210
|
{viewMode === 'messenger' && activeSessionId && (
|
|
181
211
|
<motion.div
|
|
182
212
|
key="messenger"
|
|
@@ -198,12 +228,15 @@ const Chatbot: React.FC = () => {
|
|
|
198
228
|
setOpen={(v: boolean) => (v ? requestOpen() : requestClose())}
|
|
199
229
|
setNewSession={setNewSession}
|
|
200
230
|
Opened={open}
|
|
201
|
-
setView={
|
|
231
|
+
setView={setViewMode}
|
|
202
232
|
userType={userType}
|
|
203
233
|
disbleCheckout={disbleCheckout}
|
|
234
|
+
setMessagesSuggestions={setMessagesSuggestions}
|
|
204
235
|
/>
|
|
205
236
|
</motion.div>
|
|
206
237
|
)}
|
|
238
|
+
|
|
239
|
+
|
|
207
240
|
</AnimatePresence>
|
|
208
241
|
)}
|
|
209
242
|
</div>
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
CloseOutlined,
|
|
4
|
+
ArrowLeftOutlined,
|
|
5
|
+
DeleteOutlined,
|
|
6
|
+
ShoppingOutlined,
|
|
7
|
+
LoadingOutlined
|
|
8
|
+
} from "@ant-design/icons";
|
|
9
|
+
import { Button, Spin, message } from "antd";
|
|
10
|
+
import { useCart } from "../../../hooks/useCart";
|
|
11
|
+
import { useCartAPI } from "../../../hooks/useCheckout";
|
|
12
|
+
|
|
13
|
+
type CartSessionProps = {
|
|
14
|
+
setOpen: (v: boolean) => void;
|
|
15
|
+
sessionId: string | null;
|
|
16
|
+
Opened: boolean;
|
|
17
|
+
chatbotLogo: string;
|
|
18
|
+
colorCode?: string;
|
|
19
|
+
onBack: () => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CartLayout: React.FC<CartSessionProps> = ({
|
|
23
|
+
colorCode,
|
|
24
|
+
setOpen,
|
|
25
|
+
Opened,
|
|
26
|
+
onBack,
|
|
27
|
+
sessionId,
|
|
28
|
+
}) => {
|
|
29
|
+
const { items, loading, removeFromCart, refresh } = useCart(sessionId || "");
|
|
30
|
+
const { checkOut } = useCartAPI();
|
|
31
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
const handleCheckout = async () => {
|
|
35
|
+
if (!sessionId) {
|
|
36
|
+
message.warning("No session found — please start a chat first.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setIsLoading(true);
|
|
41
|
+
try {
|
|
42
|
+
await checkOut(sessionId);
|
|
43
|
+
} catch (err: any) {
|
|
44
|
+
message.error("Checkout failed. Please try again.");
|
|
45
|
+
console.error("Checkout error:", err);
|
|
46
|
+
} finally {
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const loadingIcon = <LoadingOutlined style={{ fontSize: 24, color: colorCode||'#7F28F8' }} spin />;
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
const handleRemove = async (variantId: string) => {
|
|
55
|
+
await removeFromCart(variantId, 1);
|
|
56
|
+
message.info("Item removed from cart");
|
|
57
|
+
await refresh();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
style={{
|
|
63
|
+
maxWidth: "100%",
|
|
64
|
+
height: "100%",
|
|
65
|
+
border: "1px solid #f0f0f0",
|
|
66
|
+
borderRadius: 8,
|
|
67
|
+
display: "flex",
|
|
68
|
+
flexDirection: "column",
|
|
69
|
+
backgroundColor: "#fff",
|
|
70
|
+
overflow: "hidden",
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{/* Header */}
|
|
74
|
+
<div
|
|
75
|
+
style={{
|
|
76
|
+
padding: "16px",
|
|
77
|
+
display: "flex",
|
|
78
|
+
alignItems: "center",
|
|
79
|
+
borderBottom: "1px solid #f5f5f5",
|
|
80
|
+
boxShadow: "0 2px 6px rgba(0,0,0,0.03)",
|
|
81
|
+
zIndex: 5,
|
|
82
|
+
height:'8%'
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<Button
|
|
86
|
+
type="text"
|
|
87
|
+
icon={<ArrowLeftOutlined />}
|
|
88
|
+
onClick={onBack}
|
|
89
|
+
style={{ color: "#333", fontWeight: 500 }}
|
|
90
|
+
>
|
|
91
|
+
Back
|
|
92
|
+
</Button>
|
|
93
|
+
|
|
94
|
+
<div style={{ marginLeft: "auto" }}>
|
|
95
|
+
<CloseOutlined
|
|
96
|
+
onClick={() => setOpen(!Opened)}
|
|
97
|
+
style={{
|
|
98
|
+
fontSize: 18,
|
|
99
|
+
cursor: "pointer",
|
|
100
|
+
color: "#555",
|
|
101
|
+
transition: "color 0.2s ease",
|
|
102
|
+
}}
|
|
103
|
+
onMouseEnter={(e) =>
|
|
104
|
+
((e.currentTarget as HTMLElement).style.color =
|
|
105
|
+
colorCode || "#7F28F8")
|
|
106
|
+
}
|
|
107
|
+
onMouseLeave={(e) =>
|
|
108
|
+
((e.currentTarget as HTMLElement).style.color = "#555")
|
|
109
|
+
}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{/* Scrollable Content */}
|
|
115
|
+
<div
|
|
116
|
+
style={{
|
|
117
|
+
flex: 1,
|
|
118
|
+
overflowY: "auto",
|
|
119
|
+
padding: "16px",
|
|
120
|
+
height:'66%'
|
|
121
|
+
|
|
122
|
+
}}
|
|
123
|
+
|
|
124
|
+
>
|
|
125
|
+
<h3 style={{ fontWeight: 600, marginBottom: 16 }}>
|
|
126
|
+
<ShoppingOutlined style={{ marginRight: 8 }} />
|
|
127
|
+
Your Cart ({items.length})
|
|
128
|
+
</h3>
|
|
129
|
+
|
|
130
|
+
{loading ? (
|
|
131
|
+
<div style={{ textAlign: "center", marginTop: 40 }}>
|
|
132
|
+
<Spin indicator={loadingIcon} />
|
|
133
|
+
</div>
|
|
134
|
+
) : items.length === 0 ? (
|
|
135
|
+
<div style={{ textAlign: "center", color: "#888", marginTop: 60 }}>
|
|
136
|
+
Your cart is empty 🛒
|
|
137
|
+
</div>
|
|
138
|
+
) : (
|
|
139
|
+
<div
|
|
140
|
+
style={{
|
|
141
|
+
flex: 1, // fill remaining space
|
|
142
|
+
overflowY: "auto", // scroll only this part
|
|
143
|
+
display: "flex",
|
|
144
|
+
flexDirection: "column",
|
|
145
|
+
height:'80%',
|
|
146
|
+
gap: "12px",
|
|
147
|
+
paddingRight: "4px",
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
{items.map((item) => (
|
|
151
|
+
|
|
152
|
+
<div
|
|
153
|
+
key={item.variantId}
|
|
154
|
+
style={{
|
|
155
|
+
display: "flex",
|
|
156
|
+
justifyContent: "space-between",
|
|
157
|
+
alignItems: "center",
|
|
158
|
+
background: "#fafafa",
|
|
159
|
+
borderRadius: 10,
|
|
160
|
+
padding: "12px 10px",
|
|
161
|
+
transition: "all 0.2s ease",
|
|
162
|
+
}}
|
|
163
|
+
className="hover:shadow-sm"
|
|
164
|
+
>
|
|
165
|
+
{/* ✅ Left side */}
|
|
166
|
+
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
|
167
|
+
<img
|
|
168
|
+
src={item.variantImage}
|
|
169
|
+
alt={item.name}
|
|
170
|
+
style={{
|
|
171
|
+
width: 64,
|
|
172
|
+
height: 64,
|
|
173
|
+
borderRadius: 8,
|
|
174
|
+
objectFit: "cover",
|
|
175
|
+
backgroundColor: "#f3f3f3",
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
<div>
|
|
179
|
+
<p style={{ fontWeight: 600, color: "#222", marginBottom: 4 }}>
|
|
180
|
+
{item.name}
|
|
181
|
+
</p>
|
|
182
|
+
<p style={{ fontSize: 13, color: "#777", margin: 0 }}>
|
|
183
|
+
Qty: {item.quantity}
|
|
184
|
+
</p>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{/* ✅ Right side */}
|
|
189
|
+
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
|
190
|
+
<span style={{ fontWeight: 600 }}>£29.99</span>
|
|
191
|
+
<DeleteOutlined
|
|
192
|
+
onClick={() => handleRemove(item.variantId)}
|
|
193
|
+
style={{
|
|
194
|
+
cursor: "pointer",
|
|
195
|
+
color: "#999",
|
|
196
|
+
fontSize: 16,
|
|
197
|
+
transition: "color 0.2s ease",
|
|
198
|
+
}}
|
|
199
|
+
onMouseEnter={(e) =>
|
|
200
|
+
((e.currentTarget as HTMLElement).style.color = "#e74c3c")
|
|
201
|
+
}
|
|
202
|
+
onMouseLeave={(e) =>
|
|
203
|
+
((e.currentTarget as HTMLElement).style.color = "#999")
|
|
204
|
+
}
|
|
205
|
+
/>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
))}
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
|
|
213
|
+
{/* <Divider style={{ margin: "20px 0" }} /> */}
|
|
214
|
+
|
|
215
|
+
{/* Discount Section */}
|
|
216
|
+
{/* <div style={{ display: "flex", gap: "8px", marginBottom: "16px" }}>
|
|
217
|
+
<Input placeholder="Enter discount code" size="large" />
|
|
218
|
+
<Button
|
|
219
|
+
type="primary"
|
|
220
|
+
size="large"
|
|
221
|
+
style={{
|
|
222
|
+
backgroundColor: colorCode || "black",
|
|
223
|
+
borderColor: colorCode || "black",
|
|
224
|
+
borderRadius: 6,
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
227
|
+
APPLY
|
|
228
|
+
</Button>
|
|
229
|
+
</div> */}
|
|
230
|
+
|
|
231
|
+
{/* Summary */}
|
|
232
|
+
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
{/* Sticky Footer */}
|
|
236
|
+
<div
|
|
237
|
+
style={{
|
|
238
|
+
position: "sticky",
|
|
239
|
+
bottom: 0,
|
|
240
|
+
left: 0,
|
|
241
|
+
right: 0,
|
|
242
|
+
background: "#fff",
|
|
243
|
+
borderTop: "1px solid #f0f0f0",
|
|
244
|
+
padding: "16px 20px",
|
|
245
|
+
boxShadow: "0 -3px 10px rgba(0,0,0,0.05)",
|
|
246
|
+
zIndex: 10,
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
<Button
|
|
250
|
+
type="primary"
|
|
251
|
+
block
|
|
252
|
+
size="large"
|
|
253
|
+
onClick={handleCheckout}
|
|
254
|
+
loading={isLoading}
|
|
255
|
+
disabled={items.length === 0}
|
|
256
|
+
style={{
|
|
257
|
+
backgroundColor: colorCode || "black",
|
|
258
|
+
borderColor: colorCode || "black",
|
|
259
|
+
borderRadius: 10,
|
|
260
|
+
fontWeight: 600,
|
|
261
|
+
}}
|
|
262
|
+
>
|
|
263
|
+
<span style={{color:'white'}}>
|
|
264
|
+
CHECKOUT
|
|
265
|
+
|
|
266
|
+
</span>
|
|
267
|
+
</Button>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
{/* Scrollbar */}
|
|
271
|
+
<style>{`
|
|
272
|
+
.custom-scrollbar::-webkit-scrollbar {
|
|
273
|
+
width: 6px;
|
|
274
|
+
}
|
|
275
|
+
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
276
|
+
background-color: rgba(0,0,0,0.2);
|
|
277
|
+
border-radius: 3px;
|
|
278
|
+
}
|
|
279
|
+
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
280
|
+
background-color: rgba(0,0,0,0.4);
|
|
281
|
+
}
|
|
282
|
+
`}</style>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export default CartLayout;
|
|
@@ -300,18 +300,23 @@ const ChatInput: React.FC<ChatInputProps> = ({
|
|
|
300
300
|
padding: "0"
|
|
301
301
|
}}
|
|
302
302
|
onKeyDown={(e) => {
|
|
303
|
-
if (e.key === "Enter") {
|
|
303
|
+
if (e.key === "Enter" && !isSending) {
|
|
304
304
|
e.preventDefault();
|
|
305
|
-
|
|
305
|
+
|
|
306
|
+
const hasImage = !!selectedImage;
|
|
307
|
+
const hasText = !!userMessage.trim();
|
|
308
|
+
|
|
309
|
+
if (hasImage) {
|
|
306
310
|
handleSendImage(selectedImage, userMessage);
|
|
307
311
|
removeSelectedImage();
|
|
308
312
|
setUserMessage("");
|
|
309
|
-
} else if (
|
|
313
|
+
} else if (hasText) {
|
|
310
314
|
handleSendMessage();
|
|
311
315
|
setUserMessage("");
|
|
312
316
|
}
|
|
313
317
|
}
|
|
314
318
|
}}
|
|
319
|
+
|
|
315
320
|
/>
|
|
316
321
|
)}
|
|
317
322
|
</div>
|
|
@@ -22,7 +22,7 @@ interface AssistantMessageProps {
|
|
|
22
22
|
colorCode: string;
|
|
23
23
|
isBusiness?: boolean;
|
|
24
24
|
disableCheckout?:boolean;
|
|
25
|
-
handleAddToCart: (variantId: string, name: string, qty?: number) => Promise<void>;
|
|
25
|
+
handleAddToCart: (variantId: string, name: string,variantImage:string, qty?: number) => Promise<void>;
|
|
26
26
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -20,7 +20,7 @@ interface Props {
|
|
|
20
20
|
setSelectedVariants: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
|
21
21
|
counts: Record<string, number>;
|
|
22
22
|
setCounts: React.Dispatch<React.SetStateAction<Record<string, number>>>;
|
|
23
|
-
handleAddToCart: (variantId: string, name: string, qty
|
|
23
|
+
handleAddToCart: (variantId: string, name: string,variantImage:string, qty?: number) => Promise<void>;
|
|
24
24
|
handleRemoveFromCart: (variantId: string, qty: number) => Promise<void>;
|
|
25
25
|
colorCode?: string;
|
|
26
26
|
shortenName: (name: string) => string;
|
|
@@ -88,7 +88,7 @@ const ProductCarousel: FC<Props> = ({
|
|
|
88
88
|
...prev,
|
|
89
89
|
[currentVariant.variant_id]: (prev[currentVariant.variant_id] || 0) + 1,
|
|
90
90
|
}));
|
|
91
|
-
await handleAddToCart(currentVariant.variant_id,
|
|
91
|
+
await handleAddToCart(currentVariant.variant_id,currentVariant.name,currentVariant.image, 1);
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
const decrement = async () => {
|
|
@@ -23,7 +23,7 @@ interface Props {
|
|
|
23
23
|
setSelectedVariants: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
|
24
24
|
counts: Record<string, number>;
|
|
25
25
|
setCounts: React.Dispatch<React.SetStateAction<Record<string, number>>>;
|
|
26
|
-
handleAddToCart: (variantId: string, name: string, qty?: number) => Promise<void>;
|
|
26
|
+
handleAddToCart: (variantId: string, name: string, variantImage:string, qty?: number) => Promise<void>;
|
|
27
27
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
28
28
|
colorCode?: string;
|
|
29
29
|
shortenName: (name: string) => string;
|
|
@@ -101,11 +101,8 @@ const ProductCarousel: FC<Props> = ({
|
|
|
101
101
|
[currentVariant.variant_id]: (prev[currentVariant.variant_id] || 0) + 1,
|
|
102
102
|
}));
|
|
103
103
|
try {
|
|
104
|
-
|
|
105
|
-
await handleAddToCart(currentVariant.variant_id, currentVariant.name, 1);
|
|
104
|
+
await handleAddToCart(currentVariant.variant_id, currentVariant.name,currentVariant.image, 1);
|
|
106
105
|
|
|
107
|
-
// only update counts if the request succeeded
|
|
108
|
-
|
|
109
106
|
} catch (err: any) {
|
|
110
107
|
console.warn("Add to cart failed:", err.message);
|
|
111
108
|
setCounts(prev => ({
|
|
@@ -21,7 +21,7 @@ interface Props {
|
|
|
21
21
|
setSelectedVariants: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
|
22
22
|
counts: Record<string, number>;
|
|
23
23
|
setCounts: React.Dispatch<React.SetStateAction<Record<string, number>>>;
|
|
24
|
-
handleAddToCart: (variantId: string, name: string, qty: number) => Promise<void>;
|
|
24
|
+
handleAddToCart: (variantId: string, name: string,variantImage:string, qty: number) => Promise<void>;
|
|
25
25
|
handleRemoveFromCart: (variantId: string, qty: number) => Promise<void>;
|
|
26
26
|
colorCode?: string; // accent
|
|
27
27
|
shortenName: (name: string) => string;
|
|
@@ -21,7 +21,7 @@ interface Props {
|
|
|
21
21
|
setSelectedVariants: React.Dispatch<React.SetStateAction<Record<string, string>>>;
|
|
22
22
|
counts: Record<string, number>;
|
|
23
23
|
setCounts: React.Dispatch<React.SetStateAction<Record<string, number>>>;
|
|
24
|
-
handleAddToCart: (variantId: string, name: string, qty: number) => Promise<void>;
|
|
24
|
+
handleAddToCart: (variantId: string, name: string, variantImage:string,qty: number) => Promise<void>;
|
|
25
25
|
handleRemoveFromCart: (variantId: string, qty: number) => Promise<void>;
|
|
26
26
|
colorCode?: string; // accent
|
|
27
27
|
shortenName: (name: string) => string;
|
|
@@ -33,11 +33,15 @@ interface ChatMessagesProps {
|
|
|
33
33
|
messages: Message[];
|
|
34
34
|
colorCode: string;
|
|
35
35
|
isLoading: boolean;
|
|
36
|
+
|
|
36
37
|
handleAddToCart: (
|
|
37
38
|
variantId: string,
|
|
38
39
|
name: string,
|
|
39
|
-
|
|
40
|
+
variantImage:string,
|
|
41
|
+
qty?: number,
|
|
42
|
+
|
|
40
43
|
) => Promise<void>;
|
|
44
|
+
|
|
41
45
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
42
46
|
sessionId:string;
|
|
43
47
|
isBusiness?: boolean;
|
|
@@ -20,7 +20,7 @@ interface AssistantMessageProps {
|
|
|
20
20
|
message: string;
|
|
21
21
|
colorCode: string;
|
|
22
22
|
isBusiness?: boolean;
|
|
23
|
-
handleAddToCart: (variantId: string, name: string, qty?: number) => Promise<void>;
|
|
23
|
+
handleAddToCart: (variantId: string, name: string,variantImage:string, qty?: number) => Promise<void>;
|
|
24
24
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -105,7 +105,7 @@ const SupportConnect: React.FC<SupportConnectProps> = ({
|
|
|
105
105
|
>
|
|
106
106
|
<div className="flex items-center space-x-4">
|
|
107
107
|
<img
|
|
108
|
-
src={data.userImage}
|
|
108
|
+
src={data.userImage || 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSaC0jamaBcOGTbS86459UezKUs2Wo0lPU_mw&s'}
|
|
109
109
|
alt="User"
|
|
110
110
|
className="w-12 h-12 rounded-full border-2"
|
|
111
111
|
style={{ borderColor: colorCode }}
|
|
@@ -117,7 +117,7 @@ const SupportConnect: React.FC<SupportConnectProps> = ({
|
|
|
117
117
|
</span>
|
|
118
118
|
</p>
|
|
119
119
|
<img
|
|
120
|
-
src={data.companyImage}
|
|
120
|
+
src={data.companyImage || 'https://cdn-icons-png.flaticon.com/512/4812/4812244.png'}
|
|
121
121
|
alt="Company"
|
|
122
122
|
className="w-12 h-12 rounded-full border"
|
|
123
123
|
/>
|
|
@@ -20,7 +20,7 @@ interface AssistantMessageProps {
|
|
|
20
20
|
message: string;
|
|
21
21
|
colorCode: string;
|
|
22
22
|
isBusiness?: boolean;
|
|
23
|
-
handleAddToCart: (variantId: string, name: string, qty?: number) => Promise<void>;
|
|
23
|
+
handleAddToCart: (variantId: string, name: string,variantImage:string, qty?: number) => Promise<void>;
|
|
24
24
|
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState,useCallback } from 'react';
|
|
2
2
|
import { Button, Avatar } from 'antd';
|
|
3
|
-
import { CloseOutlined,ArrowLeftOutlined
|
|
3
|
+
import { CloseOutlined,ArrowLeftOutlined,ShoppingCartOutlined} from '@ant-design/icons';
|
|
4
4
|
import '../../Chatbot.css';
|
|
5
5
|
import { useChatbotMessage } from '../../../hooks/useSessionMessages';
|
|
6
6
|
import axios from 'axios';
|
|
@@ -14,7 +14,7 @@ import type { ChatMessage } from '../../../types/chatbot';
|
|
|
14
14
|
import { getCookie } from '../../../utils/cookies';
|
|
15
15
|
import useChatSocket from '../../../hooks/useWebsocke';
|
|
16
16
|
import Notification from '../Notification';
|
|
17
|
-
import CartWidget from '../Messages/CartWidget';
|
|
17
|
+
// import CartWidget from '../Messages/CartWidget';
|
|
18
18
|
import { useCart } from '../../../hooks/useCart';
|
|
19
19
|
|
|
20
20
|
type MessengerProps = {
|
|
@@ -24,10 +24,11 @@ type MessengerProps = {
|
|
|
24
24
|
colorCode: string;
|
|
25
25
|
newSession: boolean;
|
|
26
26
|
messagesSuggestions: string[];
|
|
27
|
+
setMessagesSuggestions: React.Dispatch<React.SetStateAction<string[]>>;
|
|
27
28
|
disableForm: boolean;
|
|
28
29
|
sessionId:string;
|
|
29
30
|
setOpen: (v: boolean) => void;
|
|
30
|
-
setView:
|
|
31
|
+
setView: React.Dispatch<React.SetStateAction<'sessions' | 'create' | 'messenger' | 'cartView'>>;
|
|
31
32
|
setNewSession: (v: boolean) => void;
|
|
32
33
|
Opened: boolean;
|
|
33
34
|
calendly:string;
|
|
@@ -52,6 +53,7 @@ const Messenger: React.FC<MessengerProps> = ({
|
|
|
52
53
|
userType,
|
|
53
54
|
setNewSession,
|
|
54
55
|
messagesSuggestions,
|
|
56
|
+
setMessagesSuggestions,
|
|
55
57
|
disableForm,
|
|
56
58
|
sessionId,
|
|
57
59
|
setOpen,
|
|
@@ -168,7 +170,10 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
168
170
|
const sendMessageSuggestion = async (message: string): Promise<void> => {
|
|
169
171
|
if (!message.trim()) return;
|
|
170
172
|
|
|
171
|
-
setIsSending(true);
|
|
173
|
+
setIsSending(true);
|
|
174
|
+
|
|
175
|
+
// Remove only the clicked suggestion
|
|
176
|
+
setMessagesSuggestions(prev => prev.filter(suggestion => suggestion !== message));
|
|
172
177
|
|
|
173
178
|
const userMsg: ChatMessage = { role: 'user', content: message };
|
|
174
179
|
setMessages(prev => [...prev, userMsg]);
|
|
@@ -187,10 +192,12 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
187
192
|
} catch (error) {
|
|
188
193
|
console.error("Error sending suggestion:", error);
|
|
189
194
|
} finally {
|
|
190
|
-
setIsSending(false);
|
|
195
|
+
setIsSending(false);
|
|
191
196
|
}
|
|
192
197
|
};
|
|
193
198
|
|
|
199
|
+
|
|
200
|
+
|
|
194
201
|
|
|
195
202
|
|
|
196
203
|
async function handleSendImage (image: File, userMessage:string) {
|
|
@@ -398,7 +405,7 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
398
405
|
}
|
|
399
406
|
};
|
|
400
407
|
|
|
401
|
-
const { items,
|
|
408
|
+
const { items, addToCart, removeFromCart } = useCart(sessionId);
|
|
402
409
|
|
|
403
410
|
|
|
404
411
|
// replace your old handleToCart:
|
|
@@ -407,9 +414,9 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
407
414
|
}
|
|
408
415
|
|
|
409
416
|
const handleToCart = useCallback(
|
|
410
|
-
async (variantId: string, name: string, qty = 1) => {
|
|
417
|
+
async (variantId: string, name: string, variantImage: string, qty = 1) => {
|
|
411
418
|
try {
|
|
412
|
-
await addToCart(variantId, name, qty);
|
|
419
|
+
await addToCart(variantId, name, qty, variantImage);
|
|
413
420
|
|
|
414
421
|
const shortName = shorten(name, 20);
|
|
415
422
|
setNotification(`✅ ${qty}× ${shortName} added to cart`);
|
|
@@ -420,6 +427,7 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
420
427
|
[addToCart]
|
|
421
428
|
);
|
|
422
429
|
|
|
430
|
+
|
|
423
431
|
const handleRemoveFromCart = useCallback(
|
|
424
432
|
async (variantId: string, qty = 1) => {
|
|
425
433
|
await removeFromCart(variantId, qty);
|
|
@@ -452,18 +460,59 @@ const socket = useChatSocket(sessionId, handleSocketData);
|
|
|
452
460
|
<span className="chatbot-title">{agentName || 'Ai Assistant'}</span>
|
|
453
461
|
|
|
454
462
|
{items.length > 0 && (
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
463
|
+
<div
|
|
464
|
+
style={{
|
|
465
|
+
position: "relative",
|
|
466
|
+
display: "flex",
|
|
467
|
+
alignItems: "center",
|
|
468
|
+
justifyContent: "center",
|
|
469
|
+
marginLeft: "auto",
|
|
470
|
+
marginRight: 12,
|
|
471
|
+
}}
|
|
472
|
+
>
|
|
473
|
+
<ShoppingCartOutlined
|
|
474
|
+
onClick={() => setView("cartView")}
|
|
475
|
+
style={{
|
|
476
|
+
fontSize: 26,
|
|
477
|
+
cursor: "pointer",
|
|
478
|
+
color: "#333",
|
|
479
|
+
padding: 6,
|
|
480
|
+
borderRadius: "50%",
|
|
481
|
+
transition: "0.2s",
|
|
482
|
+
}}
|
|
483
|
+
onMouseEnter={(e) => {
|
|
484
|
+
(e.currentTarget as HTMLElement).style.background = "rgba(0,0,0,0.05)";
|
|
485
|
+
}}
|
|
486
|
+
onMouseLeave={(e) => {
|
|
487
|
+
(e.currentTarget as HTMLElement).style.background = "transparent";
|
|
488
|
+
}}
|
|
489
|
+
/>
|
|
490
|
+
|
|
491
|
+
{/* Counter Badge */}
|
|
492
|
+
<span
|
|
493
|
+
style={{
|
|
494
|
+
position: "absolute",
|
|
495
|
+
top: 2,
|
|
496
|
+
right: 2,
|
|
497
|
+
backgroundColor: colorCode || "#7F28F8",
|
|
498
|
+
color: "white",
|
|
499
|
+
fontSize: 10,
|
|
500
|
+
fontWeight: 600,
|
|
501
|
+
width: 18,
|
|
502
|
+
height: 18,
|
|
503
|
+
borderRadius: "50%",
|
|
504
|
+
display: "flex",
|
|
505
|
+
alignItems: "center",
|
|
506
|
+
justifyContent: "center",
|
|
507
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.3)",
|
|
508
|
+
}}
|
|
509
|
+
>
|
|
510
|
+
{items.length}
|
|
511
|
+
</span>
|
|
512
|
+
</div>
|
|
513
|
+
)}
|
|
514
|
+
|
|
515
|
+
|
|
467
516
|
|
|
468
517
|
|
|
469
518
|
{/* Close Button */}
|