shopify-chatbot-widget 0.8.6 β 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 +36 -3
- 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
|
@@ -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 */}
|
|
@@ -46,13 +46,13 @@ const ChatSessions: React.FC<ChatSessionsProps> = ({ chatbotLogo, colorCode, onN
|
|
|
46
46
|
(now.getTime() - createdDate.getTime()) / (1000 * 60 * 60 * 24)
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
const
|
|
49
|
+
const isOlderThan5Days = diffDays >= 5;
|
|
50
50
|
|
|
51
51
|
return {
|
|
52
52
|
id: item.user_session_id,
|
|
53
53
|
agentName: item.assignee || 'AI Agent',
|
|
54
54
|
createdAt: item.date,
|
|
55
|
-
isBlurred: item.closed ||
|
|
55
|
+
isBlurred: item.closed || isOlderThan5Days, // β
auto-close if 3+ days old
|
|
56
56
|
agentImage: item.assignee_picture,
|
|
57
57
|
};
|
|
58
58
|
});
|
package/src/hooks/useCart.tsx
CHANGED
|
@@ -5,12 +5,14 @@ export interface CartItem {
|
|
|
5
5
|
variantId: string;
|
|
6
6
|
name: string;
|
|
7
7
|
quantity: number;
|
|
8
|
+
variantImage: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export function useCart(sessionId: string) {
|
|
11
12
|
const [items, setItems] = useState<CartItem[]>([]);
|
|
12
13
|
const [loading, setLoading] = useState(false);
|
|
13
14
|
|
|
15
|
+
// β
Fetch current cart
|
|
14
16
|
const fetchCart = useCallback(async () => {
|
|
15
17
|
if (!sessionId) return;
|
|
16
18
|
|
|
@@ -26,61 +28,66 @@ export function useCart(sessionId: string) {
|
|
|
26
28
|
}
|
|
27
29
|
}, [sessionId]);
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
// β
Add to Cart (includes image)
|
|
32
|
+
const addToCart = async (
|
|
33
|
+
variantId: string,
|
|
34
|
+
name: string,
|
|
35
|
+
qty: number,
|
|
36
|
+
variantImage: string
|
|
37
|
+
) => {
|
|
30
38
|
if (!sessionId) return;
|
|
31
|
-
|
|
32
|
-
// optimistic update
|
|
33
39
|
|
|
34
|
-
|
|
35
40
|
try {
|
|
36
41
|
const res = await fetch(`${config.apiUrl}cart/add/`, {
|
|
37
|
-
method:
|
|
38
|
-
headers: {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
44
|
body: JSON.stringify({
|
|
40
45
|
session_id: sessionId,
|
|
41
46
|
variantId,
|
|
42
47
|
name,
|
|
43
48
|
quantity: qty,
|
|
49
|
+
variantImage, // β
include image
|
|
44
50
|
}),
|
|
45
51
|
});
|
|
46
|
-
|
|
52
|
+
|
|
47
53
|
const json = await res.json();
|
|
48
54
|
if (!res.ok) {
|
|
49
|
-
throw new Error(json.error ||
|
|
50
|
-
}
|
|
51
|
-
else{
|
|
52
|
-
setItems(current => {
|
|
53
|
-
const found = current.find(i => i.variantId === variantId);
|
|
54
|
-
if (found) {
|
|
55
|
-
return current.map(i =>
|
|
56
|
-
i.variantId === variantId
|
|
57
|
-
? { ...i, quantity: i.quantity + qty }
|
|
58
|
-
: i
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
return [...current, { variantId, name, quantity: qty }];
|
|
62
|
-
});
|
|
55
|
+
throw new Error(json.error || 'Failed to add to cart');
|
|
63
56
|
}
|
|
57
|
+
|
|
58
|
+
// β
Optimistic update
|
|
59
|
+
setItems((current) => {
|
|
60
|
+
const found = current.find((i) => i.variantId === variantId);
|
|
61
|
+
if (found) {
|
|
62
|
+
return current.map((i) =>
|
|
63
|
+
i.variantId === variantId
|
|
64
|
+
? { ...i, quantity: i.quantity + qty }
|
|
65
|
+
: i
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return [...current, { variantId, name, quantity: qty, variantImage }];
|
|
69
|
+
});
|
|
64
70
|
} catch (err: any) {
|
|
71
|
+
console.error('Error adding to cart:', err);
|
|
65
72
|
throw err;
|
|
66
73
|
}
|
|
67
|
-
|
|
68
|
-
await fetchCart(); // reconcile
|
|
74
|
+
|
|
75
|
+
await fetchCart(); // β
reconcile with server
|
|
69
76
|
};
|
|
70
|
-
|
|
71
77
|
|
|
78
|
+
// β
Remove from Cart
|
|
72
79
|
const removeFromCart = async (variantId: string, qty: number) => {
|
|
73
80
|
if (!sessionId) return;
|
|
74
81
|
|
|
75
82
|
// Optimistic update
|
|
76
|
-
setItems(current =>
|
|
83
|
+
setItems((current) =>
|
|
77
84
|
current
|
|
78
|
-
.map(i =>
|
|
85
|
+
.map((i) =>
|
|
79
86
|
i.variantId === variantId
|
|
80
87
|
? { ...i, quantity: i.quantity - qty }
|
|
81
88
|
: i
|
|
82
89
|
)
|
|
83
|
-
.filter(i => i.quantity > 0)
|
|
90
|
+
.filter((i) => i.quantity > 0)
|
|
84
91
|
);
|
|
85
92
|
|
|
86
93
|
try {
|
|
@@ -94,11 +101,13 @@ export function useCart(sessionId: string) {
|
|
|
94
101
|
}),
|
|
95
102
|
});
|
|
96
103
|
} catch (err) {
|
|
104
|
+
console.error('Error removing from cart:', err);
|
|
97
105
|
}
|
|
98
106
|
|
|
99
|
-
await fetchCart(); // reconcile server
|
|
107
|
+
await fetchCart(); // β
reconcile with server
|
|
100
108
|
};
|
|
101
109
|
|
|
110
|
+
// β
Fetch cart on mount
|
|
102
111
|
useEffect(() => {
|
|
103
112
|
fetchCart();
|
|
104
113
|
}, [fetchCart]);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import config from "./config";
|
|
2
|
+
|
|
3
|
+
interface CheckoutResponse {
|
|
4
|
+
checkoutUrl: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const useCartAPI = () => {
|
|
8
|
+
const checkOut = async (sessionId: string): Promise<void> => {
|
|
9
|
+
try {
|
|
10
|
+
const companyUsername = localStorage.getItem('company_username');
|
|
11
|
+
if (!companyUsername) throw new Error('No company username found.');
|
|
12
|
+
|
|
13
|
+
const response = await fetch(`${config.apiUrl}checkout/shopify`, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: { 'Content-Type': 'application/json' },
|
|
16
|
+
body: JSON.stringify({ sessionId, company_username: companyUsername }),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!response.ok) throw new Error('Failed to initiate checkout.');
|
|
20
|
+
|
|
21
|
+
const data: CheckoutResponse = await response.json();
|
|
22
|
+
|
|
23
|
+
if (data?.checkoutUrl) {
|
|
24
|
+
// Open in parent frame for embedded chatbots
|
|
25
|
+
window.parent?.postMessage(
|
|
26
|
+
{
|
|
27
|
+
type: 'JAWEB_CHAT',
|
|
28
|
+
action: 'navigate',
|
|
29
|
+
target: '_blank',
|
|
30
|
+
url: data.checkoutUrl,
|
|
31
|
+
},
|
|
32
|
+
'*'
|
|
33
|
+
);
|
|
34
|
+
} else {
|
|
35
|
+
alert('No checkout URL returned.');
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error('Checkout error:', err);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
checkOut,
|
|
44
|
+
};
|
|
45
|
+
};
|
package/src/Chatbot/OLDCHAT.tsx
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import React, { useState,useEffect } from 'react';
|
|
2
|
-
import { Button } from 'antd';
|
|
3
|
-
|
|
4
|
-
import { useChatbotData } from '../hooks/useChatbotData';
|
|
5
|
-
import ChatSessions from './components/Sessions/RenderList';
|
|
6
|
-
import CreateSession from './components/Sessions/CreateSession';
|
|
7
|
-
import Messenger from './components/Messenger/Messenger';
|
|
8
|
-
import './Chatbot.css'
|
|
9
|
-
import config from '../hooks/config';
|
|
10
|
-
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
|
11
|
-
|
|
12
|
-
const Chatbot: React.FC = () => {
|
|
13
|
-
const [open, setOpen] = useState(false);
|
|
14
|
-
const [viewMode, setViewMode] = useState<'sessions' | 'create' | 'messenger'>('sessions');
|
|
15
|
-
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
16
|
-
const [HasInfo,setHasInfo]=useState(false)
|
|
17
|
-
const [activeAgentName,setActiveAgentName]=useState('')
|
|
18
|
-
const [activeAgentImage,setActiveAgentImage]=useState('')
|
|
19
|
-
const [newSession,setNewSession]=useState(false)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const windowWidth = window.innerWidth;
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
chatbotLogo,
|
|
26
|
-
colorCode,
|
|
27
|
-
subscription,
|
|
28
|
-
messagesSuggestions,
|
|
29
|
-
isFetchingDetails,
|
|
30
|
-
disableForm,
|
|
31
|
-
merchantPhone,
|
|
32
|
-
userType,
|
|
33
|
-
calendly,
|
|
34
|
-
disbleCheckout
|
|
35
|
-
} = useChatbotData();
|
|
36
|
-
|
|
37
|
-
const [visitorData, setVisitorData] = useState<{ visitorId: string; country: string } | null>(null);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const handleNewSessionClick = () => {
|
|
41
|
-
// If the form is *not* disabled, always go to create
|
|
42
|
-
if (!disableForm && !HasInfo) {
|
|
43
|
-
setViewMode('create');
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
else {
|
|
48
|
-
const newSessionId = crypto.randomUUID();
|
|
49
|
-
setActiveSessionId(newSessionId);
|
|
50
|
-
setActiveAgentImage(chatbotLogo);
|
|
51
|
-
setActiveAgentName('Ai Agent');
|
|
52
|
-
setNewSession(true);
|
|
53
|
-
setViewMode('messenger');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Otherwise (disableForm true && !HasInfo) we simply bail.
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
const fetchVisitorInfo = async () => {
|
|
61
|
-
try {
|
|
62
|
-
let visitorId = localStorage.getItem('visitor_id');
|
|
63
|
-
let ip = localStorage.getItem('ip_address');
|
|
64
|
-
let country = localStorage.getItem('country');
|
|
65
|
-
|
|
66
|
-
// β
If all cached, safely set and exit
|
|
67
|
-
// if (visitorId && ip && country) {
|
|
68
|
-
// setVisitorData({ visitorId, country });
|
|
69
|
-
|
|
70
|
-
// return;
|
|
71
|
-
// }
|
|
72
|
-
|
|
73
|
-
// π Generate fingerprint if needed
|
|
74
|
-
if (!visitorId) {
|
|
75
|
-
const fp = await FingerprintJS.load();
|
|
76
|
-
const result = await fp.get();
|
|
77
|
-
visitorId = result.visitorId;
|
|
78
|
-
localStorage.setItem('visitor_id', visitorId);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// π Fetch IP info if missing
|
|
82
|
-
if (!ip || !country) {
|
|
83
|
-
const res = await fetch('https://ipapi.co/json');
|
|
84
|
-
const data = await res.json();
|
|
85
|
-
ip = data.ip;
|
|
86
|
-
country = data.country_name;
|
|
87
|
-
|
|
88
|
-
if (ip) localStorage.setItem('ip_address', ip);
|
|
89
|
-
if (country) localStorage.setItem('country', country);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// β
Ensure values are non-null before use
|
|
93
|
-
if (visitorId && ip && country) {
|
|
94
|
-
setVisitorData({ visitorId, country });
|
|
95
|
-
|
|
96
|
-
// π‘ Send to backend
|
|
97
|
-
const backendRes = await fetch(`${config.apiUrl}register-ip/`, {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: {
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
},
|
|
102
|
-
body: JSON.stringify({
|
|
103
|
-
ip_address: ip,
|
|
104
|
-
country,
|
|
105
|
-
visitor_id: visitorId,
|
|
106
|
-
}),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const result = await backendRes.json();
|
|
110
|
-
setHasInfo(result.has_user_info || false);
|
|
111
|
-
} else {
|
|
112
|
-
console.warn('Missing IP or country info after fetch');
|
|
113
|
-
}
|
|
114
|
-
} catch (err) {
|
|
115
|
-
console.error('Failed to fetch or send visitor info', err);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
fetchVisitorInfo();
|
|
120
|
-
}, []);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const handleSessionSelect = (sessionId: string,agentName:string,agentImage:string) => {
|
|
127
|
-
setActiveSessionId(sessionId);
|
|
128
|
-
setActiveAgentName(agentName)
|
|
129
|
-
setActiveAgentImage(agentImage)
|
|
130
|
-
setNewSession(false)
|
|
131
|
-
setViewMode('messenger');
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
const onMessage = (e: MessageEvent) => {
|
|
137
|
-
const msg = (e.data || {}) as any;
|
|
138
|
-
if (msg.type !== 'JAWEB_CHAT') return;
|
|
139
|
-
|
|
140
|
-
if (msg.action === 'open-ready') {
|
|
141
|
-
// Parent has expanded iframe β now we can render full UI
|
|
142
|
-
setOpen(true);
|
|
143
|
-
} else if (msg.action === 'closed') {
|
|
144
|
-
// Parent collapsed iframe β hide full UI
|
|
145
|
-
setOpen(false);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
window.addEventListener('message', onMessage);
|
|
149
|
-
return () => window.removeEventListener('message', onMessage);
|
|
150
|
-
}, []);
|
|
151
|
-
|
|
152
|
-
const requestOpen = () => {
|
|
153
|
-
// Ask parent to expand iframe
|
|
154
|
-
window.parent?.postMessage({ type: 'JAWEB_CHAT', action: 'open' }, '*');
|
|
155
|
-
};
|
|
156
|
-
const requestClose = () => {
|
|
157
|
-
// Ask parent to collapse iframe
|
|
158
|
-
window.parent?.postMessage({ type: 'JAWEB_CHAT', action: 'close' }, '*');
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<div>
|
|
166
|
-
{!isFetchingDetails && subscription ? (
|
|
167
|
-
<div
|
|
168
|
-
style={{
|
|
169
|
-
bottom: windowWidth < 768 ? '20%' : 0,
|
|
170
|
-
top: windowWidth < 768 ? '0' : '5%',
|
|
171
|
-
height: windowWidth < 768 ? '100%' : '85%',
|
|
172
|
-
borderRadius: 10,
|
|
173
|
-
width: '100%',
|
|
174
|
-
zIndex: 9999,
|
|
175
|
-
}}
|
|
176
|
-
>
|
|
177
|
-
{open && (
|
|
178
|
-
<div className="chatbot-box">
|
|
179
|
-
{viewMode === 'sessions' && (
|
|
180
|
-
<ChatSessions
|
|
181
|
-
chatbotLogo={chatbotLogo}
|
|
182
|
-
Phone={merchantPhone || ''}
|
|
183
|
-
colorCode={colorCode}
|
|
184
|
-
visitorId={visitorData?.visitorId || ''}
|
|
185
|
-
Opened={open}
|
|
186
|
-
// when a child wants to close, talk to parent
|
|
187
|
-
setOpen={(v: boolean) => (v ? requestOpen() : requestClose())}
|
|
188
|
-
onNewSession={handleNewSessionClick}
|
|
189
|
-
onSessionSelect={handleSessionSelect}
|
|
190
|
-
/>
|
|
191
|
-
)}
|
|
192
|
-
|
|
193
|
-
{viewMode === 'create' && (
|
|
194
|
-
<CreateSession
|
|
195
|
-
colorCode={colorCode}
|
|
196
|
-
visitorId={visitorData?.visitorId || ''}
|
|
197
|
-
country={visitorData?.country || ''}
|
|
198
|
-
onBack={() => setViewMode('sessions')}
|
|
199
|
-
onCreate={(userData) => {
|
|
200
|
-
setActiveSessionId(userData.session_id);
|
|
201
|
-
setHasInfo(userData.hasInfo);
|
|
202
|
-
setViewMode('messenger');
|
|
203
|
-
}}
|
|
204
|
-
/>
|
|
205
|
-
)}
|
|
206
|
-
|
|
207
|
-
{viewMode === 'messenger' && activeSessionId && (
|
|
208
|
-
<Messenger
|
|
209
|
-
sessionId={activeSessionId}
|
|
210
|
-
chatbotLogo={activeAgentImage || chatbotLogo}
|
|
211
|
-
agentName={activeAgentName || 'Ai Assistant'}
|
|
212
|
-
colorCode={colorCode}
|
|
213
|
-
messagesSuggestions={messagesSuggestions}
|
|
214
|
-
newSession={newSession}
|
|
215
|
-
disableForm={disableForm}
|
|
216
|
-
calendly={calendly ?? ''}
|
|
217
|
-
// children call this to close/open β routed to parent
|
|
218
|
-
setOpen={(v: boolean) => (v ? requestOpen() : requestClose())}
|
|
219
|
-
setNewSession={setNewSession}
|
|
220
|
-
Opened={open}
|
|
221
|
-
setView={() => setViewMode('sessions')}
|
|
222
|
-
userType={userType}
|
|
223
|
-
disbleCheckout={disbleCheckout}
|
|
224
|
-
/>
|
|
225
|
-
)}
|
|
226
|
-
</div>
|
|
227
|
-
)}
|
|
228
|
-
</div>
|
|
229
|
-
) : null}
|
|
230
|
-
|
|
231
|
-
<div className="jaweb-chatbot-container">
|
|
232
|
-
{!isFetchingDetails && subscription ? (
|
|
233
|
-
<Button
|
|
234
|
-
type="primary"
|
|
235
|
-
shape="circle"
|
|
236
|
-
icon={
|
|
237
|
-
<img
|
|
238
|
-
src="https://jaweb.me/wp-content/uploads/2025/03/download__2_-removebg-preview.png"
|
|
239
|
-
alt="Chat Icon"
|
|
240
|
-
style={{ width: 30, height: 30 }}
|
|
241
|
-
/>
|
|
242
|
-
}
|
|
243
|
-
size="large"
|
|
244
|
-
style={{ backgroundColor: colorCode, width: 60, height: 60 }}
|
|
245
|
-
className={`chat-launcher ${open ? 'chatbot-open' : ''}`}
|
|
246
|
-
// π Donβt setOpen directly; request parent resize
|
|
247
|
-
onClick={() => (open ? requestClose() : requestOpen())}
|
|
248
|
-
/>
|
|
249
|
-
) : null}
|
|
250
|
-
</div>
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
export default Chatbot;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|