shopify-chatbot-widget 0.0.4 → 0.0.7
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 -0
- package/dist/jaweb-chatbot.iife.js +685 -0
- package/dist/vite.svg +1 -0
- package/package.json +1 -1
- package/src/App.css +0 -42
- package/src/App.tsx +0 -13
- package/src/Chatbot/Chatbot.css +0 -154
- package/src/Chatbot/Chatbot.tsx +0 -207
- package/src/Chatbot/components/ChatInput.css +0 -127
- package/src/Chatbot/components/ChatInput.tsx +0 -262
- package/src/Chatbot/components/Messages/Addcart.tsx +0 -70
- package/src/Chatbot/components/Messages/AgentStatus.tsx +0 -37
- package/src/Chatbot/components/Messages/AssistantMesage.tsx +0 -316
- package/src/Chatbot/components/Messages/AudioMessage.tsx +0 -123
- package/src/Chatbot/components/Messages/CartWidget.tsx +0 -116
- package/src/Chatbot/components/Messages/ChatImage.tsx +0 -46
- package/src/Chatbot/components/Messages/ChatImageText.tsx +0 -74
- package/src/Chatbot/components/Messages/ChatImageTextAssis.tsx +0 -72
- package/src/Chatbot/components/Messages/ChatMessages.css +0 -5
- package/src/Chatbot/components/Messages/ChatMessages.tsx +0 -284
- package/src/Chatbot/components/Messages/Support.tsx +0 -177
- package/src/Chatbot/components/Messages/Typing.css +0 -27
- package/src/Chatbot/components/Messages/Typing.tsx +0 -14
- package/src/Chatbot/components/Messages/ZidAssistantMesage.tsx +0 -142
- package/src/Chatbot/components/Messenger/Messenger.tsx +0 -521
- package/src/Chatbot/components/Notification.tsx +0 -27
- package/src/Chatbot/components/Powered.css +0 -16
- package/src/Chatbot/components/Powered.tsx +0 -13
- package/src/Chatbot/components/Sessions/CreateSession.tsx +0 -139
- package/src/Chatbot/components/Sessions/RenderList.tsx +0 -202
- package/src/Chatbot/components/Suggestions.css +0 -34
- package/src/Chatbot/components/Suggestions.tsx +0 -28
- package/src/assets/react.svg +0 -1
- package/src/chatbot-widget/index.tsx +0 -14
- package/src/hooks/config.tsx +0 -13
- package/src/hooks/useCart.tsx +0 -99
- package/src/hooks/useChatbotAPI.tsx +0 -160
- package/src/hooks/useChatbotData.tsx +0 -129
- package/src/hooks/useSessionMessages.tsx +0 -51
- package/src/hooks/useWebsocke.tsx +0 -45
- package/src/i18n.tsx +0 -30
- package/src/index.css +0 -1
- package/src/main.tsx +0 -10
- package/src/types/chatbot.ts +0 -38
- package/src/utils/cookies.tsx +0 -14
- package/src/vite-env.d.ts +0 -1
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Spin } from 'antd';
|
|
3
|
-
import { LoadingOutlined ,CheckCircleOutlined,CloseCircleOutlined} from '@ant-design/icons';
|
|
4
|
-
import Typing from './Typing';
|
|
5
|
-
import AssistantMessage from './AssistantMesage';
|
|
6
|
-
import ChatImage from './ChatImage';
|
|
7
|
-
import './ChatMessages.css'
|
|
8
|
-
import ChatImageText from './ChatImageText';
|
|
9
|
-
import AudioVisualizer from './AudioMessage';
|
|
10
|
-
import AgentStatusBanner from './AgentStatus';
|
|
11
|
-
import AddToCartConfirmation from './Addcart';
|
|
12
|
-
import SupportConnect from './Support';
|
|
13
|
-
import ChatImageTextAssist from './ChatImageTextAssis';
|
|
14
|
-
import ZidAssistantChatMessage from './ZidAssistantMesage';
|
|
15
|
-
interface Message {
|
|
16
|
-
role?: string;
|
|
17
|
-
content?: any;
|
|
18
|
-
text?: string;
|
|
19
|
-
typing?: boolean;
|
|
20
|
-
image_url?:string;
|
|
21
|
-
status?:string;
|
|
22
|
-
audio_url?:string;
|
|
23
|
-
isConnect?:boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface ChatMessagesProps {
|
|
27
|
-
messages: Message[];
|
|
28
|
-
colorCode: string;
|
|
29
|
-
isLoading: boolean;
|
|
30
|
-
handleAddToCart: (
|
|
31
|
-
variantId: string,
|
|
32
|
-
name: string,
|
|
33
|
-
qty?: number
|
|
34
|
-
) => Promise<void>;
|
|
35
|
-
handleRemoveFromCart: (variantId: string, qty?: number) => Promise<void>;
|
|
36
|
-
sessionId:string;
|
|
37
|
-
isBusiness?: boolean;
|
|
38
|
-
agentStatus?: {
|
|
39
|
-
status?: string;
|
|
40
|
-
user?: string;
|
|
41
|
-
};
|
|
42
|
-
userType:string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const spinIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
|
|
49
|
-
|
|
50
|
-
const ChatMessages: React.FC<ChatMessagesProps> = ({
|
|
51
|
-
messages,
|
|
52
|
-
colorCode,
|
|
53
|
-
handleAddToCart,
|
|
54
|
-
handleRemoveFromCart,
|
|
55
|
-
isLoading,
|
|
56
|
-
agentStatus,
|
|
57
|
-
sessionId,
|
|
58
|
-
userType
|
|
59
|
-
|
|
60
|
-
}) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const handleAddToCartZid = () => {
|
|
64
|
-
// Your logic goes here
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (isLoading || !messages) {
|
|
69
|
-
return (
|
|
70
|
-
<div
|
|
71
|
-
className="loader-wrapper"
|
|
72
|
-
style={{
|
|
73
|
-
display: 'flex',
|
|
74
|
-
alignItems: 'center',
|
|
75
|
-
justifyContent: 'center',
|
|
76
|
-
height: '100%',
|
|
77
|
-
}}
|
|
78
|
-
>
|
|
79
|
-
<Spin indicator={spinIcon} />
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<div className="chatbot-messages space-y-4">
|
|
86
|
-
{messages.map((msg, i) => {
|
|
87
|
-
if (msg.typing) {
|
|
88
|
-
return (
|
|
89
|
-
<div key={i} className="chat-msg typing">
|
|
90
|
-
<Typing />
|
|
91
|
-
</div>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (msg.isConnect){
|
|
96
|
-
return(
|
|
97
|
-
<SupportConnect sessionId={sessionId}/>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (msg.role === 'assistant') {
|
|
105
|
-
// Handle array content separately
|
|
106
|
-
if (Array.isArray(msg.content)) {
|
|
107
|
-
return (
|
|
108
|
-
<div key={i} className="chat-msg assistant" style={{ backgroundColor: 'transparent', padding: 0 }}>
|
|
109
|
-
{msg.image_url && (
|
|
110
|
-
<img
|
|
111
|
-
src={msg.image_url}
|
|
112
|
-
alt="Chat image"
|
|
113
|
-
style={{ width: 200 }}
|
|
114
|
-
className="rounded-md mt-2 max-w-xs border border-green-400 shadow-sm"
|
|
115
|
-
/>
|
|
116
|
-
)}
|
|
117
|
-
<ChatImage message={msg.content} />
|
|
118
|
-
<div className="flex justify-start">
|
|
119
|
-
<ChatImageTextAssist message={msg.content} />
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return (
|
|
126
|
-
<div key={i} className="chat-msg assistant">
|
|
127
|
-
{msg.image_url && (
|
|
128
|
-
<img
|
|
129
|
-
src={msg.image_url}
|
|
130
|
-
alt="Chat image"
|
|
131
|
-
style={{ width: 200 }}
|
|
132
|
-
className="rounded-md mt-2 max-w-xs border border-green-400 shadow-sm"
|
|
133
|
-
/>
|
|
134
|
-
)}
|
|
135
|
-
|
|
136
|
-
{userType==="shopify"?(
|
|
137
|
-
<AssistantMessage
|
|
138
|
-
message={msg.content || msg.text || ''}
|
|
139
|
-
colorCode={colorCode}
|
|
140
|
-
handleAddToCart={handleAddToCart}
|
|
141
|
-
handleRemoveFromCart={handleRemoveFromCart}
|
|
142
|
-
/>
|
|
143
|
-
)
|
|
144
|
-
:(
|
|
145
|
-
|
|
146
|
-
<ZidAssistantChatMessage
|
|
147
|
-
message={msg.content || msg.text || ''}
|
|
148
|
-
colorCode={colorCode}
|
|
149
|
-
handleAddToCart={handleAddToCartZid}
|
|
150
|
-
/>
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
</div>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if(msg?.audio_url){
|
|
163
|
-
|
|
164
|
-
return(
|
|
165
|
-
<div
|
|
166
|
-
className={`chat-msg user`}
|
|
167
|
-
style={{
|
|
168
|
-
backgroundColor:'transparent',
|
|
169
|
-
padding:0
|
|
170
|
-
}}
|
|
171
|
-
>
|
|
172
|
-
|
|
173
|
-
<div style={{
|
|
174
|
-
borderRadius:12
|
|
175
|
-
}}>
|
|
176
|
-
|
|
177
|
-
<AudioVisualizer link={msg.audio_url} transcription={msg.content} colorCode={colorCode} />
|
|
178
|
-
|
|
179
|
-
{/* Upload/Delivery Status */}
|
|
180
|
-
|
|
181
|
-
</div>
|
|
182
|
-
|
|
183
|
-
<div className="mt-1 mr-2 flex items-end justify-end">
|
|
184
|
-
{msg.status === 'uploading' ? (
|
|
185
|
-
<Spin indicator={<LoadingOutlined spin />} size="small" />
|
|
186
|
-
) : msg.status === 'delivered' ? (
|
|
187
|
-
<CheckCircleOutlined style={{ color: 'green' }} />
|
|
188
|
-
) : msg.status === 'failed' ? (
|
|
189
|
-
<CloseCircleOutlined style={{ color: 'red' }} />
|
|
190
|
-
) : null}
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// ✅ IMAGE MESSAGE (non-assistant)
|
|
198
|
-
if (msg.image_url) {
|
|
199
|
-
return (
|
|
200
|
-
<div key={i} className="chat-msg user" style={{ backgroundColor: 'transparent' }}>
|
|
201
|
-
<img
|
|
202
|
-
src={msg.image_url}
|
|
203
|
-
alt="Chat image"
|
|
204
|
-
style={{ width: 200 }}
|
|
205
|
-
className={`rounded-md mt-2 max-w-xs ${
|
|
206
|
-
msg.content === 'user'
|
|
207
|
-
? 'border border-gray-300 shadow-sm'
|
|
208
|
-
: 'border border-green-400 shadow-sm'
|
|
209
|
-
}`}
|
|
210
|
-
/>
|
|
211
|
-
|
|
212
|
-
<div className="mt-1 mr-2 flex items-end justify-end">
|
|
213
|
-
{msg.status === 'uploading' ? (
|
|
214
|
-
<Spin indicator={<LoadingOutlined spin />} size="small" />
|
|
215
|
-
) : msg.status === 'delivered' ? (
|
|
216
|
-
<CheckCircleOutlined style={{ color: 'green' }} />
|
|
217
|
-
) : msg.status === 'failed' ? (
|
|
218
|
-
<CloseCircleOutlined style={{ color: 'red' }} />
|
|
219
|
-
) : null}
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
{msg.content && (
|
|
223
|
-
<div className="flex justify-end mt-2">
|
|
224
|
-
<div
|
|
225
|
-
style={{
|
|
226
|
-
color: 'white',
|
|
227
|
-
border: 1,
|
|
228
|
-
backgroundColor: colorCode,
|
|
229
|
-
paddingTop: 10,
|
|
230
|
-
paddingBottom: 10,
|
|
231
|
-
paddingInline: 14,
|
|
232
|
-
maxWidth: '85%',
|
|
233
|
-
lineHeight: 1.4,
|
|
234
|
-
borderRadius: '16px',
|
|
235
|
-
wordBreak: 'break-word',
|
|
236
|
-
}}
|
|
237
|
-
>
|
|
238
|
-
<span>{msg.content}</span>
|
|
239
|
-
</div>
|
|
240
|
-
</div>
|
|
241
|
-
)}
|
|
242
|
-
</div>
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// ✅ MULTIPLE IMAGES (array)
|
|
248
|
-
if (Array.isArray(msg.content)) {
|
|
249
|
-
return (
|
|
250
|
-
<div key={i} className='chat-msg user' style={{backgroundColor:'transparent',padding:0}}>
|
|
251
|
-
<div style={{backgroundColor:'transparent',padding:0}}>
|
|
252
|
-
<ChatImage message={msg.content} />
|
|
253
|
-
</div>
|
|
254
|
-
<div key={i} className='flex justify-end'>
|
|
255
|
-
<ChatImageText message={msg.content} colorCode={colorCode} />
|
|
256
|
-
</div>
|
|
257
|
-
|
|
258
|
-
</div>
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return (
|
|
264
|
-
<div
|
|
265
|
-
key={i}
|
|
266
|
-
className={`chat-msg ${msg.role}`}
|
|
267
|
-
style={
|
|
268
|
-
msg.role === 'user'
|
|
269
|
-
? { backgroundColor: colorCode, color: '#fff' }
|
|
270
|
-
: {}
|
|
271
|
-
}
|
|
272
|
-
>
|
|
273
|
-
|
|
274
|
-
<AddToCartConfirmation message={msg.content} colorCode={colorCode}/>
|
|
275
|
-
|
|
276
|
-
</div>
|
|
277
|
-
);
|
|
278
|
-
})}
|
|
279
|
-
<AgentStatusBanner status={agentStatus?.status} user={agentStatus?.user} />
|
|
280
|
-
</div>
|
|
281
|
-
);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
export default ChatMessages;
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
-
import { Mail } from 'lucide-react';
|
|
4
|
-
import config from '../../../hooks/config';
|
|
5
|
-
|
|
6
|
-
interface SupportConnectProps {
|
|
7
|
-
sessionId?: string;
|
|
8
|
-
colorCode?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type Stage = 'email_sent' | 'connecting' | 'auto';
|
|
12
|
-
|
|
13
|
-
interface SupportData {
|
|
14
|
-
userImage?: string;
|
|
15
|
-
agentName?: string;
|
|
16
|
-
companyImage?: string;
|
|
17
|
-
agentAvatar?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const SupportConnect: React.FC<SupportConnectProps> = ({
|
|
21
|
-
sessionId,
|
|
22
|
-
colorCode = '#000',
|
|
23
|
-
}) => {
|
|
24
|
-
const [stage, setStage] = useState<Stage | null>(null);
|
|
25
|
-
const [data, setData] = useState<SupportData>({});
|
|
26
|
-
const [progress, setProgress] = useState<number>(0);
|
|
27
|
-
const dots = [0, 1, 2];
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
if (!sessionId) return;
|
|
31
|
-
const ws = new WebSocket(
|
|
32
|
-
`${config.websocketUrl}/support/${sessionId}/`
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
ws.onmessage = ({ data: text }) => {
|
|
36
|
-
const msg = JSON.parse(text);
|
|
37
|
-
setStage(msg.stage as Stage);
|
|
38
|
-
setData(msg.data || {});
|
|
39
|
-
};
|
|
40
|
-
ws.onclose = () => console.log('Support socket closed');
|
|
41
|
-
return () => ws.close();
|
|
42
|
-
}, [sessionId]);
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
let timerId: number;
|
|
46
|
-
let start: number;
|
|
47
|
-
|
|
48
|
-
if (stage === 'connecting') {
|
|
49
|
-
start = Date.now();
|
|
50
|
-
setProgress(100);
|
|
51
|
-
|
|
52
|
-
timerId = window.setInterval(() => {
|
|
53
|
-
const elapsed = (Date.now() - start) / 1000; // seconds
|
|
54
|
-
const pct = Math.max(100 - (elapsed / 7) * 100, 0);
|
|
55
|
-
setProgress(pct);
|
|
56
|
-
if (pct <= 0) window.clearInterval(timerId);
|
|
57
|
-
}, 100);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return () => {
|
|
61
|
-
if (timerId) window.clearInterval(timerId);
|
|
62
|
-
};
|
|
63
|
-
}, [stage]);
|
|
64
|
-
|
|
65
|
-
if (!stage) return null;
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<div className="bg-opacity-50 flex items-center justify-center z-50">
|
|
69
|
-
<AnimatePresence>
|
|
70
|
-
{stage === 'email_sent' && (
|
|
71
|
-
<motion.div
|
|
72
|
-
key="email"
|
|
73
|
-
className="bg-white rounded-2xl shadow-xl p-6 flex flex-col items-center"
|
|
74
|
-
initial={{ scale: 0.9, opacity: 0 }}
|
|
75
|
-
animate={{ scale: 1, opacity: 1 }}
|
|
76
|
-
exit={{ scale: 0.8, opacity: 0 }}
|
|
77
|
-
>
|
|
78
|
-
<Mail size={32} className="mb-2" style={{ color: colorCode }} />
|
|
79
|
-
<p className="text-lg font-semibold">
|
|
80
|
-
Sending your request to support…
|
|
81
|
-
</p>
|
|
82
|
-
<div className="flex space-x-2 mt-2">
|
|
83
|
-
{dots.map((_, i) => (
|
|
84
|
-
<motion.span
|
|
85
|
-
key={i}
|
|
86
|
-
className="text-2xl"
|
|
87
|
-
style={{ color: colorCode }}
|
|
88
|
-
animate={{ scale: [0.7, 1.3, 0.7] }}
|
|
89
|
-
transition={{ delay: i * 0.2, repeat: Infinity, duration: 0.8 }}
|
|
90
|
-
>
|
|
91
|
-
•
|
|
92
|
-
</motion.span>
|
|
93
|
-
))}
|
|
94
|
-
</div>
|
|
95
|
-
</motion.div>
|
|
96
|
-
)}
|
|
97
|
-
|
|
98
|
-
{stage === 'connecting' && (
|
|
99
|
-
<motion.div
|
|
100
|
-
key="connecting"
|
|
101
|
-
className="bg-white rounded-2xl shadow-xl p-6 flex flex-col items-center"
|
|
102
|
-
initial={{ scale: 0.9, opacity: 0 }}
|
|
103
|
-
animate={{ scale: 1, opacity: 1 }}
|
|
104
|
-
exit={{ scale: 0.8, opacity: 0 }}
|
|
105
|
-
>
|
|
106
|
-
<div className="flex items-center space-x-4">
|
|
107
|
-
<img
|
|
108
|
-
src={data.userImage}
|
|
109
|
-
alt="User"
|
|
110
|
-
className="w-12 h-12 rounded-full border-2"
|
|
111
|
-
style={{ borderColor: colorCode }}
|
|
112
|
-
/>
|
|
113
|
-
<p className="text-lg">
|
|
114
|
-
Connecting you to{' '}
|
|
115
|
-
<span className="text-indigo-600">
|
|
116
|
-
{data.agentName || 'Support Agent'}
|
|
117
|
-
</span>
|
|
118
|
-
</p>
|
|
119
|
-
<img
|
|
120
|
-
src={data.companyImage}
|
|
121
|
-
alt="Company"
|
|
122
|
-
className="w-12 h-12 rounded-full border"
|
|
123
|
-
/>
|
|
124
|
-
</div>
|
|
125
|
-
<div className="flex space-x-2 mt-3">
|
|
126
|
-
{dots.map((_, i) => (
|
|
127
|
-
<motion.span
|
|
128
|
-
key={i}
|
|
129
|
-
className="text-2xl"
|
|
130
|
-
style={{ color: colorCode }}
|
|
131
|
-
animate={{ scale: [0.7, 1.3, 0.7] }}
|
|
132
|
-
transition={{ delay: i * 0.2, repeat: Infinity, duration: 0.8 }}
|
|
133
|
-
>
|
|
134
|
-
•
|
|
135
|
-
</motion.span>
|
|
136
|
-
))}
|
|
137
|
-
</div>
|
|
138
|
-
<div className="w-full bg-gray-200 h-1 rounded-full mt-4 overflow-hidden">
|
|
139
|
-
<motion.div
|
|
140
|
-
className="h-full"
|
|
141
|
-
style={{ width: `${progress}%`, backgroundColor: colorCode }}
|
|
142
|
-
transition={{ ease: 'linear', duration: 1 }}
|
|
143
|
-
/>
|
|
144
|
-
</div>
|
|
145
|
-
</motion.div>
|
|
146
|
-
)}
|
|
147
|
-
|
|
148
|
-
{stage === 'auto' && (
|
|
149
|
-
<motion.div
|
|
150
|
-
key="auto"
|
|
151
|
-
className="bg-white rounded-2xl shadow-xl p-6 flex items-center space-x-4"
|
|
152
|
-
initial={{ y: 50, opacity: 0 }}
|
|
153
|
-
animate={{ y: 0, opacity: 1 }}
|
|
154
|
-
exit={{ y: -50, opacity: 0 }}
|
|
155
|
-
>
|
|
156
|
-
<img
|
|
157
|
-
src={
|
|
158
|
-
data.agentAvatar ||
|
|
159
|
-
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSaC0jamaBcOGTbS86459UezKUs2Wo0lPU_mw&s'
|
|
160
|
-
}
|
|
161
|
-
alt="Auto Support"
|
|
162
|
-
className="w-12 h-12 rounded-2xl"
|
|
163
|
-
/>
|
|
164
|
-
<p className="text-lg">
|
|
165
|
-
You’re now connected to{' '}
|
|
166
|
-
<span style={{ color: colorCode }}>
|
|
167
|
-
{data.agentName || 'Auto-Support'}
|
|
168
|
-
</span>
|
|
169
|
-
</p>
|
|
170
|
-
</motion.div>
|
|
171
|
-
)}
|
|
172
|
-
</AnimatePresence>
|
|
173
|
-
</div>
|
|
174
|
-
);
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
export default SupportConnect;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
.typing-animation span {
|
|
2
|
-
display: inline-block;
|
|
3
|
-
animation: blink 1.4s infinite;
|
|
4
|
-
font-size: 20px;
|
|
5
|
-
margin: 0 2px;
|
|
6
|
-
color: #555;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.typing-animation span:nth-child(2) {
|
|
10
|
-
animation-delay: 0.2s;
|
|
11
|
-
}
|
|
12
|
-
.typing-animation span:nth-child(3) {
|
|
13
|
-
animation-delay: 0.4s;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
@keyframes blink {
|
|
17
|
-
0% {
|
|
18
|
-
opacity: 0.2;
|
|
19
|
-
}
|
|
20
|
-
20% {
|
|
21
|
-
opacity: 1;
|
|
22
|
-
}
|
|
23
|
-
100% {
|
|
24
|
-
opacity: 0.2;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import './Typing.css'; // make sure this file contains the animation
|
|
3
|
-
|
|
4
|
-
const Typing: React.FC = () => {
|
|
5
|
-
return (
|
|
6
|
-
<div className="typing-animation">
|
|
7
|
-
<span>.</span>
|
|
8
|
-
<span>.</span>
|
|
9
|
-
<span>.</span>
|
|
10
|
-
</div>
|
|
11
|
-
);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export default Typing;
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ShoppingCartOutlined } from '@ant-design/icons';
|
|
3
|
-
|
|
4
|
-
interface ChatMessageProps {
|
|
5
|
-
message: string;
|
|
6
|
-
colorCode: string;
|
|
7
|
-
isBusiness?: boolean;
|
|
8
|
-
handleAddToCart: (info: string) => void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface ProductDetails {
|
|
12
|
-
name: string;
|
|
13
|
-
price: string;
|
|
14
|
-
link: string;
|
|
15
|
-
image: string;
|
|
16
|
-
variant_id: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const ZidAssistantChatMessage: React.FC<ChatMessageProps> = ({
|
|
20
|
-
message,
|
|
21
|
-
colorCode,
|
|
22
|
-
handleAddToCart,
|
|
23
|
-
}) => {
|
|
24
|
-
if (!message) return null;
|
|
25
|
-
|
|
26
|
-
// 1. Rejoin the message and split into logical product blocks
|
|
27
|
-
const combinedMessage = message.split('|||').join('\n');
|
|
28
|
-
const segments = combinedMessage
|
|
29
|
-
.split(/(?=Name\s*[:\-])/g)
|
|
30
|
-
.map((seg) => seg.trim())
|
|
31
|
-
.filter(Boolean);
|
|
32
|
-
|
|
33
|
-
// 2. Extract product details
|
|
34
|
-
const extractProductDetails = (text: string): ProductDetails | null => {
|
|
35
|
-
const normalized = text.trim();
|
|
36
|
-
|
|
37
|
-
if (!normalized.startsWith('Name:')) return null;
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
!normalized.includes('Price:') ||
|
|
41
|
-
!normalized.includes('Link:') ||
|
|
42
|
-
(!normalized.includes('img -') && !normalized.includes('img:'))
|
|
43
|
-
) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const nameMatch = normalized.match(/Name\s*[:\-]\s*(.+)/i);
|
|
48
|
-
const priceMatch = normalized.match(/Price\s*[:\-]\s*(.+)/i);
|
|
49
|
-
const linkMatch = normalized.match(/Link\s*[:\-]\s*(https?:\/\/[^\s]+)/i);
|
|
50
|
-
const imgMatch = normalized.match(/img\s*[:\-]\s*(https?:\/\/[^\s]+)/i);
|
|
51
|
-
const variantMatch = normalized.match(/VariantId\s*[:\-]\s*(.+)/i);
|
|
52
|
-
|
|
53
|
-
if (!nameMatch || !priceMatch || !linkMatch || !imgMatch) return null;
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
name: nameMatch[1].trim(),
|
|
57
|
-
price: priceMatch[1].trim(),
|
|
58
|
-
link: linkMatch[1].trim(),
|
|
59
|
-
image: imgMatch[1].trim(),
|
|
60
|
-
variant_id: variantMatch?.[1]?.trim() || '',
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// 3. Linkify plain text
|
|
65
|
-
const formatTextWithLinks = (text: string): React.ReactNode[] => {
|
|
66
|
-
const urlRegex = /(https?:\/\/[^\s]+)/g;
|
|
67
|
-
return text?.split(urlRegex).map((part, i) =>
|
|
68
|
-
urlRegex.test(part) ? (
|
|
69
|
-
<a
|
|
70
|
-
key={i}
|
|
71
|
-
href={part.trim()}
|
|
72
|
-
style={{ color: colorCode }}
|
|
73
|
-
target="_blank"
|
|
74
|
-
rel="noopener noreferrer"
|
|
75
|
-
className="underline break-words"
|
|
76
|
-
>
|
|
77
|
-
{part}
|
|
78
|
-
</a>
|
|
79
|
-
) : (
|
|
80
|
-
<span key={i}>{part}</span>
|
|
81
|
-
)
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// 4. Render UI
|
|
86
|
-
return (
|
|
87
|
-
<div className="space-y-4">
|
|
88
|
-
{segments.map((part, idx) => {
|
|
89
|
-
const product = extractProductDetails(part);
|
|
90
|
-
|
|
91
|
-
if (product) {
|
|
92
|
-
return (
|
|
93
|
-
<div
|
|
94
|
-
key={idx}
|
|
95
|
-
className="flex items-center bg-white shadow-lg rounded-2xl p-4 space-x-4"
|
|
96
|
-
>
|
|
97
|
-
<div className="w-32 h-32 sm:w-40 sm:h-40 relative">
|
|
98
|
-
<img
|
|
99
|
-
src={product.image}
|
|
100
|
-
alt={product.name}
|
|
101
|
-
className="w-full h-full object-cover rounded-xl cursor-pointer"
|
|
102
|
-
onClick={() => window.open(product.link, '_blank')}
|
|
103
|
-
/>
|
|
104
|
-
</div>
|
|
105
|
-
<div className="flex-1">
|
|
106
|
-
<a
|
|
107
|
-
href={product.link}
|
|
108
|
-
target="_blank"
|
|
109
|
-
rel="noopener noreferrer"
|
|
110
|
-
className="text-lg font-semibold text-gray-900"
|
|
111
|
-
>
|
|
112
|
-
{product.name}
|
|
113
|
-
</a>
|
|
114
|
-
<p className="text-md font-medium text-gray-700">{product.price}</p>
|
|
115
|
-
<button
|
|
116
|
-
className="mt-3 flex items-center gap-2 px-4 py-2 rounded-lg font-semibold transition hover:opacity-90"
|
|
117
|
-
style={{ backgroundColor: colorCode, color: '#fff' }}
|
|
118
|
-
onClick={() =>
|
|
119
|
-
handleAddToCart(
|
|
120
|
-
`name:${product.name},variantId:${product.variant_id},Link:${product.link}`
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
>
|
|
124
|
-
<ShoppingCartOutlined />
|
|
125
|
-
<span>+</span>
|
|
126
|
-
</button>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<p key={idx} className="whitespace-pre-wrap break-words">
|
|
134
|
-
{formatTextWithLinks(part)}
|
|
135
|
-
</p>
|
|
136
|
-
);
|
|
137
|
-
})}
|
|
138
|
-
</div>
|
|
139
|
-
);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
export default ZidAssistantChatMessage;
|