@xcelsior/ui-chat 2.0.4 → 2.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/index.d.mts +58 -5
- package/dist/index.d.ts +58 -5
- package/dist/index.js +1042 -427
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1009 -397
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/components/BookingCancelledCard.tsx +103 -0
- package/src/components/BookingCards.stories.tsx +102 -0
- package/src/components/BookingConfirmationCard.tsx +170 -0
- package/src/components/BookingSlotPicker.stories.tsx +87 -0
- package/src/components/BookingSlotPicker.tsx +253 -0
- package/src/components/BrandIcons.stories.tsx +32 -1
- package/src/components/BrandIcons.tsx +21 -17
- package/src/components/Chat.tsx +43 -9
- package/src/components/ChatWidget.tsx +30 -2
- package/src/components/MessageItem.tsx +83 -72
- package/src/components/MessageList.tsx +4 -0
- package/src/hooks/useDraggablePosition.ts +147 -42
- package/src/hooks/useMessages.ts +106 -53
- package/src/hooks/useWebSocket.ts +17 -4
- package/src/index.tsx +11 -0
- package/src/types.ts +39 -2
- package/src/utils/api.ts +1 -0
- package/storybook-static/assets/BookingCancelledCard-XHuB-Ebp.js +31 -0
- package/storybook-static/assets/BookingCards.stories-DfJ482RS.js +66 -0
- package/storybook-static/assets/BookingSlotPicker-BkfssueW.js +1 -0
- package/storybook-static/assets/BookingSlotPicker.stories-fYlg1zLg.js +50 -0
- package/storybook-static/assets/BrandIcons-BsRAdWzL.js +4 -0
- package/storybook-static/assets/BrandIcons.stories-C6gBovfU.js +106 -0
- package/storybook-static/assets/Chat.stories-BrR7LHsz.js +830 -0
- package/storybook-static/assets/{Color-YHDXOIA2-CSuNIR0a.js → Color-YHDXOIA2-azE51u2m.js} +1 -1
- package/storybook-static/assets/{DocsRenderer-CFRXHY34-dpuOKTQp.js → DocsRenderer-CFRXHY34-jTmzKIDk.js} +3 -3
- package/storybook-static/assets/MessageItem-pEOwuLyh.js +34 -0
- package/storybook-static/assets/{MessageItem.stories-CsxqSqu-.js → MessageItem.stories-Cs5Vtkle.js} +2 -2
- package/storybook-static/assets/{entry-preview-C_-WO6GJ.js → entry-preview-vcpiajAT.js} +1 -1
- package/storybook-static/assets/globe-BtMvkLMD.js +31 -0
- package/storybook-static/assets/{iframe-BXTccXxS.js → iframe-Cx1n-SeE.js} +2 -2
- package/storybook-static/assets/{preview-Cyx3pE7Q.js → preview-Do3b3dZv.js} +2 -2
- package/storybook-static/iframe.html +1 -1
- package/storybook-static/index.json +1 -1
- package/storybook-static/project.json +1 -1
- package/storybook-static/assets/BrandIcons-Cjy5INAp.js +0 -4
- package/storybook-static/assets/BrandIcons.stories-BeVC6svr.js +0 -64
- package/storybook-static/assets/Chat.stories-BkbpOOSG.js +0 -830
- package/storybook-static/assets/MessageItem-Dlb6dSKL.js +0 -14
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/ChatWidget.tsx
|
|
2
|
-
import { useCallback as
|
|
2
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/hooks/useWebSocket.ts
|
|
5
5
|
import { useEffect, useRef, useState, useCallback } from "react";
|
|
@@ -12,6 +12,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
12
12
|
const reconnectAttemptsRef = useRef(0);
|
|
13
13
|
const messageHandlerRef = useRef(null);
|
|
14
14
|
const abortedRef = useRef(false);
|
|
15
|
+
const connectionGenerationRef = useRef(0);
|
|
15
16
|
const maxReconnectAttempts = 5;
|
|
16
17
|
const reconnectDelay = 3e3;
|
|
17
18
|
const isUsingExternalWs = !!externalWebSocket;
|
|
@@ -23,9 +24,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
23
24
|
try {
|
|
24
25
|
const message = JSON.parse(event.data);
|
|
25
26
|
setLastMessage(message);
|
|
26
|
-
if (message.type === "
|
|
27
|
-
config.onMessageReceived?.(message.data);
|
|
28
|
-
} else if (message.type === "error") {
|
|
27
|
+
if (message.type === "error") {
|
|
29
28
|
const err = new Error(message.data?.message || "WebSocket error");
|
|
30
29
|
setError(err);
|
|
31
30
|
config.onError?.(err);
|
|
@@ -45,6 +44,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
45
44
|
}, []);
|
|
46
45
|
const connect = useCallback(() => {
|
|
47
46
|
if (abortedRef.current) return;
|
|
47
|
+
const generation = ++connectionGenerationRef.current;
|
|
48
48
|
console.log("connecting to WebSocket...", config.currentUser, config.conversationId);
|
|
49
49
|
try {
|
|
50
50
|
if (wsRef.current) {
|
|
@@ -64,6 +64,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
64
64
|
}
|
|
65
65
|
const ws = new WebSocket(url.toString());
|
|
66
66
|
ws.onopen = () => {
|
|
67
|
+
if (generation !== connectionGenerationRef.current) return;
|
|
67
68
|
if (abortedRef.current) {
|
|
68
69
|
ws.close(1e3, "Effect cleaned up");
|
|
69
70
|
return;
|
|
@@ -75,6 +76,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
75
76
|
config.onConnectionChange?.(true);
|
|
76
77
|
};
|
|
77
78
|
ws.onerror = (event) => {
|
|
79
|
+
if (generation !== connectionGenerationRef.current) return;
|
|
78
80
|
if (abortedRef.current) return;
|
|
79
81
|
console.error("WebSocket error:", event);
|
|
80
82
|
const err = new Error("WebSocket connection error");
|
|
@@ -82,6 +84,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
82
84
|
config.onError?.(err);
|
|
83
85
|
};
|
|
84
86
|
ws.onclose = (event) => {
|
|
87
|
+
if (generation !== connectionGenerationRef.current) return;
|
|
85
88
|
if (abortedRef.current) return;
|
|
86
89
|
console.log("WebSocket closed:", event.code, event.reason);
|
|
87
90
|
setIsConnected(false);
|
|
@@ -183,7 +186,8 @@ async function fetchMessages(baseUrl, params, headers) {
|
|
|
183
186
|
headers: {
|
|
184
187
|
"Content-Type": "application/json",
|
|
185
188
|
...headers
|
|
186
|
-
}
|
|
189
|
+
},
|
|
190
|
+
timeout: 8e3
|
|
187
191
|
});
|
|
188
192
|
return {
|
|
189
193
|
data: response.data.data ?? [],
|
|
@@ -200,7 +204,8 @@ async function fetchMessages(baseUrl, params, headers) {
|
|
|
200
204
|
}
|
|
201
205
|
|
|
202
206
|
// src/hooks/useMessages.ts
|
|
203
|
-
var BOT_THINKING_TIMEOUT =
|
|
207
|
+
var BOT_THINKING_TIMEOUT = 15e3;
|
|
208
|
+
var AGENT_STATUSES = /* @__PURE__ */ new Set(["agent_active", "pending_agent"]);
|
|
204
209
|
function useMessages(websocket, config) {
|
|
205
210
|
const [messages, setMessages] = useState2([]);
|
|
206
211
|
const [isLoading, setIsLoading] = useState2(false);
|
|
@@ -210,6 +215,7 @@ function useMessages(websocket, config) {
|
|
|
210
215
|
const [isLoadingMore, setIsLoadingMore] = useState2(false);
|
|
211
216
|
const [isBotThinking, setIsBotThinking] = useState2(false);
|
|
212
217
|
const botThinkingTimerRef = useRef2(null);
|
|
218
|
+
const agentActiveRef = useRef2(false);
|
|
213
219
|
const { httpApiUrl, conversationId, headers, onError, toast } = config;
|
|
214
220
|
const headersWithApiKey = useMemo(
|
|
215
221
|
() => ({
|
|
@@ -218,11 +224,33 @@ function useMessages(websocket, config) {
|
|
|
218
224
|
}),
|
|
219
225
|
[headers, config.apiKey]
|
|
220
226
|
);
|
|
227
|
+
const clearBotThinking = useCallback2(() => {
|
|
228
|
+
setIsBotThinking(false);
|
|
229
|
+
if (botThinkingTimerRef.current) {
|
|
230
|
+
clearTimeout(botThinkingTimerRef.current);
|
|
231
|
+
botThinkingTimerRef.current = null;
|
|
232
|
+
}
|
|
233
|
+
}, []);
|
|
221
234
|
useEffect2(() => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
if (!httpApiUrl || !conversationId) return;
|
|
236
|
+
const fetchStatus = async () => {
|
|
237
|
+
try {
|
|
238
|
+
const url = `${httpApiUrl}/conversation?conversationId=${encodeURIComponent(conversationId)}`;
|
|
239
|
+
const res = await fetch(url, { headers: headersWithApiKey });
|
|
240
|
+
if (!res.ok) return;
|
|
241
|
+
const json = await res.json();
|
|
242
|
+
const conv = json.data?.conversation ?? json.data;
|
|
243
|
+
if (conv?.status && AGENT_STATUSES.has(conv.status)) {
|
|
244
|
+
agentActiveRef.current = true;
|
|
245
|
+
}
|
|
246
|
+
} catch {
|
|
225
247
|
}
|
|
248
|
+
};
|
|
249
|
+
fetchStatus();
|
|
250
|
+
}, [httpApiUrl, conversationId, headersWithApiKey]);
|
|
251
|
+
useEffect2(() => {
|
|
252
|
+
if (!httpApiUrl || !conversationId) return;
|
|
253
|
+
const loadMessages = async () => {
|
|
226
254
|
setIsLoading(true);
|
|
227
255
|
setError(null);
|
|
228
256
|
try {
|
|
@@ -234,6 +262,9 @@ function useMessages(websocket, config) {
|
|
|
234
262
|
setMessages(result.data);
|
|
235
263
|
setNextPageToken(result.nextPageToken);
|
|
236
264
|
setHasMore(!!result.nextPageToken);
|
|
265
|
+
if (result.data.some((m) => m.senderType === "agent")) {
|
|
266
|
+
agentActiveRef.current = true;
|
|
267
|
+
}
|
|
237
268
|
} catch (err) {
|
|
238
269
|
const error2 = err instanceof Error ? err : new Error("Failed to load messages");
|
|
239
270
|
setError(error2);
|
|
@@ -249,33 +280,55 @@ function useMessages(websocket, config) {
|
|
|
249
280
|
useEffect2(() => {
|
|
250
281
|
if (websocket.lastMessage?.type === "message" && websocket.lastMessage.data) {
|
|
251
282
|
const newMessage = websocket.lastMessage.data;
|
|
252
|
-
if (conversationId && newMessage.conversationId !== conversationId)
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
283
|
+
if (conversationId && newMessage.conversationId !== conversationId) return;
|
|
255
284
|
setMessages((prev) => {
|
|
256
|
-
if (prev.some((msg) => msg.id === newMessage.id))
|
|
257
|
-
|
|
285
|
+
if (prev.some((msg) => msg.id === newMessage.id)) return prev;
|
|
286
|
+
const newMsgTime = new Date(newMessage.createdAt).getTime();
|
|
287
|
+
const tempIdx = prev.findIndex(
|
|
288
|
+
(msg) => msg.id.startsWith("temp-") && msg.content === newMessage.content && msg.senderId === newMessage.senderId && Math.abs(new Date(msg.createdAt).getTime() - newMsgTime) < 1e4
|
|
289
|
+
);
|
|
290
|
+
if (tempIdx >= 0) {
|
|
291
|
+
const next = [...prev];
|
|
292
|
+
next[tempIdx] = newMessage;
|
|
293
|
+
return next;
|
|
258
294
|
}
|
|
259
295
|
return [...prev, newMessage];
|
|
260
296
|
});
|
|
261
|
-
if (newMessage.senderType === "
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
297
|
+
if (newMessage.senderType === "agent") {
|
|
298
|
+
agentActiveRef.current = true;
|
|
299
|
+
}
|
|
300
|
+
if (newMessage.senderType !== "customer") {
|
|
301
|
+
clearBotThinking();
|
|
267
302
|
}
|
|
268
303
|
onMessageReceived?.(newMessage);
|
|
269
304
|
}
|
|
270
|
-
}, [websocket.lastMessage, onMessageReceived, conversationId]);
|
|
305
|
+
}, [websocket.lastMessage, onMessageReceived, conversationId, clearBotThinking]);
|
|
306
|
+
useEffect2(() => {
|
|
307
|
+
if (!websocket.lastMessage) return;
|
|
308
|
+
const { type, data } = websocket.lastMessage;
|
|
309
|
+
if (type === "bot_not_responding") {
|
|
310
|
+
if (data?.conversationId && conversationId && data.conversationId !== conversationId) return;
|
|
311
|
+
clearBotThinking();
|
|
312
|
+
agentActiveRef.current = true;
|
|
313
|
+
}
|
|
314
|
+
if (type === "conversation_updated") {
|
|
315
|
+
if (data?.conversationId && conversationId && data.conversationId !== conversationId) return;
|
|
316
|
+
if (data?.status && AGENT_STATUSES.has(data.status)) {
|
|
317
|
+
agentActiveRef.current = true;
|
|
318
|
+
clearBotThinking();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}, [websocket.lastMessage, conversationId, clearBotThinking]);
|
|
271
322
|
const addMessage = useCallback2((message) => {
|
|
323
|
+
const isAgentActive = agentActiveRef.current;
|
|
272
324
|
setMessages((prev) => {
|
|
273
|
-
if (prev.some((msg) => msg.id === message.id))
|
|
274
|
-
return prev;
|
|
275
|
-
}
|
|
325
|
+
if (prev.some((msg) => msg.id === message.id)) return prev;
|
|
276
326
|
return [...prev, message];
|
|
277
327
|
});
|
|
278
|
-
if (message.senderType === "
|
|
328
|
+
if (message.senderType === "agent") {
|
|
329
|
+
agentActiveRef.current = true;
|
|
330
|
+
}
|
|
331
|
+
if (message.senderType === "customer" && !isAgentActive) {
|
|
279
332
|
setIsBotThinking(true);
|
|
280
333
|
if (botThinkingTimerRef.current) clearTimeout(botThinkingTimerRef.current);
|
|
281
334
|
botThinkingTimerRef.current = setTimeout(() => {
|
|
@@ -288,21 +341,16 @@ function useMessages(websocket, config) {
|
|
|
288
341
|
}, []);
|
|
289
342
|
const clearMessages = useCallback2(() => {
|
|
290
343
|
setMessages([]);
|
|
344
|
+
agentActiveRef.current = false;
|
|
291
345
|
}, []);
|
|
292
346
|
const loadMore = useCallback2(async () => {
|
|
293
|
-
if (!hasMore || isLoadingMore || !httpApiUrl || !conversationId || !nextPageToken)
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
347
|
+
if (!hasMore || isLoadingMore || !httpApiUrl || !conversationId || !nextPageToken) return;
|
|
296
348
|
setIsLoadingMore(true);
|
|
297
349
|
setError(null);
|
|
298
350
|
try {
|
|
299
351
|
const result = await fetchMessages(
|
|
300
352
|
httpApiUrl,
|
|
301
|
-
{
|
|
302
|
-
conversationId,
|
|
303
|
-
limit: 20,
|
|
304
|
-
pageToken: nextPageToken
|
|
305
|
-
},
|
|
353
|
+
{ conversationId, limit: 20, pageToken: nextPageToken },
|
|
306
354
|
headersWithApiKey
|
|
307
355
|
);
|
|
308
356
|
setMessages((prev) => [...result.data, ...prev]);
|
|
@@ -315,15 +363,7 @@ function useMessages(websocket, config) {
|
|
|
315
363
|
} finally {
|
|
316
364
|
setIsLoadingMore(false);
|
|
317
365
|
}
|
|
318
|
-
}, [
|
|
319
|
-
hasMore,
|
|
320
|
-
isLoadingMore,
|
|
321
|
-
httpApiUrl,
|
|
322
|
-
conversationId,
|
|
323
|
-
nextPageToken,
|
|
324
|
-
headersWithApiKey,
|
|
325
|
-
onError
|
|
326
|
-
]);
|
|
366
|
+
}, [hasMore, isLoadingMore, httpApiUrl, conversationId, nextPageToken, headersWithApiKey, onError]);
|
|
327
367
|
useEffect2(() => {
|
|
328
368
|
return () => {
|
|
329
369
|
if (botThinkingTimerRef.current) clearTimeout(botThinkingTimerRef.current);
|
|
@@ -756,24 +796,20 @@ function XcelsiorSymbol({ size = 24, color = "white", className = "", style }) {
|
|
|
756
796
|
}
|
|
757
797
|
);
|
|
758
798
|
}
|
|
759
|
-
function ChatBubbleIcon({ size =
|
|
760
|
-
return /* @__PURE__ */
|
|
761
|
-
"
|
|
762
|
-
{
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
"aria-hidden": "true",
|
|
774
|
-
children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
|
|
775
|
-
}
|
|
776
|
-
);
|
|
799
|
+
function ChatBubbleIcon({ size = 36, color = "white", className = "", style }) {
|
|
800
|
+
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 32 32", fill: "none", xmlns: "http://www.w3.org/2000/svg", className, style, "aria-hidden": "true", children: [
|
|
801
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "3.5", r: "3", fill: "#67e8f9", opacity: 0.4 }),
|
|
802
|
+
/* @__PURE__ */ jsx("circle", { cx: "16", cy: "3.5", r: "1.5", fill: "#67e8f9" }),
|
|
803
|
+
/* @__PURE__ */ jsx("rect", { x: "15", y: "4.5", width: "2", height: "4", rx: "1", fill: color }),
|
|
804
|
+
/* @__PURE__ */ jsx("rect", { x: "1", y: "15", width: "4", height: "6", rx: "2", fill: color, opacity: 0.5 }),
|
|
805
|
+
/* @__PURE__ */ jsx("rect", { x: "27", y: "15", width: "4", height: "6", rx: "2", fill: color, opacity: 0.5 }),
|
|
806
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "8.5", width: "22", height: "19", rx: "5", fill: color }),
|
|
807
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "16.5", r: "3", fill: "#1a4fd0" }),
|
|
808
|
+
/* @__PURE__ */ jsx("circle", { cx: "20", cy: "16.5", r: "3", fill: "#1a4fd0" }),
|
|
809
|
+
/* @__PURE__ */ jsx("circle", { cx: "13", cy: "15.5", r: "1.2", fill: color }),
|
|
810
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "15.5", r: "1.2", fill: color }),
|
|
811
|
+
/* @__PURE__ */ jsx("path", { d: "M13 22.5q3 2.5 6 0", stroke: "#1a4fd0", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
812
|
+
] });
|
|
777
813
|
}
|
|
778
814
|
function XcelsiorAvatar({ size = 40, className = "" }) {
|
|
779
815
|
const iconSize = Math.round(size * 0.55);
|
|
@@ -937,7 +973,7 @@ function ChatHeader({ agent, onClose, onMinimize, theme }) {
|
|
|
937
973
|
}
|
|
938
974
|
|
|
939
975
|
// src/components/MessageList.tsx
|
|
940
|
-
import { useCallback as
|
|
976
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef5 } from "react";
|
|
941
977
|
|
|
942
978
|
// src/components/Spinner.tsx
|
|
943
979
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -985,6 +1021,7 @@ function Spinner({ size = "md", color = "currentColor" }) {
|
|
|
985
1021
|
|
|
986
1022
|
// src/components/MessageItem.tsx
|
|
987
1023
|
import { formatDistanceToNow } from "date-fns";
|
|
1024
|
+
import { Check, CheckCheck, Headphones, Paperclip } from "lucide-react";
|
|
988
1025
|
|
|
989
1026
|
// src/components/MarkdownMessage.tsx
|
|
990
1027
|
import ReactMarkdown from "react-markdown";
|
|
@@ -1224,14 +1261,481 @@ function MarkdownMessage({ content, theme }) {
|
|
|
1224
1261
|
) });
|
|
1225
1262
|
}
|
|
1226
1263
|
|
|
1264
|
+
// src/components/BookingSlotPicker.tsx
|
|
1265
|
+
import { useState as useState6, useCallback as useCallback4 } from "react";
|
|
1266
|
+
import { Calendar, Clock, Globe } from "lucide-react";
|
|
1267
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1268
|
+
function BookingSlotPicker({ data, theme, onSlotSelected, disabled = false }) {
|
|
1269
|
+
const [selectedDateIndex, setSelectedDateIndex] = useState6(0);
|
|
1270
|
+
const [selectedSlot, setSelectedSlot] = useState6(null);
|
|
1271
|
+
const bgColor = theme?.background || "#00001a";
|
|
1272
|
+
const isLightTheme = (() => {
|
|
1273
|
+
if (!bgColor.startsWith("#")) return false;
|
|
1274
|
+
const hex = bgColor.replace("#", "");
|
|
1275
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1276
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1277
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1278
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
1279
|
+
})();
|
|
1280
|
+
const primaryColor = theme?.primary || "#337eff";
|
|
1281
|
+
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1282
|
+
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)");
|
|
1283
|
+
const cardStyle = isLightTheme ? {
|
|
1284
|
+
backgroundColor: "rgba(0,0,0,0.03)",
|
|
1285
|
+
boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)",
|
|
1286
|
+
borderRadius: "14px",
|
|
1287
|
+
padding: "14px"
|
|
1288
|
+
} : {
|
|
1289
|
+
backgroundColor: "rgba(255,255,255,0.04)",
|
|
1290
|
+
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.08), inset 0 1px 0 0 rgba(255,255,255,0.1)",
|
|
1291
|
+
borderRadius: "14px",
|
|
1292
|
+
padding: "14px"
|
|
1293
|
+
};
|
|
1294
|
+
const activePillStyle = {
|
|
1295
|
+
backgroundColor: primaryColor,
|
|
1296
|
+
color: "#ffffff",
|
|
1297
|
+
borderRadius: "20px",
|
|
1298
|
+
padding: "5px 12px",
|
|
1299
|
+
fontSize: "12px",
|
|
1300
|
+
fontWeight: "600",
|
|
1301
|
+
letterSpacing: "0.01em",
|
|
1302
|
+
cursor: disabled ? "default" : "pointer",
|
|
1303
|
+
border: "none",
|
|
1304
|
+
flexShrink: 0
|
|
1305
|
+
};
|
|
1306
|
+
const inactivePillStyle = isLightTheme ? {
|
|
1307
|
+
backgroundColor: "rgba(0,0,0,0.04)",
|
|
1308
|
+
color: textColor,
|
|
1309
|
+
borderRadius: "20px",
|
|
1310
|
+
padding: "5px 12px",
|
|
1311
|
+
fontSize: "12px",
|
|
1312
|
+
fontWeight: "500",
|
|
1313
|
+
letterSpacing: "0.01em",
|
|
1314
|
+
cursor: disabled ? "default" : "pointer",
|
|
1315
|
+
border: "none",
|
|
1316
|
+
boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.1)",
|
|
1317
|
+
flexShrink: 0
|
|
1318
|
+
} : {
|
|
1319
|
+
backgroundColor: "rgba(255,255,255,0.05)",
|
|
1320
|
+
color: textColor,
|
|
1321
|
+
borderRadius: "20px",
|
|
1322
|
+
padding: "5px 12px",
|
|
1323
|
+
fontSize: "12px",
|
|
1324
|
+
fontWeight: "500",
|
|
1325
|
+
letterSpacing: "0.01em",
|
|
1326
|
+
cursor: disabled ? "default" : "pointer",
|
|
1327
|
+
border: "none",
|
|
1328
|
+
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.1)",
|
|
1329
|
+
flexShrink: 0
|
|
1330
|
+
};
|
|
1331
|
+
const activeSlot = selectedSlot?.date === data.availableDates[selectedDateIndex]?.date ? selectedSlot.time : null;
|
|
1332
|
+
const [confirming, setConfirming] = useState6(false);
|
|
1333
|
+
const handleSlotClick = (date, time) => {
|
|
1334
|
+
if (disabled || confirming) return;
|
|
1335
|
+
setSelectedSlot({ date, time });
|
|
1336
|
+
};
|
|
1337
|
+
const handleConfirm = useCallback4(() => {
|
|
1338
|
+
if (!selectedSlot || disabled || confirming) return;
|
|
1339
|
+
setConfirming(true);
|
|
1340
|
+
onSlotSelected(selectedSlot.date, selectedSlot.time);
|
|
1341
|
+
}, [selectedSlot, disabled, confirming, onSlotSelected]);
|
|
1342
|
+
const currentDate = data.availableDates[selectedDateIndex];
|
|
1343
|
+
return /* @__PURE__ */ jsxs4("div", { style: cardStyle, children: [
|
|
1344
|
+
/* @__PURE__ */ jsx5(
|
|
1345
|
+
"p",
|
|
1346
|
+
{
|
|
1347
|
+
style: {
|
|
1348
|
+
fontSize: "11px",
|
|
1349
|
+
fontWeight: "600",
|
|
1350
|
+
letterSpacing: "0.06em",
|
|
1351
|
+
textTransform: "uppercase",
|
|
1352
|
+
color: textMuted,
|
|
1353
|
+
marginBottom: "10px"
|
|
1354
|
+
},
|
|
1355
|
+
children: "Select a date"
|
|
1356
|
+
}
|
|
1357
|
+
),
|
|
1358
|
+
/* @__PURE__ */ jsx5(
|
|
1359
|
+
"div",
|
|
1360
|
+
{
|
|
1361
|
+
style: {
|
|
1362
|
+
display: "flex",
|
|
1363
|
+
gap: "6px",
|
|
1364
|
+
overflowX: "auto",
|
|
1365
|
+
paddingBottom: "2px",
|
|
1366
|
+
scrollbarWidth: "none",
|
|
1367
|
+
msOverflowStyle: "none",
|
|
1368
|
+
marginBottom: "14px"
|
|
1369
|
+
},
|
|
1370
|
+
children: data.availableDates.map((dateSlot, index) => /* @__PURE__ */ jsx5(
|
|
1371
|
+
"button",
|
|
1372
|
+
{
|
|
1373
|
+
type: "button",
|
|
1374
|
+
onClick: () => !disabled && setSelectedDateIndex(index),
|
|
1375
|
+
style: index === selectedDateIndex ? activePillStyle : inactivePillStyle,
|
|
1376
|
+
title: dateSlot.dayName,
|
|
1377
|
+
"aria-pressed": index === selectedDateIndex,
|
|
1378
|
+
disabled,
|
|
1379
|
+
children: dateSlot.dayLabel
|
|
1380
|
+
},
|
|
1381
|
+
dateSlot.date
|
|
1382
|
+
))
|
|
1383
|
+
}
|
|
1384
|
+
),
|
|
1385
|
+
currentDate && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1386
|
+
/* @__PURE__ */ jsx5(
|
|
1387
|
+
"p",
|
|
1388
|
+
{
|
|
1389
|
+
style: {
|
|
1390
|
+
fontSize: "11px",
|
|
1391
|
+
fontWeight: "600",
|
|
1392
|
+
letterSpacing: "0.06em",
|
|
1393
|
+
textTransform: "uppercase",
|
|
1394
|
+
color: textMuted,
|
|
1395
|
+
marginBottom: "8px"
|
|
1396
|
+
},
|
|
1397
|
+
children: currentDate.dayName
|
|
1398
|
+
}
|
|
1399
|
+
),
|
|
1400
|
+
/* @__PURE__ */ jsx5(
|
|
1401
|
+
"div",
|
|
1402
|
+
{
|
|
1403
|
+
style: {
|
|
1404
|
+
display: "grid",
|
|
1405
|
+
gridTemplateColumns: "repeat(3, 1fr)",
|
|
1406
|
+
gap: "6px",
|
|
1407
|
+
marginBottom: "14px"
|
|
1408
|
+
},
|
|
1409
|
+
children: currentDate.slots.map((time) => {
|
|
1410
|
+
const isSelected = activeSlot === time;
|
|
1411
|
+
const isConfirmed = selectedSlot?.date === currentDate.date && selectedSlot.time === time;
|
|
1412
|
+
return /* @__PURE__ */ jsx5(
|
|
1413
|
+
"button",
|
|
1414
|
+
{
|
|
1415
|
+
type: "button",
|
|
1416
|
+
onClick: () => handleSlotClick(currentDate.date, time),
|
|
1417
|
+
disabled: disabled && !isConfirmed,
|
|
1418
|
+
"aria-pressed": isSelected,
|
|
1419
|
+
style: {
|
|
1420
|
+
...isSelected ? activePillStyle : inactivePillStyle,
|
|
1421
|
+
padding: "7px 8px",
|
|
1422
|
+
borderRadius: "8px",
|
|
1423
|
+
fontSize: "13px",
|
|
1424
|
+
fontWeight: isSelected ? "600" : "400",
|
|
1425
|
+
textAlign: "center",
|
|
1426
|
+
opacity: disabled && !isConfirmed ? 0.4 : 1,
|
|
1427
|
+
transition: "opacity 0.15s ease"
|
|
1428
|
+
},
|
|
1429
|
+
children: time
|
|
1430
|
+
},
|
|
1431
|
+
time
|
|
1432
|
+
);
|
|
1433
|
+
})
|
|
1434
|
+
}
|
|
1435
|
+
)
|
|
1436
|
+
] }),
|
|
1437
|
+
selectedSlot && !disabled && /* @__PURE__ */ jsxs4(
|
|
1438
|
+
"button",
|
|
1439
|
+
{
|
|
1440
|
+
type: "button",
|
|
1441
|
+
onClick: handleConfirm,
|
|
1442
|
+
disabled: confirming,
|
|
1443
|
+
style: {
|
|
1444
|
+
width: "100%",
|
|
1445
|
+
padding: "10px 16px",
|
|
1446
|
+
borderRadius: "10px",
|
|
1447
|
+
border: "none",
|
|
1448
|
+
backgroundColor: confirming ? `${primaryColor}80` : primaryColor,
|
|
1449
|
+
color: "#ffffff",
|
|
1450
|
+
fontSize: "13px",
|
|
1451
|
+
fontWeight: "600",
|
|
1452
|
+
letterSpacing: "0.01em",
|
|
1453
|
+
cursor: confirming ? "default" : "pointer",
|
|
1454
|
+
marginBottom: "12px",
|
|
1455
|
+
display: "flex",
|
|
1456
|
+
alignItems: "center",
|
|
1457
|
+
justifyContent: "center",
|
|
1458
|
+
gap: "6px",
|
|
1459
|
+
transition: "background-color 0.15s ease"
|
|
1460
|
+
},
|
|
1461
|
+
children: [
|
|
1462
|
+
/* @__PURE__ */ jsx5(Calendar, { size: 14, "aria-hidden": "true" }),
|
|
1463
|
+
confirming ? "Booking..." : `Book ${selectedSlot.time} on ${data.availableDates.find((d) => d.date === selectedSlot.date)?.dayLabel ?? selectedSlot.date}`
|
|
1464
|
+
]
|
|
1465
|
+
}
|
|
1466
|
+
),
|
|
1467
|
+
/* @__PURE__ */ jsxs4(
|
|
1468
|
+
"div",
|
|
1469
|
+
{
|
|
1470
|
+
style: {
|
|
1471
|
+
display: "flex",
|
|
1472
|
+
alignItems: "center",
|
|
1473
|
+
justifyContent: "space-between",
|
|
1474
|
+
gap: "8px"
|
|
1475
|
+
},
|
|
1476
|
+
children: [
|
|
1477
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "5px" }, children: [
|
|
1478
|
+
/* @__PURE__ */ jsx5(Globe, { size: 11, color: textMuted, "aria-hidden": "true" }),
|
|
1479
|
+
/* @__PURE__ */ jsx5("span", { style: { fontSize: "11px", color: textMuted, letterSpacing: "0.01em" }, children: data.timezone })
|
|
1480
|
+
] }),
|
|
1481
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "5px" }, children: [
|
|
1482
|
+
/* @__PURE__ */ jsx5(Clock, { size: 11, color: textMuted, "aria-hidden": "true" }),
|
|
1483
|
+
/* @__PURE__ */ jsxs4("span", { style: { fontSize: "11px", color: textMuted, letterSpacing: "0.01em" }, children: [
|
|
1484
|
+
data.meetingDuration,
|
|
1485
|
+
" min"
|
|
1486
|
+
] })
|
|
1487
|
+
] })
|
|
1488
|
+
]
|
|
1489
|
+
}
|
|
1490
|
+
)
|
|
1491
|
+
] });
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
// src/components/BookingConfirmationCard.tsx
|
|
1495
|
+
import { CircleCheck, Calendar as Calendar2, Clock as Clock2, MessageSquare, Video, CalendarPlus, Globe as Globe2 } from "lucide-react";
|
|
1496
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1497
|
+
function BookingConfirmationCard({ data, theme }) {
|
|
1498
|
+
const bgColor = theme?.background || "#00001a";
|
|
1499
|
+
const isLightTheme = (() => {
|
|
1500
|
+
if (!bgColor.startsWith("#")) return false;
|
|
1501
|
+
const hex = bgColor.replace("#", "");
|
|
1502
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1503
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1504
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1505
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
1506
|
+
})();
|
|
1507
|
+
const primaryColor = theme?.primary || "#337eff";
|
|
1508
|
+
const successColor = theme?.statusPositive || "#22c55e";
|
|
1509
|
+
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1510
|
+
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)");
|
|
1511
|
+
const cardStyle = isLightTheme ? {
|
|
1512
|
+
backgroundColor: "rgba(0,0,0,0.03)",
|
|
1513
|
+
boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)",
|
|
1514
|
+
borderRadius: "14px",
|
|
1515
|
+
padding: "16px",
|
|
1516
|
+
overflow: "hidden"
|
|
1517
|
+
} : {
|
|
1518
|
+
backgroundColor: "rgba(255,255,255,0.04)",
|
|
1519
|
+
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.08), inset 0 1px 0 0 rgba(255,255,255,0.1)",
|
|
1520
|
+
borderRadius: "14px",
|
|
1521
|
+
padding: "16px",
|
|
1522
|
+
overflow: "hidden"
|
|
1523
|
+
};
|
|
1524
|
+
const detailRowStyle = {
|
|
1525
|
+
display: "flex",
|
|
1526
|
+
alignItems: "flex-start",
|
|
1527
|
+
gap: "8px",
|
|
1528
|
+
marginBottom: "8px"
|
|
1529
|
+
};
|
|
1530
|
+
const dividerStyle = {
|
|
1531
|
+
height: "1px",
|
|
1532
|
+
backgroundColor: isLightTheme ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.06)",
|
|
1533
|
+
margin: "12px 0"
|
|
1534
|
+
};
|
|
1535
|
+
const actionButtonBase = {
|
|
1536
|
+
display: "flex",
|
|
1537
|
+
alignItems: "center",
|
|
1538
|
+
justifyContent: "center",
|
|
1539
|
+
gap: "6px",
|
|
1540
|
+
flex: 1,
|
|
1541
|
+
padding: "9px 12px",
|
|
1542
|
+
borderRadius: "9px",
|
|
1543
|
+
fontSize: "12px",
|
|
1544
|
+
fontWeight: "600",
|
|
1545
|
+
letterSpacing: "0.01em",
|
|
1546
|
+
cursor: "pointer",
|
|
1547
|
+
textDecoration: "none",
|
|
1548
|
+
border: "none",
|
|
1549
|
+
transition: "opacity 0.15s ease"
|
|
1550
|
+
};
|
|
1551
|
+
return /* @__PURE__ */ jsxs5("div", { style: cardStyle, children: [
|
|
1552
|
+
/* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" }, children: [
|
|
1553
|
+
/* @__PURE__ */ jsx6(CircleCheck, { size: 22, color: successColor, "aria-hidden": "true" }),
|
|
1554
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1555
|
+
/* @__PURE__ */ jsx6(
|
|
1556
|
+
"p",
|
|
1557
|
+
{
|
|
1558
|
+
style: {
|
|
1559
|
+
fontSize: "14px",
|
|
1560
|
+
fontWeight: "700",
|
|
1561
|
+
color: textColor,
|
|
1562
|
+
letterSpacing: "0.006em",
|
|
1563
|
+
lineHeight: 1.3
|
|
1564
|
+
},
|
|
1565
|
+
children: "Meeting Confirmed"
|
|
1566
|
+
}
|
|
1567
|
+
),
|
|
1568
|
+
/* @__PURE__ */ jsx6("p", { style: { fontSize: "11px", color: successColor, letterSpacing: "0.01em", marginTop: "1px" }, children: "You'll receive a calendar invite shortly" })
|
|
1569
|
+
] })
|
|
1570
|
+
] }),
|
|
1571
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1572
|
+
/* @__PURE__ */ jsxs5("div", { style: detailRowStyle, children: [
|
|
1573
|
+
/* @__PURE__ */ jsx6(Calendar2, { size: 14, color: primaryColor, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1574
|
+
/* @__PURE__ */ jsxs5("span", { style: { fontSize: "13px", color: textColor, lineHeight: 1.4 }, children: [
|
|
1575
|
+
data.meetingDate,
|
|
1576
|
+
" at ",
|
|
1577
|
+
data.meetingTime
|
|
1578
|
+
] })
|
|
1579
|
+
] }),
|
|
1580
|
+
/* @__PURE__ */ jsxs5("div", { style: detailRowStyle, children: [
|
|
1581
|
+
/* @__PURE__ */ jsx6(Clock2, { size: 14, color: primaryColor, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1582
|
+
/* @__PURE__ */ jsxs5("span", { style: { fontSize: "13px", color: textColor, lineHeight: 1.4 }, children: [
|
|
1583
|
+
data.meetingDuration,
|
|
1584
|
+
" minutes"
|
|
1585
|
+
] })
|
|
1586
|
+
] }),
|
|
1587
|
+
/* @__PURE__ */ jsxs5("div", { style: { ...detailRowStyle, marginBottom: "0" }, children: [
|
|
1588
|
+
/* @__PURE__ */ jsx6(MessageSquare, { size: 14, color: primaryColor, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1589
|
+
/* @__PURE__ */ jsx6("span", { style: { fontSize: "13px", color: textColor, lineHeight: 1.4 }, children: data.meetingPurpose })
|
|
1590
|
+
] })
|
|
1591
|
+
] }),
|
|
1592
|
+
data.timezone && /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: "5px", marginTop: "8px" }, children: [
|
|
1593
|
+
/* @__PURE__ */ jsx6(Globe2, { size: 11, color: textMuted, "aria-hidden": "true" }),
|
|
1594
|
+
/* @__PURE__ */ jsx6("span", { style: { fontSize: "11px", color: textMuted, letterSpacing: "0.01em" }, children: data.timezone })
|
|
1595
|
+
] }),
|
|
1596
|
+
(data.meetLink || data.calendarLink) && /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1597
|
+
/* @__PURE__ */ jsx6("div", { style: dividerStyle }),
|
|
1598
|
+
/* @__PURE__ */ jsxs5("div", { style: { display: "flex", gap: "8px" }, children: [
|
|
1599
|
+
data.meetLink && /* @__PURE__ */ jsxs5(
|
|
1600
|
+
"a",
|
|
1601
|
+
{
|
|
1602
|
+
href: data.meetLink,
|
|
1603
|
+
target: "_blank",
|
|
1604
|
+
rel: "noopener noreferrer",
|
|
1605
|
+
style: {
|
|
1606
|
+
...actionButtonBase,
|
|
1607
|
+
background: `linear-gradient(135deg, ${primaryColor}, ${theme?.primaryStrong || "#005eff"})`,
|
|
1608
|
+
color: "#ffffff",
|
|
1609
|
+
boxShadow: `0 2px 8px -2px ${primaryColor}50`
|
|
1610
|
+
},
|
|
1611
|
+
children: [
|
|
1612
|
+
/* @__PURE__ */ jsx6(Video, { size: 13, "aria-hidden": "true" }),
|
|
1613
|
+
"Join Meet"
|
|
1614
|
+
]
|
|
1615
|
+
}
|
|
1616
|
+
),
|
|
1617
|
+
data.calendarLink && /* @__PURE__ */ jsxs5(
|
|
1618
|
+
"a",
|
|
1619
|
+
{
|
|
1620
|
+
href: data.calendarLink,
|
|
1621
|
+
target: "_blank",
|
|
1622
|
+
rel: "noopener noreferrer",
|
|
1623
|
+
style: {
|
|
1624
|
+
...actionButtonBase,
|
|
1625
|
+
backgroundColor: isLightTheme ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.07)",
|
|
1626
|
+
color: textColor,
|
|
1627
|
+
boxShadow: isLightTheme ? "inset 0 0 0 1px rgba(0,0,0,0.1)" : "inset 0 0 0 0.5px rgba(255,255,255,0.12)"
|
|
1628
|
+
},
|
|
1629
|
+
children: [
|
|
1630
|
+
/* @__PURE__ */ jsx6(CalendarPlus, { size: 13, "aria-hidden": "true" }),
|
|
1631
|
+
"Add to Calendar"
|
|
1632
|
+
]
|
|
1633
|
+
}
|
|
1634
|
+
)
|
|
1635
|
+
] })
|
|
1636
|
+
] })
|
|
1637
|
+
] });
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// src/components/BookingCancelledCard.tsx
|
|
1641
|
+
import { CircleX, Calendar as Calendar3, MessageSquare as MessageSquare2 } from "lucide-react";
|
|
1642
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1643
|
+
function BookingCancelledCard({ data, theme }) {
|
|
1644
|
+
const bgColor = theme?.background || "#00001a";
|
|
1645
|
+
const isLightTheme = (() => {
|
|
1646
|
+
if (!bgColor.startsWith("#")) return false;
|
|
1647
|
+
const hex = bgColor.replace("#", "");
|
|
1648
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1649
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1650
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1651
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
1652
|
+
})();
|
|
1653
|
+
const negativeColor = theme?.statusNegative || "#ef4444";
|
|
1654
|
+
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1655
|
+
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)");
|
|
1656
|
+
const cardStyle = isLightTheme ? {
|
|
1657
|
+
backgroundColor: "rgba(239,68,68,0.04)",
|
|
1658
|
+
boxShadow: "inset 0 0 0 1px rgba(239,68,68,0.12)",
|
|
1659
|
+
borderRadius: "14px",
|
|
1660
|
+
padding: "16px"
|
|
1661
|
+
} : {
|
|
1662
|
+
backgroundColor: "rgba(239,68,68,0.06)",
|
|
1663
|
+
boxShadow: "inset 0 0 0 0.5px rgba(239,68,68,0.18)",
|
|
1664
|
+
borderRadius: "14px",
|
|
1665
|
+
padding: "16px"
|
|
1666
|
+
};
|
|
1667
|
+
const strikethroughTextStyle = {
|
|
1668
|
+
fontSize: "13px",
|
|
1669
|
+
color: textMuted,
|
|
1670
|
+
lineHeight: 1.4,
|
|
1671
|
+
textDecoration: "line-through",
|
|
1672
|
+
textDecorationColor: isLightTheme ? "rgba(0,0,0,0.25)" : "rgba(255,255,255,0.2)"
|
|
1673
|
+
};
|
|
1674
|
+
const detailRowStyle = {
|
|
1675
|
+
display: "flex",
|
|
1676
|
+
alignItems: "flex-start",
|
|
1677
|
+
gap: "8px",
|
|
1678
|
+
marginBottom: "8px"
|
|
1679
|
+
};
|
|
1680
|
+
const cancelledByLabel = data.cancelledBy === "visitor" ? "Cancelled by you" : "Cancelled by support";
|
|
1681
|
+
return /* @__PURE__ */ jsxs6("div", { style: cardStyle, children: [
|
|
1682
|
+
/* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" }, children: [
|
|
1683
|
+
/* @__PURE__ */ jsx7(CircleX, { size: 22, color: negativeColor, "aria-hidden": "true" }),
|
|
1684
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
1685
|
+
/* @__PURE__ */ jsx7(
|
|
1686
|
+
"p",
|
|
1687
|
+
{
|
|
1688
|
+
style: {
|
|
1689
|
+
fontSize: "14px",
|
|
1690
|
+
fontWeight: "700",
|
|
1691
|
+
color: textColor,
|
|
1692
|
+
letterSpacing: "0.006em",
|
|
1693
|
+
lineHeight: 1.3
|
|
1694
|
+
},
|
|
1695
|
+
children: "Meeting Cancelled"
|
|
1696
|
+
}
|
|
1697
|
+
),
|
|
1698
|
+
/* @__PURE__ */ jsx7(
|
|
1699
|
+
"p",
|
|
1700
|
+
{
|
|
1701
|
+
style: {
|
|
1702
|
+
fontSize: "11px",
|
|
1703
|
+
color: negativeColor,
|
|
1704
|
+
letterSpacing: "0.01em",
|
|
1705
|
+
marginTop: "1px",
|
|
1706
|
+
opacity: 0.8
|
|
1707
|
+
},
|
|
1708
|
+
children: cancelledByLabel
|
|
1709
|
+
}
|
|
1710
|
+
)
|
|
1711
|
+
] })
|
|
1712
|
+
] }),
|
|
1713
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
1714
|
+
/* @__PURE__ */ jsxs6("div", { style: detailRowStyle, children: [
|
|
1715
|
+
/* @__PURE__ */ jsx7(Calendar3, { size: 14, color: textMuted, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1716
|
+
/* @__PURE__ */ jsxs6("span", { style: strikethroughTextStyle, children: [
|
|
1717
|
+
data.meetingDate,
|
|
1718
|
+
" at ",
|
|
1719
|
+
data.meetingTime
|
|
1720
|
+
] })
|
|
1721
|
+
] }),
|
|
1722
|
+
/* @__PURE__ */ jsxs6("div", { style: { ...detailRowStyle, marginBottom: "0" }, children: [
|
|
1723
|
+
/* @__PURE__ */ jsx7(MessageSquare2, { size: 14, color: textMuted, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1724
|
+
/* @__PURE__ */ jsx7("span", { style: strikethroughTextStyle, children: data.meetingPurpose })
|
|
1725
|
+
] })
|
|
1726
|
+
] })
|
|
1727
|
+
] });
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1227
1730
|
// src/components/MessageItem.tsx
|
|
1228
|
-
import { jsx as
|
|
1731
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1229
1732
|
function MessageItem({
|
|
1230
1733
|
message,
|
|
1231
1734
|
currentUser,
|
|
1232
1735
|
showAvatar = true,
|
|
1233
1736
|
showTimestamp = true,
|
|
1234
|
-
theme
|
|
1737
|
+
theme,
|
|
1738
|
+
onBookingSlotSelected
|
|
1235
1739
|
}) {
|
|
1236
1740
|
const isOwnMessage = message.senderType === currentUser.type;
|
|
1237
1741
|
const isSystemMessage = message.senderType === "system";
|
|
@@ -1250,8 +1754,63 @@ function MessageItem({
|
|
|
1250
1754
|
const primaryStrong = theme?.primaryStrong || "#005eff";
|
|
1251
1755
|
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1252
1756
|
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.35)" : "rgba(247,247,248,0.35)");
|
|
1757
|
+
if (message.messageType === "booking_slots" || message.messageType === "booking_confirmation" || message.messageType === "booking_cancelled") {
|
|
1758
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex gap-2.5 mb-3 flex-row", children: [
|
|
1759
|
+
showAvatar && /* @__PURE__ */ jsx8("div", { className: "flex-shrink-0 mt-auto mb-5", children: /* @__PURE__ */ jsx8(XcelsiorAvatar, { size: 28 }) }),
|
|
1760
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-col max-w-[85%] items-start", children: [
|
|
1761
|
+
/* @__PURE__ */ jsx8(
|
|
1762
|
+
"span",
|
|
1763
|
+
{
|
|
1764
|
+
className: "mb-1 px-1 font-medium",
|
|
1765
|
+
style: {
|
|
1766
|
+
color: isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)",
|
|
1767
|
+
fontSize: "11px",
|
|
1768
|
+
letterSpacing: "0.019em"
|
|
1769
|
+
},
|
|
1770
|
+
children: "AI Assistant"
|
|
1771
|
+
}
|
|
1772
|
+
),
|
|
1773
|
+
message.messageType === "booking_slots" && /* @__PURE__ */ jsx8(
|
|
1774
|
+
BookingSlotPicker,
|
|
1775
|
+
{
|
|
1776
|
+
data: message.metadata?.bookingData,
|
|
1777
|
+
theme,
|
|
1778
|
+
onSlotSelected: (date, time) => {
|
|
1779
|
+
onBookingSlotSelected?.(date, time, message.id);
|
|
1780
|
+
},
|
|
1781
|
+
disabled: !!message.metadata?.slotSelected
|
|
1782
|
+
}
|
|
1783
|
+
),
|
|
1784
|
+
message.messageType === "booking_confirmation" && /* @__PURE__ */ jsx8(
|
|
1785
|
+
BookingConfirmationCard,
|
|
1786
|
+
{
|
|
1787
|
+
data: message.metadata?.bookingConfirmation,
|
|
1788
|
+
theme
|
|
1789
|
+
}
|
|
1790
|
+
),
|
|
1791
|
+
message.messageType === "booking_cancelled" && /* @__PURE__ */ jsx8(
|
|
1792
|
+
BookingCancelledCard,
|
|
1793
|
+
{
|
|
1794
|
+
data: message.metadata?.bookingCancelled,
|
|
1795
|
+
theme
|
|
1796
|
+
}
|
|
1797
|
+
),
|
|
1798
|
+
showTimestamp && /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-1.5 mt-1 px-1", children: /* @__PURE__ */ jsx8(
|
|
1799
|
+
"span",
|
|
1800
|
+
{
|
|
1801
|
+
style: {
|
|
1802
|
+
fontSize: "11px",
|
|
1803
|
+
letterSpacing: "0.019em",
|
|
1804
|
+
color: textMuted
|
|
1805
|
+
},
|
|
1806
|
+
children: formatDistanceToNow(new Date(message.createdAt), { addSuffix: true })
|
|
1807
|
+
}
|
|
1808
|
+
) })
|
|
1809
|
+
] })
|
|
1810
|
+
] });
|
|
1811
|
+
}
|
|
1253
1812
|
if (isSystemMessage) {
|
|
1254
|
-
return /* @__PURE__ */
|
|
1813
|
+
return /* @__PURE__ */ jsx8("div", { className: "flex justify-center my-3", children: /* @__PURE__ */ jsx8(
|
|
1255
1814
|
"div",
|
|
1256
1815
|
{
|
|
1257
1816
|
className: "px-4 py-1.5 rounded-full",
|
|
@@ -1259,7 +1818,7 @@ function MessageItem({
|
|
|
1259
1818
|
backgroundColor: isLightTheme ? "rgba(0,0,0,0.04)" : "rgba(255,255,255,0.03)",
|
|
1260
1819
|
boxShadow: isLightTheme ? "inset 0 0 0 1px rgba(0,0,0,0.06)" : "inset 0 0 0 0.5px rgba(255,255,255,0.06)"
|
|
1261
1820
|
},
|
|
1262
|
-
children: /* @__PURE__ */
|
|
1821
|
+
children: /* @__PURE__ */ jsx8(
|
|
1263
1822
|
"p",
|
|
1264
1823
|
{
|
|
1265
1824
|
style: {
|
|
@@ -1298,12 +1857,12 @@ function MessageItem({
|
|
|
1298
1857
|
borderRadius: "18px 18px 18px 4px",
|
|
1299
1858
|
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.06), inset 0 1px 0 0 rgba(255,255,255,0.08)"
|
|
1300
1859
|
};
|
|
1301
|
-
return /* @__PURE__ */
|
|
1860
|
+
return /* @__PURE__ */ jsxs7(
|
|
1302
1861
|
"div",
|
|
1303
1862
|
{
|
|
1304
1863
|
className: `flex gap-2.5 mb-3 ${isOwnMessage ? "flex-row-reverse" : "flex-row"}`,
|
|
1305
1864
|
children: [
|
|
1306
|
-
showAvatar && !isOwnMessage && /* @__PURE__ */
|
|
1865
|
+
showAvatar && !isOwnMessage && /* @__PURE__ */ jsx8("div", { className: "flex-shrink-0 mt-auto mb-5", children: isBotMessage || isAIMessage ? /* @__PURE__ */ jsx8(XcelsiorAvatar, { size: 28 }) : /* @__PURE__ */ jsx8(
|
|
1307
1866
|
"div",
|
|
1308
1867
|
{
|
|
1309
1868
|
className: "h-7 w-7 rounded-full flex items-center justify-center",
|
|
@@ -1311,34 +1870,24 @@ function MessageItem({
|
|
|
1311
1870
|
background: isLightTheme ? `linear-gradient(135deg, ${primaryColor}30, rgba(0,0,0,0.04))` : `linear-gradient(135deg, ${primaryColor}60, rgba(255,255,255,0.06))`,
|
|
1312
1871
|
boxShadow: isLightTheme ? "inset 0 0 0 1px rgba(0,0,0,0.08)" : "inset 0 0 0 0.5px rgba(255,255,255,0.1)"
|
|
1313
1872
|
},
|
|
1314
|
-
children: /* @__PURE__ */
|
|
1315
|
-
|
|
1873
|
+
children: /* @__PURE__ */ jsx8(
|
|
1874
|
+
Headphones,
|
|
1316
1875
|
{
|
|
1317
|
-
|
|
1318
|
-
height: "14",
|
|
1319
|
-
viewBox: "0 0 24 24",
|
|
1320
|
-
fill: "none",
|
|
1876
|
+
size: 14,
|
|
1321
1877
|
stroke: isLightTheme ? primaryColor : "white",
|
|
1322
|
-
strokeWidth:
|
|
1323
|
-
|
|
1324
|
-
strokeLinejoin: "round",
|
|
1325
|
-
"aria-hidden": "true",
|
|
1326
|
-
children: [
|
|
1327
|
-
/* @__PURE__ */ jsx5("title", { children: "Agent" }),
|
|
1328
|
-
/* @__PURE__ */ jsx5("path", { d: "M3 18v-6a9 9 0 0 1 18 0v6" }),
|
|
1329
|
-
/* @__PURE__ */ jsx5("path", { d: "M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" })
|
|
1330
|
-
]
|
|
1878
|
+
strokeWidth: 2,
|
|
1879
|
+
"aria-hidden": "true"
|
|
1331
1880
|
}
|
|
1332
1881
|
)
|
|
1333
1882
|
}
|
|
1334
1883
|
) }),
|
|
1335
|
-
showAvatar && isOwnMessage && /* @__PURE__ */
|
|
1336
|
-
/* @__PURE__ */
|
|
1884
|
+
showAvatar && isOwnMessage && /* @__PURE__ */ jsx8("div", { className: "w-7 flex-shrink-0" }),
|
|
1885
|
+
/* @__PURE__ */ jsxs7(
|
|
1337
1886
|
"div",
|
|
1338
1887
|
{
|
|
1339
1888
|
className: `flex flex-col max-w-[75%] ${isOwnMessage ? "items-end" : "items-start"}`,
|
|
1340
1889
|
children: [
|
|
1341
|
-
senderLabel && /* @__PURE__ */
|
|
1890
|
+
senderLabel && /* @__PURE__ */ jsx8(
|
|
1342
1891
|
"span",
|
|
1343
1892
|
{
|
|
1344
1893
|
className: "mb-1 px-1 font-medium",
|
|
@@ -1350,7 +1899,7 @@ function MessageItem({
|
|
|
1350
1899
|
children: senderLabel
|
|
1351
1900
|
}
|
|
1352
1901
|
),
|
|
1353
|
-
/* @__PURE__ */
|
|
1902
|
+
/* @__PURE__ */ jsxs7(
|
|
1354
1903
|
"div",
|
|
1355
1904
|
{
|
|
1356
1905
|
className: "px-4 py-2.5",
|
|
@@ -1363,12 +1912,12 @@ function MessageItem({
|
|
|
1363
1912
|
children: [
|
|
1364
1913
|
message.messageType === "text" && (isOwnMessage ? (
|
|
1365
1914
|
// User messages: plain text — no markdown parsing
|
|
1366
|
-
/* @__PURE__ */
|
|
1915
|
+
/* @__PURE__ */ jsx8("span", { style: { whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: message.content })
|
|
1367
1916
|
) : (
|
|
1368
1917
|
// Bot / agent messages: full markdown rendering
|
|
1369
|
-
/* @__PURE__ */
|
|
1918
|
+
/* @__PURE__ */ jsx8(MarkdownMessage, { content: message.content, theme })
|
|
1370
1919
|
)),
|
|
1371
|
-
message.messageType === "image" && /* @__PURE__ */
|
|
1920
|
+
message.messageType === "image" && /* @__PURE__ */ jsx8("div", { children: /* @__PURE__ */ jsx8(
|
|
1372
1921
|
"img",
|
|
1373
1922
|
{
|
|
1374
1923
|
src: message.content,
|
|
@@ -1377,26 +1926,9 @@ function MessageItem({
|
|
|
1377
1926
|
loading: "lazy"
|
|
1378
1927
|
}
|
|
1379
1928
|
) }),
|
|
1380
|
-
message.messageType === "file" && /* @__PURE__ */
|
|
1381
|
-
/* @__PURE__ */
|
|
1382
|
-
|
|
1383
|
-
{
|
|
1384
|
-
width: "18",
|
|
1385
|
-
height: "18",
|
|
1386
|
-
viewBox: "0 0 24 24",
|
|
1387
|
-
fill: "none",
|
|
1388
|
-
stroke: "currentColor",
|
|
1389
|
-
strokeWidth: "1.75",
|
|
1390
|
-
strokeLinecap: "round",
|
|
1391
|
-
strokeLinejoin: "round",
|
|
1392
|
-
"aria-hidden": "true",
|
|
1393
|
-
children: [
|
|
1394
|
-
/* @__PURE__ */ jsx5("title", { children: "File" }),
|
|
1395
|
-
/* @__PURE__ */ jsx5("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" })
|
|
1396
|
-
]
|
|
1397
|
-
}
|
|
1398
|
-
),
|
|
1399
|
-
/* @__PURE__ */ jsx5(
|
|
1929
|
+
message.messageType === "file" && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
|
|
1930
|
+
/* @__PURE__ */ jsx8(Paperclip, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" }),
|
|
1931
|
+
/* @__PURE__ */ jsx8(
|
|
1400
1932
|
"a",
|
|
1401
1933
|
{
|
|
1402
1934
|
href: message.content,
|
|
@@ -1414,12 +1946,12 @@ function MessageItem({
|
|
|
1414
1946
|
]
|
|
1415
1947
|
}
|
|
1416
1948
|
),
|
|
1417
|
-
showTimestamp && /* @__PURE__ */
|
|
1949
|
+
showTimestamp && /* @__PURE__ */ jsxs7(
|
|
1418
1950
|
"div",
|
|
1419
1951
|
{
|
|
1420
1952
|
className: `flex items-center gap-1.5 mt-1 px-1 ${isOwnMessage ? "flex-row-reverse" : "flex-row"}`,
|
|
1421
1953
|
children: [
|
|
1422
|
-
/* @__PURE__ */
|
|
1954
|
+
/* @__PURE__ */ jsx8(
|
|
1423
1955
|
"span",
|
|
1424
1956
|
{
|
|
1425
1957
|
style: {
|
|
@@ -1432,63 +1964,10 @@ function MessageItem({
|
|
|
1432
1964
|
})
|
|
1433
1965
|
}
|
|
1434
1966
|
),
|
|
1435
|
-
isOwnMessage && message.status && /* @__PURE__ */
|
|
1436
|
-
message.status === "sent" && /* @__PURE__ */
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
width: "14",
|
|
1440
|
-
height: "14",
|
|
1441
|
-
viewBox: "0 0 24 24",
|
|
1442
|
-
fill: "none",
|
|
1443
|
-
stroke: "currentColor",
|
|
1444
|
-
strokeWidth: "2.5",
|
|
1445
|
-
strokeLinecap: "round",
|
|
1446
|
-
strokeLinejoin: "round",
|
|
1447
|
-
"aria-hidden": "true",
|
|
1448
|
-
children: [
|
|
1449
|
-
/* @__PURE__ */ jsx5("title", { children: "Sent" }),
|
|
1450
|
-
/* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" })
|
|
1451
|
-
]
|
|
1452
|
-
}
|
|
1453
|
-
),
|
|
1454
|
-
message.status === "delivered" && /* @__PURE__ */ jsxs4(
|
|
1455
|
-
"svg",
|
|
1456
|
-
{
|
|
1457
|
-
width: "14",
|
|
1458
|
-
height: "14",
|
|
1459
|
-
viewBox: "0 0 24 24",
|
|
1460
|
-
fill: "none",
|
|
1461
|
-
stroke: "currentColor",
|
|
1462
|
-
strokeWidth: "2.5",
|
|
1463
|
-
strokeLinecap: "round",
|
|
1464
|
-
strokeLinejoin: "round",
|
|
1465
|
-
"aria-hidden": "true",
|
|
1466
|
-
children: [
|
|
1467
|
-
/* @__PURE__ */ jsx5("title", { children: "Delivered" }),
|
|
1468
|
-
/* @__PURE__ */ jsx5("polyline", { points: "18 6 7 17 2 12" }),
|
|
1469
|
-
/* @__PURE__ */ jsx5("polyline", { points: "22 6 11 17" })
|
|
1470
|
-
]
|
|
1471
|
-
}
|
|
1472
|
-
),
|
|
1473
|
-
message.status === "read" && /* @__PURE__ */ jsxs4(
|
|
1474
|
-
"svg",
|
|
1475
|
-
{
|
|
1476
|
-
width: "14",
|
|
1477
|
-
height: "14",
|
|
1478
|
-
viewBox: "0 0 24 24",
|
|
1479
|
-
fill: "none",
|
|
1480
|
-
stroke: primaryColor,
|
|
1481
|
-
strokeWidth: "2.5",
|
|
1482
|
-
strokeLinecap: "round",
|
|
1483
|
-
strokeLinejoin: "round",
|
|
1484
|
-
"aria-hidden": "true",
|
|
1485
|
-
children: [
|
|
1486
|
-
/* @__PURE__ */ jsx5("title", { children: "Read" }),
|
|
1487
|
-
/* @__PURE__ */ jsx5("polyline", { points: "18 6 7 17 2 12" }),
|
|
1488
|
-
/* @__PURE__ */ jsx5("polyline", { points: "22 6 11 17" })
|
|
1489
|
-
]
|
|
1490
|
-
}
|
|
1491
|
-
)
|
|
1967
|
+
isOwnMessage && message.status && /* @__PURE__ */ jsxs7("span", { style: { color: textMuted }, children: [
|
|
1968
|
+
message.status === "sent" && /* @__PURE__ */ jsx8(Check, { size: 14, strokeWidth: 2.5, "aria-hidden": "true" }),
|
|
1969
|
+
message.status === "delivered" && /* @__PURE__ */ jsx8(CheckCheck, { size: 14, strokeWidth: 2.5, "aria-hidden": "true" }),
|
|
1970
|
+
message.status === "read" && /* @__PURE__ */ jsx8(CheckCheck, { size: 14, strokeWidth: 2.5, stroke: primaryColor, "aria-hidden": "true" })
|
|
1492
1971
|
] })
|
|
1493
1972
|
]
|
|
1494
1973
|
}
|
|
@@ -1502,8 +1981,8 @@ function MessageItem({
|
|
|
1502
1981
|
}
|
|
1503
1982
|
|
|
1504
1983
|
// src/components/ThinkingIndicator.tsx
|
|
1505
|
-
import { useEffect as useEffect5, useRef as useRef4, useState as
|
|
1506
|
-
import { jsx as
|
|
1984
|
+
import { useEffect as useEffect5, useRef as useRef4, useState as useState7 } from "react";
|
|
1985
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1507
1986
|
var PHRASE_POOLS = {
|
|
1508
1987
|
// ── Greetings & small talk ──
|
|
1509
1988
|
greeting: [
|
|
@@ -1686,9 +2165,9 @@ function ThinkingIndicator({
|
|
|
1686
2165
|
const isLightTheme = computeIsLightTheme2(theme?.background);
|
|
1687
2166
|
const primaryColor = theme?.primary || "#337eff";
|
|
1688
2167
|
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.4)" : "rgba(247,247,248,0.45)");
|
|
1689
|
-
const [phraseIndex, setPhraseIndex] =
|
|
1690
|
-
const [displayText, setDisplayText] =
|
|
1691
|
-
const [isDeleting, setIsDeleting] =
|
|
2168
|
+
const [phraseIndex, setPhraseIndex] = useState7(0);
|
|
2169
|
+
const [displayText, setDisplayText] = useState7("");
|
|
2170
|
+
const [isDeleting, setIsDeleting] = useState7(false);
|
|
1692
2171
|
const phrasesRef = useRef4(getShuffledPhrases(detectContext(lastUserMessage)));
|
|
1693
2172
|
useEffect5(() => {
|
|
1694
2173
|
phrasesRef.current = getShuffledPhrases(detectContext(lastUserMessage));
|
|
@@ -1722,7 +2201,7 @@ function ThinkingIndicator({
|
|
|
1722
2201
|
}
|
|
1723
2202
|
return () => clearTimeout(timeout);
|
|
1724
2203
|
}, [displayText, isDeleting, phraseIndex]);
|
|
1725
|
-
return /* @__PURE__ */
|
|
2204
|
+
return /* @__PURE__ */ jsxs8(
|
|
1726
2205
|
"div",
|
|
1727
2206
|
{
|
|
1728
2207
|
className: "flex gap-2.5 mb-3",
|
|
@@ -1730,8 +2209,8 @@ function ThinkingIndicator({
|
|
|
1730
2209
|
"aria-live": "polite",
|
|
1731
2210
|
"aria-label": "Xcelsior is thinking",
|
|
1732
2211
|
children: [
|
|
1733
|
-
showAvatar && /* @__PURE__ */
|
|
1734
|
-
/* @__PURE__ */
|
|
2212
|
+
showAvatar && /* @__PURE__ */ jsx9("div", { className: "flex-shrink-0 mt-auto mb-5", children: /* @__PURE__ */ jsx9(XcelsiorAvatar, { size: 28 }) }),
|
|
2213
|
+
/* @__PURE__ */ jsx9("div", { className: "flex flex-col items-start", children: /* @__PURE__ */ jsx9(
|
|
1735
2214
|
"div",
|
|
1736
2215
|
{
|
|
1737
2216
|
style: {
|
|
@@ -1741,8 +2220,8 @@ function ThinkingIndicator({
|
|
|
1741
2220
|
padding: "12px 16px",
|
|
1742
2221
|
minWidth: 160
|
|
1743
2222
|
},
|
|
1744
|
-
children: /* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
2223
|
+
children: /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
2224
|
+
/* @__PURE__ */ jsx9("div", { style: { display: "flex", gap: 4, alignItems: "center" }, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx9(
|
|
1746
2225
|
"span",
|
|
1747
2226
|
{
|
|
1748
2227
|
style: {
|
|
@@ -1756,7 +2235,7 @@ function ThinkingIndicator({
|
|
|
1756
2235
|
},
|
|
1757
2236
|
i
|
|
1758
2237
|
)) }),
|
|
1759
|
-
/* @__PURE__ */
|
|
2238
|
+
/* @__PURE__ */ jsxs8(
|
|
1760
2239
|
"span",
|
|
1761
2240
|
{
|
|
1762
2241
|
style: {
|
|
@@ -1767,7 +2246,7 @@ function ThinkingIndicator({
|
|
|
1767
2246
|
},
|
|
1768
2247
|
children: [
|
|
1769
2248
|
displayText,
|
|
1770
|
-
/* @__PURE__ */
|
|
2249
|
+
/* @__PURE__ */ jsx9(
|
|
1771
2250
|
"span",
|
|
1772
2251
|
{
|
|
1773
2252
|
style: {
|
|
@@ -1793,7 +2272,7 @@ function ThinkingIndicator({
|
|
|
1793
2272
|
}
|
|
1794
2273
|
|
|
1795
2274
|
// src/components/MessageList.tsx
|
|
1796
|
-
import { jsx as
|
|
2275
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1797
2276
|
function MessageList({
|
|
1798
2277
|
messages,
|
|
1799
2278
|
currentUser,
|
|
@@ -1806,7 +2285,8 @@ function MessageList({
|
|
|
1806
2285
|
isLoadingMore = false,
|
|
1807
2286
|
theme,
|
|
1808
2287
|
onQuickAction,
|
|
1809
|
-
isBotThinking = false
|
|
2288
|
+
isBotThinking = false,
|
|
2289
|
+
onBookingSlotSelected
|
|
1810
2290
|
}) {
|
|
1811
2291
|
const messagesEndRef = useRef5(null);
|
|
1812
2292
|
const containerRef = useRef5(null);
|
|
@@ -1859,7 +2339,7 @@ function MessageList({
|
|
|
1859
2339
|
prevScrollHeightRef.current = 0;
|
|
1860
2340
|
}
|
|
1861
2341
|
}, [isLoadingMore]);
|
|
1862
|
-
const handleScroll =
|
|
2342
|
+
const handleScroll = useCallback5(() => {
|
|
1863
2343
|
if (!containerRef.current || !onLoadMore || !hasMore || isLoadingMore) return;
|
|
1864
2344
|
if (!isUserScrollingRef.current) return;
|
|
1865
2345
|
const { scrollTop } = containerRef.current;
|
|
@@ -1874,11 +2354,11 @@ function MessageList({
|
|
|
1874
2354
|
return () => container.removeEventListener("scroll", handleScroll);
|
|
1875
2355
|
}, [handleScroll]);
|
|
1876
2356
|
if (isLoading) {
|
|
1877
|
-
return /* @__PURE__ */
|
|
2357
|
+
return /* @__PURE__ */ jsx10("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsx10(Spinner, { size: "lg" }) });
|
|
1878
2358
|
}
|
|
1879
2359
|
if (messages.length === 0) {
|
|
1880
|
-
return /* @__PURE__ */
|
|
1881
|
-
/* @__PURE__ */
|
|
2360
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center justify-center h-full text-center", style: { padding: "40px 32px" }, children: [
|
|
2361
|
+
/* @__PURE__ */ jsx10(
|
|
1882
2362
|
"h3",
|
|
1883
2363
|
{
|
|
1884
2364
|
className: "font-semibold mb-2",
|
|
@@ -1890,7 +2370,7 @@ function MessageList({
|
|
|
1890
2370
|
children: "How can we help?"
|
|
1891
2371
|
}
|
|
1892
2372
|
),
|
|
1893
|
-
/* @__PURE__ */
|
|
2373
|
+
/* @__PURE__ */ jsx10(
|
|
1894
2374
|
"p",
|
|
1895
2375
|
{
|
|
1896
2376
|
className: "max-w-[240px]",
|
|
@@ -1904,11 +2384,11 @@ function MessageList({
|
|
|
1904
2384
|
children: "Ask us anything. We are here to help you get the most out of Xcelsior."
|
|
1905
2385
|
}
|
|
1906
2386
|
),
|
|
1907
|
-
/* @__PURE__ */
|
|
2387
|
+
/* @__PURE__ */ jsx10("div", { className: "flex flex-wrap justify-center gap-2", children: [
|
|
1908
2388
|
{ label: "Our services", message: "What services does Xcelsior offer?" },
|
|
1909
2389
|
{ label: "Get a quote", message: "I would like to get a quote for a project" },
|
|
1910
2390
|
{ label: "Support", message: "I need help with something" }
|
|
1911
|
-
].map((action) => /* @__PURE__ */
|
|
2391
|
+
].map((action) => /* @__PURE__ */ jsx10(
|
|
1912
2392
|
"button",
|
|
1913
2393
|
{
|
|
1914
2394
|
type: "button",
|
|
@@ -1940,14 +2420,14 @@ function MessageList({
|
|
|
1940
2420
|
)) })
|
|
1941
2421
|
] });
|
|
1942
2422
|
}
|
|
1943
|
-
return /* @__PURE__ */
|
|
2423
|
+
return /* @__PURE__ */ jsxs9(
|
|
1944
2424
|
"div",
|
|
1945
2425
|
{
|
|
1946
2426
|
ref: containerRef,
|
|
1947
2427
|
className: "flex-1 overflow-y-auto px-4 py-3",
|
|
1948
2428
|
style: { scrollBehavior: "smooth" },
|
|
1949
2429
|
children: [
|
|
1950
|
-
/* @__PURE__ */
|
|
2430
|
+
/* @__PURE__ */ jsx10("style", { children: `
|
|
1951
2431
|
@keyframes thinkingPulse {
|
|
1952
2432
|
0%, 60%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
1953
2433
|
30% { opacity: 1; transform: scale(1); }
|
|
@@ -1957,23 +2437,24 @@ function MessageList({
|
|
|
1957
2437
|
50% { opacity: 0; }
|
|
1958
2438
|
}
|
|
1959
2439
|
` }),
|
|
1960
|
-
isLoadingMore && /* @__PURE__ */
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
messages.map((message) => /* @__PURE__ */
|
|
2440
|
+
isLoadingMore && /* @__PURE__ */ jsx10("div", { className: "flex justify-center py-3", children: /* @__PURE__ */ jsx10(Spinner, { size: "sm" }) }),
|
|
2441
|
+
/* @__PURE__ */ jsx10("div", { ref: loadMoreTriggerRef }),
|
|
2442
|
+
messages.map((message) => /* @__PURE__ */ jsx10(
|
|
1963
2443
|
MessageItem,
|
|
1964
2444
|
{
|
|
1965
2445
|
message,
|
|
1966
2446
|
currentUser,
|
|
1967
2447
|
showAvatar: true,
|
|
1968
2448
|
showTimestamp: true,
|
|
1969
|
-
theme
|
|
2449
|
+
theme,
|
|
2450
|
+
onBookingSlotSelected
|
|
1970
2451
|
},
|
|
1971
2452
|
message.id
|
|
1972
2453
|
)),
|
|
1973
|
-
isTyping && /* @__PURE__ */
|
|
1974
|
-
/* @__PURE__ */
|
|
1975
|
-
/* @__PURE__ */
|
|
1976
|
-
/* @__PURE__ */
|
|
2454
|
+
isTyping && /* @__PURE__ */ jsxs9("div", { className: "flex gap-2.5 mb-3", children: [
|
|
2455
|
+
/* @__PURE__ */ jsx10("div", { className: "flex-shrink-0 mt-auto mb-5", children: /* @__PURE__ */ jsx10(XcelsiorAvatar, { size: 28 }) }),
|
|
2456
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-start", children: [
|
|
2457
|
+
/* @__PURE__ */ jsx10(
|
|
1977
2458
|
"div",
|
|
1978
2459
|
{
|
|
1979
2460
|
className: "px-4 py-3",
|
|
@@ -1982,7 +2463,7 @@ function MessageList({
|
|
|
1982
2463
|
borderRadius: "18px 18px 18px 4px",
|
|
1983
2464
|
boxShadow: isLightTheme ? "inset 0 0 0 1px rgba(0,0,0,0.06)" : "inset 0 0 0 0.5px rgba(255,255,255,0.06), inset 0 1px 0 0 rgba(255,255,255,0.08)"
|
|
1984
2465
|
},
|
|
1985
|
-
children: /* @__PURE__ */
|
|
2466
|
+
children: /* @__PURE__ */ jsx10("div", { className: "flex gap-1.5 items-center", style: { height: 16 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx10(
|
|
1986
2467
|
"span",
|
|
1987
2468
|
{
|
|
1988
2469
|
className: "rounded-full animate-bounce",
|
|
@@ -1998,7 +2479,7 @@ function MessageList({
|
|
|
1998
2479
|
)) })
|
|
1999
2480
|
}
|
|
2000
2481
|
),
|
|
2001
|
-
typingUser && /* @__PURE__ */
|
|
2482
|
+
typingUser && /* @__PURE__ */ jsxs9(
|
|
2002
2483
|
"span",
|
|
2003
2484
|
{
|
|
2004
2485
|
className: "mt-1 px-1",
|
|
@@ -2015,24 +2496,24 @@ function MessageList({
|
|
|
2015
2496
|
)
|
|
2016
2497
|
] })
|
|
2017
2498
|
] }),
|
|
2018
|
-
isBotThinking && !isTyping && /* @__PURE__ */
|
|
2499
|
+
isBotThinking && !isTyping && /* @__PURE__ */ jsx10(
|
|
2019
2500
|
ThinkingIndicator,
|
|
2020
2501
|
{
|
|
2021
2502
|
theme,
|
|
2022
2503
|
lastUserMessage: messages.filter((m) => m.senderType === "customer").pop()?.content
|
|
2023
2504
|
}
|
|
2024
2505
|
),
|
|
2025
|
-
/* @__PURE__ */
|
|
2506
|
+
/* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
|
|
2026
2507
|
]
|
|
2027
2508
|
}
|
|
2028
2509
|
);
|
|
2029
2510
|
}
|
|
2030
2511
|
|
|
2031
2512
|
// src/components/ChatInput.tsx
|
|
2032
|
-
import { useEffect as useEffect7, useRef as useRef6, useState as
|
|
2513
|
+
import { useEffect as useEffect7, useRef as useRef6, useState as useState8 } from "react";
|
|
2033
2514
|
import { createPortal } from "react-dom";
|
|
2034
2515
|
import Picker from "@emoji-mart/react";
|
|
2035
|
-
import { Fragment as
|
|
2516
|
+
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2036
2517
|
function isLightColor(color) {
|
|
2037
2518
|
let r = 0, g = 0, b = 0;
|
|
2038
2519
|
if (color.startsWith("#")) {
|
|
@@ -2050,11 +2531,11 @@ function ChatInput({
|
|
|
2050
2531
|
fileUpload,
|
|
2051
2532
|
disabled = false
|
|
2052
2533
|
}) {
|
|
2053
|
-
const [message, setMessage] =
|
|
2054
|
-
const [showEmojiPicker, setShowEmojiPicker] =
|
|
2055
|
-
const [emojiData, setEmojiData] =
|
|
2056
|
-
const [emojiPickerPosition, setEmojiPickerPosition] =
|
|
2057
|
-
const [isFocused, setIsFocused] =
|
|
2534
|
+
const [message, setMessage] = useState8("");
|
|
2535
|
+
const [showEmojiPicker, setShowEmojiPicker] = useState8(false);
|
|
2536
|
+
const [emojiData, setEmojiData] = useState8();
|
|
2537
|
+
const [emojiPickerPosition, setEmojiPickerPosition] = useState8(null);
|
|
2538
|
+
const [isFocused, setIsFocused] = useState8(false);
|
|
2058
2539
|
const textAreaRef = useRef6(null);
|
|
2059
2540
|
const emojiPickerRef = useRef6(null);
|
|
2060
2541
|
const emojiButtonRef = useRef6(null);
|
|
@@ -2210,7 +2691,7 @@ ${uploadedFile.markdown}
|
|
|
2210
2691
|
};
|
|
2211
2692
|
const canSend = message.trim().length > 0 && !disabled;
|
|
2212
2693
|
const placeholderColor = isLightTheme ? "rgba(0,0,0,0.35)" : "rgba(247,247,248,0.3)";
|
|
2213
|
-
return /* @__PURE__ */
|
|
2694
|
+
return /* @__PURE__ */ jsxs10(
|
|
2214
2695
|
"div",
|
|
2215
2696
|
{
|
|
2216
2697
|
className: "px-4 py-3",
|
|
@@ -2218,7 +2699,7 @@ ${uploadedFile.markdown}
|
|
|
2218
2699
|
borderTop: isLightTheme ? "1px solid rgba(0,0,0,0.06)" : "1px solid rgba(255,255,255,0.06)"
|
|
2219
2700
|
},
|
|
2220
2701
|
children: [
|
|
2221
|
-
/* @__PURE__ */
|
|
2702
|
+
/* @__PURE__ */ jsxs10(
|
|
2222
2703
|
"div",
|
|
2223
2704
|
{
|
|
2224
2705
|
className: "relative flex items-center rounded-full transition-all duration-200",
|
|
@@ -2230,8 +2711,8 @@ ${uploadedFile.markdown}
|
|
|
2230
2711
|
backdropFilter: "blur(32px)"
|
|
2231
2712
|
},
|
|
2232
2713
|
children: [
|
|
2233
|
-
/* @__PURE__ */
|
|
2234
|
-
/* @__PURE__ */
|
|
2714
|
+
/* @__PURE__ */ jsx11("style", { children: `.xchat-input::placeholder { color: ${placeholderColor}; }` }),
|
|
2715
|
+
/* @__PURE__ */ jsx11(
|
|
2235
2716
|
"textarea",
|
|
2236
2717
|
{
|
|
2237
2718
|
ref: textAreaRef,
|
|
@@ -2260,8 +2741,8 @@ ${uploadedFile.markdown}
|
|
|
2260
2741
|
disabled
|
|
2261
2742
|
}
|
|
2262
2743
|
),
|
|
2263
|
-
/* @__PURE__ */
|
|
2264
|
-
enableEmoji && /* @__PURE__ */
|
|
2744
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 px-2 shrink-0", children: [
|
|
2745
|
+
enableEmoji && /* @__PURE__ */ jsx11(
|
|
2265
2746
|
"button",
|
|
2266
2747
|
{
|
|
2267
2748
|
ref: emojiButtonRef,
|
|
@@ -2291,7 +2772,7 @@ ${uploadedFile.markdown}
|
|
|
2291
2772
|
},
|
|
2292
2773
|
disabled,
|
|
2293
2774
|
"aria-label": "Add emoji",
|
|
2294
|
-
children: /* @__PURE__ */
|
|
2775
|
+
children: /* @__PURE__ */ jsxs10(
|
|
2295
2776
|
"svg",
|
|
2296
2777
|
{
|
|
2297
2778
|
width: "18",
|
|
@@ -2304,18 +2785,18 @@ ${uploadedFile.markdown}
|
|
|
2304
2785
|
strokeLinejoin: "round",
|
|
2305
2786
|
"aria-hidden": "true",
|
|
2306
2787
|
children: [
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
2309
|
-
/* @__PURE__ */
|
|
2310
|
-
/* @__PURE__ */
|
|
2311
|
-
/* @__PURE__ */
|
|
2788
|
+
/* @__PURE__ */ jsx11("title", { children: "Emoji" }),
|
|
2789
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2790
|
+
/* @__PURE__ */ jsx11("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
|
|
2791
|
+
/* @__PURE__ */ jsx11("line", { x1: "9", y1: "9", x2: "9.01", y2: "9" }),
|
|
2792
|
+
/* @__PURE__ */ jsx11("line", { x1: "15", y1: "9", x2: "15.01", y2: "9" })
|
|
2312
2793
|
]
|
|
2313
2794
|
}
|
|
2314
2795
|
)
|
|
2315
2796
|
}
|
|
2316
2797
|
),
|
|
2317
|
-
enableFileUpload && fileUpload.canUpload && /* @__PURE__ */
|
|
2318
|
-
/* @__PURE__ */
|
|
2798
|
+
enableFileUpload && fileUpload.canUpload && /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
2799
|
+
/* @__PURE__ */ jsx11(
|
|
2319
2800
|
"button",
|
|
2320
2801
|
{
|
|
2321
2802
|
type: "button",
|
|
@@ -2335,7 +2816,7 @@ ${uploadedFile.markdown}
|
|
|
2335
2816
|
},
|
|
2336
2817
|
disabled: disabled || fileUpload.isUploading,
|
|
2337
2818
|
"aria-label": "Attach file",
|
|
2338
|
-
children: fileUpload.isUploading ? /* @__PURE__ */
|
|
2819
|
+
children: fileUpload.isUploading ? /* @__PURE__ */ jsxs10(
|
|
2339
2820
|
"svg",
|
|
2340
2821
|
{
|
|
2341
2822
|
width: "18",
|
|
@@ -2347,11 +2828,11 @@ ${uploadedFile.markdown}
|
|
|
2347
2828
|
className: "animate-spin",
|
|
2348
2829
|
"aria-hidden": "true",
|
|
2349
2830
|
children: [
|
|
2350
|
-
/* @__PURE__ */
|
|
2351
|
-
/* @__PURE__ */
|
|
2831
|
+
/* @__PURE__ */ jsx11("title", { children: "Uploading" }),
|
|
2832
|
+
/* @__PURE__ */ jsx11("path", { d: "M21 12a9 9 0 11-6.219-8.56" })
|
|
2352
2833
|
]
|
|
2353
2834
|
}
|
|
2354
|
-
) : /* @__PURE__ */
|
|
2835
|
+
) : /* @__PURE__ */ jsxs10(
|
|
2355
2836
|
"svg",
|
|
2356
2837
|
{
|
|
2357
2838
|
width: "18",
|
|
@@ -2364,14 +2845,14 @@ ${uploadedFile.markdown}
|
|
|
2364
2845
|
strokeLinejoin: "round",
|
|
2365
2846
|
"aria-hidden": "true",
|
|
2366
2847
|
children: [
|
|
2367
|
-
/* @__PURE__ */
|
|
2368
|
-
/* @__PURE__ */
|
|
2848
|
+
/* @__PURE__ */ jsx11("title", { children: "Attach file" }),
|
|
2849
|
+
/* @__PURE__ */ jsx11("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" })
|
|
2369
2850
|
]
|
|
2370
2851
|
}
|
|
2371
2852
|
)
|
|
2372
2853
|
}
|
|
2373
2854
|
),
|
|
2374
|
-
/* @__PURE__ */
|
|
2855
|
+
/* @__PURE__ */ jsx11(
|
|
2375
2856
|
"input",
|
|
2376
2857
|
{
|
|
2377
2858
|
ref: fileInputRef,
|
|
@@ -2382,7 +2863,7 @@ ${uploadedFile.markdown}
|
|
|
2382
2863
|
}
|
|
2383
2864
|
)
|
|
2384
2865
|
] }),
|
|
2385
|
-
/* @__PURE__ */
|
|
2866
|
+
/* @__PURE__ */ jsx11(
|
|
2386
2867
|
"button",
|
|
2387
2868
|
{
|
|
2388
2869
|
type: "button",
|
|
@@ -2397,7 +2878,7 @@ ${uploadedFile.markdown}
|
|
|
2397
2878
|
boxShadow: canSend ? `0 2px 8px -2px ${primaryColor}60` : "none"
|
|
2398
2879
|
},
|
|
2399
2880
|
"aria-label": "Send message",
|
|
2400
|
-
children: /* @__PURE__ */
|
|
2881
|
+
children: /* @__PURE__ */ jsxs10(
|
|
2401
2882
|
"svg",
|
|
2402
2883
|
{
|
|
2403
2884
|
width: "18",
|
|
@@ -2410,9 +2891,9 @@ ${uploadedFile.markdown}
|
|
|
2410
2891
|
strokeLinejoin: "round",
|
|
2411
2892
|
"aria-hidden": "true",
|
|
2412
2893
|
children: [
|
|
2413
|
-
/* @__PURE__ */
|
|
2414
|
-
/* @__PURE__ */
|
|
2415
|
-
/* @__PURE__ */
|
|
2894
|
+
/* @__PURE__ */ jsx11("title", { children: "Send" }),
|
|
2895
|
+
/* @__PURE__ */ jsx11("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
2896
|
+
/* @__PURE__ */ jsx11("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
2416
2897
|
]
|
|
2417
2898
|
}
|
|
2418
2899
|
)
|
|
@@ -2422,8 +2903,8 @@ ${uploadedFile.markdown}
|
|
|
2422
2903
|
]
|
|
2423
2904
|
}
|
|
2424
2905
|
),
|
|
2425
|
-
fileUpload.isUploading && /* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2906
|
+
fileUpload.isUploading && /* @__PURE__ */ jsxs10("div", { className: "mt-2 px-1", children: [
|
|
2907
|
+
/* @__PURE__ */ jsx11(
|
|
2427
2908
|
"div",
|
|
2428
2909
|
{
|
|
2429
2910
|
className: "w-full rounded-full overflow-hidden",
|
|
@@ -2431,7 +2912,7 @@ ${uploadedFile.markdown}
|
|
|
2431
2912
|
height: 3,
|
|
2432
2913
|
backgroundColor: isLightTheme ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.06)"
|
|
2433
2914
|
},
|
|
2434
|
-
children: /* @__PURE__ */
|
|
2915
|
+
children: /* @__PURE__ */ jsx11(
|
|
2435
2916
|
"div",
|
|
2436
2917
|
{
|
|
2437
2918
|
className: "h-full rounded-full transition-all duration-300",
|
|
@@ -2443,7 +2924,7 @@ ${uploadedFile.markdown}
|
|
|
2443
2924
|
)
|
|
2444
2925
|
}
|
|
2445
2926
|
),
|
|
2446
|
-
/* @__PURE__ */
|
|
2927
|
+
/* @__PURE__ */ jsxs10(
|
|
2447
2928
|
"p",
|
|
2448
2929
|
{
|
|
2449
2930
|
className: "mt-1",
|
|
@@ -2461,7 +2942,7 @@ ${uploadedFile.markdown}
|
|
|
2461
2942
|
)
|
|
2462
2943
|
] }),
|
|
2463
2944
|
showEmojiPicker && emojiData && emojiPickerPosition && typeof document !== "undefined" && createPortal(
|
|
2464
|
-
/* @__PURE__ */
|
|
2945
|
+
/* @__PURE__ */ jsx11(
|
|
2465
2946
|
"div",
|
|
2466
2947
|
{
|
|
2467
2948
|
ref: emojiPickerRef,
|
|
@@ -2476,7 +2957,7 @@ ${uploadedFile.markdown}
|
|
|
2476
2957
|
"0 16px 48px -8px rgba(0,0,0,0.5)"
|
|
2477
2958
|
].join(", ")
|
|
2478
2959
|
},
|
|
2479
|
-
children: /* @__PURE__ */
|
|
2960
|
+
children: /* @__PURE__ */ jsx11(
|
|
2480
2961
|
Picker,
|
|
2481
2962
|
{
|
|
2482
2963
|
data: emojiData,
|
|
@@ -2502,7 +2983,7 @@ ${uploadedFile.markdown}
|
|
|
2502
2983
|
}
|
|
2503
2984
|
|
|
2504
2985
|
// src/components/ChatWidget.tsx
|
|
2505
|
-
import { jsx as
|
|
2986
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2506
2987
|
function getAnonymousUser() {
|
|
2507
2988
|
let anonId;
|
|
2508
2989
|
try {
|
|
@@ -2548,14 +3029,20 @@ function ChatWidget({
|
|
|
2548
3029
|
maxHeight: 900,
|
|
2549
3030
|
enabled: !isFullPage
|
|
2550
3031
|
});
|
|
2551
|
-
const
|
|
3032
|
+
const lastSentRef = useRef7(null);
|
|
3033
|
+
const handleSendMessage = useCallback6(
|
|
2552
3034
|
(content) => {
|
|
2553
3035
|
if (!websocket.isConnected) {
|
|
2554
3036
|
config.toast?.error("Not connected to chat server");
|
|
2555
3037
|
return;
|
|
2556
3038
|
}
|
|
3039
|
+
const now = Date.now();
|
|
3040
|
+
if (lastSentRef.current && lastSentRef.current.content === content && now - lastSentRef.current.time < 1e3) {
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
lastSentRef.current = { content, time: now };
|
|
2557
3044
|
const optimisticMessage = {
|
|
2558
|
-
id: `temp-${
|
|
3045
|
+
id: `temp-${now}`,
|
|
2559
3046
|
conversationId: config.conversationId || "",
|
|
2560
3047
|
senderId: effectiveUser.email,
|
|
2561
3048
|
senderType: effectiveUser.type,
|
|
@@ -2574,7 +3061,18 @@ function ChatWidget({
|
|
|
2574
3061
|
},
|
|
2575
3062
|
[websocket, config, addMessage, effectiveUser]
|
|
2576
3063
|
);
|
|
2577
|
-
const
|
|
3064
|
+
const handleBookingSlotSelected = useCallback6(
|
|
3065
|
+
(date, time, _messageId) => {
|
|
3066
|
+
if (!websocket.isConnected) return;
|
|
3067
|
+
websocket.sendMessage("bookSlot", {
|
|
3068
|
+
conversationId: config.conversationId,
|
|
3069
|
+
date,
|
|
3070
|
+
time
|
|
3071
|
+
});
|
|
3072
|
+
},
|
|
3073
|
+
[websocket, config]
|
|
3074
|
+
);
|
|
3075
|
+
const handleTyping = useCallback6(
|
|
2578
3076
|
(isTyping2) => {
|
|
2579
3077
|
if (!websocket.isConnected || config.enableTypingIndicator === false) return;
|
|
2580
3078
|
websocket.sendMessage("typing", {
|
|
@@ -2635,14 +3133,14 @@ function ChatWidget({
|
|
|
2635
3133
|
outlineOffset: -1,
|
|
2636
3134
|
transition: "outline 150ms ease"
|
|
2637
3135
|
};
|
|
2638
|
-
return /* @__PURE__ */
|
|
3136
|
+
return /* @__PURE__ */ jsxs11(
|
|
2639
3137
|
"div",
|
|
2640
3138
|
{
|
|
2641
3139
|
className: isFullPage ? `flex flex-col h-full ${className}` : `fixed bottom-20 ${positionClass} z-50 flex flex-col overflow-hidden ${className}`,
|
|
2642
3140
|
style: { ...containerStyle, ...dotGridBg, ...edgeHintStyle },
|
|
2643
3141
|
...!isFullPage ? containerResizeProps : {},
|
|
2644
3142
|
children: [
|
|
2645
|
-
!isFullPage && /* @__PURE__ */
|
|
3143
|
+
!isFullPage && /* @__PURE__ */ jsx12(
|
|
2646
3144
|
ChatHeader,
|
|
2647
3145
|
{
|
|
2648
3146
|
agent: effectiveUser.type === "customer" ? {
|
|
@@ -2656,7 +3154,7 @@ function ChatWidget({
|
|
|
2656
3154
|
theme: config.theme
|
|
2657
3155
|
}
|
|
2658
3156
|
),
|
|
2659
|
-
!websocket.isConnected && /* @__PURE__ */
|
|
3157
|
+
!websocket.isConnected && /* @__PURE__ */ jsx12(
|
|
2660
3158
|
"div",
|
|
2661
3159
|
{
|
|
2662
3160
|
className: "px-4 py-2",
|
|
@@ -2664,15 +3162,15 @@ function ChatWidget({
|
|
|
2664
3162
|
backgroundColor: isLightTheme ? "rgba(255,169,56,0.08)" : "rgba(255,169,56,0.06)",
|
|
2665
3163
|
borderBottom: `1px solid rgba(255,169,56,${isLightTheme ? "0.15" : "0.12"})`
|
|
2666
3164
|
},
|
|
2667
|
-
children: /* @__PURE__ */
|
|
2668
|
-
/* @__PURE__ */
|
|
3165
|
+
children: /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
|
|
3166
|
+
/* @__PURE__ */ jsx12(
|
|
2669
3167
|
"div",
|
|
2670
3168
|
{
|
|
2671
3169
|
className: "w-1.5 h-1.5 rounded-full animate-pulse",
|
|
2672
3170
|
style: { backgroundColor: config.theme?.statusCaution || "#ffa938" }
|
|
2673
3171
|
}
|
|
2674
3172
|
),
|
|
2675
|
-
/* @__PURE__ */
|
|
3173
|
+
/* @__PURE__ */ jsx12(
|
|
2676
3174
|
"span",
|
|
2677
3175
|
{
|
|
2678
3176
|
style: {
|
|
@@ -2683,7 +3181,7 @@ function ChatWidget({
|
|
|
2683
3181
|
children: "Reconnecting..."
|
|
2684
3182
|
}
|
|
2685
3183
|
),
|
|
2686
|
-
/* @__PURE__ */
|
|
3184
|
+
/* @__PURE__ */ jsx12(
|
|
2687
3185
|
"button",
|
|
2688
3186
|
{
|
|
2689
3187
|
type: "button",
|
|
@@ -2707,8 +3205,8 @@ function ChatWidget({
|
|
|
2707
3205
|
] })
|
|
2708
3206
|
}
|
|
2709
3207
|
),
|
|
2710
|
-
isLoading ? /* @__PURE__ */
|
|
2711
|
-
/* @__PURE__ */
|
|
3208
|
+
isLoading ? /* @__PURE__ */ jsx12("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs11("div", { className: "text-center", children: [
|
|
3209
|
+
/* @__PURE__ */ jsx12(
|
|
2712
3210
|
"div",
|
|
2713
3211
|
{
|
|
2714
3212
|
className: "w-7 h-7 border-2 border-t-transparent rounded-full animate-spin mx-auto mb-3",
|
|
@@ -2718,7 +3216,7 @@ function ChatWidget({
|
|
|
2718
3216
|
}
|
|
2719
3217
|
}
|
|
2720
3218
|
),
|
|
2721
|
-
/* @__PURE__ */
|
|
3219
|
+
/* @__PURE__ */ jsx12(
|
|
2722
3220
|
"p",
|
|
2723
3221
|
{
|
|
2724
3222
|
style: {
|
|
@@ -2729,7 +3227,7 @@ function ChatWidget({
|
|
|
2729
3227
|
children: "Loading messages..."
|
|
2730
3228
|
}
|
|
2731
3229
|
)
|
|
2732
|
-
] }) }) : /* @__PURE__ */
|
|
3230
|
+
] }) }) : /* @__PURE__ */ jsx12(
|
|
2733
3231
|
MessageList,
|
|
2734
3232
|
{
|
|
2735
3233
|
messages,
|
|
@@ -2742,10 +3240,11 @@ function ChatWidget({
|
|
|
2742
3240
|
isLoadingMore,
|
|
2743
3241
|
theme: config.theme,
|
|
2744
3242
|
onQuickAction: handleSendMessage,
|
|
2745
|
-
isBotThinking
|
|
3243
|
+
isBotThinking,
|
|
3244
|
+
onBookingSlotSelected: handleBookingSlotSelected
|
|
2746
3245
|
}
|
|
2747
3246
|
),
|
|
2748
|
-
/* @__PURE__ */
|
|
3247
|
+
/* @__PURE__ */ jsx12(
|
|
2749
3248
|
ChatInput,
|
|
2750
3249
|
{
|
|
2751
3250
|
onSend: handleSendMessage,
|
|
@@ -2755,7 +3254,7 @@ function ChatWidget({
|
|
|
2755
3254
|
disabled: !websocket.isConnected
|
|
2756
3255
|
}
|
|
2757
3256
|
),
|
|
2758
|
-
!isFullPage && /* @__PURE__ */
|
|
3257
|
+
!isFullPage && /* @__PURE__ */ jsx12(
|
|
2759
3258
|
"div",
|
|
2760
3259
|
{
|
|
2761
3260
|
className: "px-4 py-1.5 text-center",
|
|
@@ -2763,7 +3262,7 @@ function ChatWidget({
|
|
|
2763
3262
|
borderTop: isLightTheme ? "1px solid rgba(0,0,0,0.06)" : "1px solid rgba(255,255,255,0.06)",
|
|
2764
3263
|
backgroundColor: isLightTheme ? "rgba(0,0,0,0.03)" : "rgba(0,0,0,0.2)"
|
|
2765
3264
|
},
|
|
2766
|
-
children: /* @__PURE__ */
|
|
3265
|
+
children: /* @__PURE__ */ jsxs11(
|
|
2767
3266
|
"p",
|
|
2768
3267
|
{
|
|
2769
3268
|
style: {
|
|
@@ -2774,7 +3273,7 @@ function ChatWidget({
|
|
|
2774
3273
|
children: [
|
|
2775
3274
|
"Powered by",
|
|
2776
3275
|
" ",
|
|
2777
|
-
/* @__PURE__ */
|
|
3276
|
+
/* @__PURE__ */ jsx12("span", { style: {
|
|
2778
3277
|
fontWeight: 600,
|
|
2779
3278
|
color: isLightTheme ? "rgba(0,0,0,0.5)" : "rgba(247,247,248,0.45)"
|
|
2780
3279
|
}, children: "Xcelsior" })
|
|
@@ -2789,11 +3288,11 @@ function ChatWidget({
|
|
|
2789
3288
|
}
|
|
2790
3289
|
|
|
2791
3290
|
// src/components/Chat.tsx
|
|
2792
|
-
import { useCallback as
|
|
3291
|
+
import { useCallback as useCallback9, useEffect as useEffect10, useRef as useRef9, useState as useState12 } from "react";
|
|
2793
3292
|
|
|
2794
3293
|
// src/components/PreChatForm.tsx
|
|
2795
|
-
import { useState as
|
|
2796
|
-
import { jsx as
|
|
3294
|
+
import { useState as useState9 } from "react";
|
|
3295
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2797
3296
|
function PreChatForm({
|
|
2798
3297
|
onSubmit,
|
|
2799
3298
|
className = "",
|
|
@@ -2802,11 +3301,11 @@ function PreChatForm({
|
|
|
2802
3301
|
onMinimize,
|
|
2803
3302
|
onClose
|
|
2804
3303
|
}) {
|
|
2805
|
-
const [name, setName] =
|
|
2806
|
-
const [email, setEmail] =
|
|
2807
|
-
const [errors, setErrors] =
|
|
2808
|
-
const [isSubmitting, setIsSubmitting] =
|
|
2809
|
-
const [focusedField, setFocusedField] =
|
|
3304
|
+
const [name, setName] = useState9(initialName);
|
|
3305
|
+
const [email, setEmail] = useState9(initialEmail);
|
|
3306
|
+
const [errors, setErrors] = useState9({});
|
|
3307
|
+
const [isSubmitting, setIsSubmitting] = useState9(false);
|
|
3308
|
+
const [focusedField, setFocusedField] = useState9(null);
|
|
2810
3309
|
const validateForm = () => {
|
|
2811
3310
|
const newErrors = {};
|
|
2812
3311
|
if (!name.trim()) {
|
|
@@ -2854,7 +3353,7 @@ function PreChatForm({
|
|
|
2854
3353
|
transition: "box-shadow 0.2s ease",
|
|
2855
3354
|
caretColor: "#337eff"
|
|
2856
3355
|
});
|
|
2857
|
-
return /* @__PURE__ */
|
|
3356
|
+
return /* @__PURE__ */ jsxs12(
|
|
2858
3357
|
"div",
|
|
2859
3358
|
{
|
|
2860
3359
|
className: `fixed bottom-4 right-4 z-50 flex flex-col overflow-hidden ${className}`,
|
|
@@ -2874,7 +3373,7 @@ function PreChatForm({
|
|
|
2874
3373
|
backgroundSize: "24px 24px"
|
|
2875
3374
|
},
|
|
2876
3375
|
children: [
|
|
2877
|
-
/* @__PURE__ */
|
|
3376
|
+
/* @__PURE__ */ jsxs12(
|
|
2878
3377
|
"div",
|
|
2879
3378
|
{
|
|
2880
3379
|
className: "relative text-white overflow-hidden",
|
|
@@ -2882,7 +3381,7 @@ function PreChatForm({
|
|
|
2882
3381
|
background: "linear-gradient(135deg, #337eff 0%, #005eff 50%, #001a66 100%)"
|
|
2883
3382
|
},
|
|
2884
3383
|
children: [
|
|
2885
|
-
/* @__PURE__ */
|
|
3384
|
+
/* @__PURE__ */ jsx13(
|
|
2886
3385
|
"div",
|
|
2887
3386
|
{
|
|
2888
3387
|
className: "absolute inset-0 pointer-events-none",
|
|
@@ -2891,9 +3390,9 @@ function PreChatForm({
|
|
|
2891
3390
|
}
|
|
2892
3391
|
}
|
|
2893
3392
|
),
|
|
2894
|
-
/* @__PURE__ */
|
|
2895
|
-
/* @__PURE__ */
|
|
2896
|
-
/* @__PURE__ */
|
|
3393
|
+
/* @__PURE__ */ jsx13("div", { className: "relative px-5 py-4", children: /* @__PURE__ */ jsxs12("div", { className: "flex items-start justify-between", children: [
|
|
3394
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex-1", children: [
|
|
3395
|
+
/* @__PURE__ */ jsx13(
|
|
2897
3396
|
"h2",
|
|
2898
3397
|
{
|
|
2899
3398
|
className: "font-semibold",
|
|
@@ -2904,7 +3403,7 @@ function PreChatForm({
|
|
|
2904
3403
|
children: "Start a Conversation"
|
|
2905
3404
|
}
|
|
2906
3405
|
),
|
|
2907
|
-
/* @__PURE__ */
|
|
3406
|
+
/* @__PURE__ */ jsx13(
|
|
2908
3407
|
"p",
|
|
2909
3408
|
{
|
|
2910
3409
|
className: "mt-1",
|
|
@@ -2917,8 +3416,8 @@ function PreChatForm({
|
|
|
2917
3416
|
}
|
|
2918
3417
|
)
|
|
2919
3418
|
] }),
|
|
2920
|
-
/* @__PURE__ */
|
|
2921
|
-
/* @__PURE__ */
|
|
3419
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex gap-1 ml-2", children: [
|
|
3420
|
+
/* @__PURE__ */ jsx13(
|
|
2922
3421
|
"button",
|
|
2923
3422
|
{
|
|
2924
3423
|
type: "button",
|
|
@@ -2932,7 +3431,7 @@ function PreChatForm({
|
|
|
2932
3431
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
2933
3432
|
},
|
|
2934
3433
|
"aria-label": "Minimize chat",
|
|
2935
|
-
children: /* @__PURE__ */
|
|
3434
|
+
children: /* @__PURE__ */ jsxs12(
|
|
2936
3435
|
"svg",
|
|
2937
3436
|
{
|
|
2938
3437
|
width: "18",
|
|
@@ -2941,8 +3440,8 @@ function PreChatForm({
|
|
|
2941
3440
|
stroke: "currentColor",
|
|
2942
3441
|
viewBox: "0 0 24 24",
|
|
2943
3442
|
children: [
|
|
2944
|
-
/* @__PURE__ */
|
|
2945
|
-
/* @__PURE__ */
|
|
3443
|
+
/* @__PURE__ */ jsx13("title", { children: "Minimize" }),
|
|
3444
|
+
/* @__PURE__ */ jsx13(
|
|
2946
3445
|
"path",
|
|
2947
3446
|
{
|
|
2948
3447
|
strokeLinecap: "round",
|
|
@@ -2956,7 +3455,7 @@ function PreChatForm({
|
|
|
2956
3455
|
)
|
|
2957
3456
|
}
|
|
2958
3457
|
),
|
|
2959
|
-
/* @__PURE__ */
|
|
3458
|
+
/* @__PURE__ */ jsx13(
|
|
2960
3459
|
"button",
|
|
2961
3460
|
{
|
|
2962
3461
|
type: "button",
|
|
@@ -2970,7 +3469,7 @@ function PreChatForm({
|
|
|
2970
3469
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
2971
3470
|
},
|
|
2972
3471
|
"aria-label": "Close chat",
|
|
2973
|
-
children: /* @__PURE__ */
|
|
3472
|
+
children: /* @__PURE__ */ jsxs12(
|
|
2974
3473
|
"svg",
|
|
2975
3474
|
{
|
|
2976
3475
|
width: "18",
|
|
@@ -2979,8 +3478,8 @@ function PreChatForm({
|
|
|
2979
3478
|
stroke: "currentColor",
|
|
2980
3479
|
viewBox: "0 0 24 24",
|
|
2981
3480
|
children: [
|
|
2982
|
-
/* @__PURE__ */
|
|
2983
|
-
/* @__PURE__ */
|
|
3481
|
+
/* @__PURE__ */ jsx13("title", { children: "Close" }),
|
|
3482
|
+
/* @__PURE__ */ jsx13(
|
|
2984
3483
|
"path",
|
|
2985
3484
|
{
|
|
2986
3485
|
strokeLinecap: "round",
|
|
@@ -2996,7 +3495,7 @@ function PreChatForm({
|
|
|
2996
3495
|
)
|
|
2997
3496
|
] })
|
|
2998
3497
|
] }) }),
|
|
2999
|
-
/* @__PURE__ */
|
|
3498
|
+
/* @__PURE__ */ jsx13(
|
|
3000
3499
|
"div",
|
|
3001
3500
|
{
|
|
3002
3501
|
className: "h-px",
|
|
@@ -3008,9 +3507,9 @@ function PreChatForm({
|
|
|
3008
3507
|
]
|
|
3009
3508
|
}
|
|
3010
3509
|
),
|
|
3011
|
-
/* @__PURE__ */
|
|
3012
|
-
/* @__PURE__ */
|
|
3013
|
-
/* @__PURE__ */
|
|
3510
|
+
/* @__PURE__ */ jsxs12("form", { onSubmit: handleSubmit, className: "p-5 flex flex-col gap-4", children: [
|
|
3511
|
+
/* @__PURE__ */ jsxs12("div", { children: [
|
|
3512
|
+
/* @__PURE__ */ jsxs12(
|
|
3014
3513
|
"label",
|
|
3015
3514
|
{
|
|
3016
3515
|
htmlFor: "chat-name",
|
|
@@ -3022,11 +3521,11 @@ function PreChatForm({
|
|
|
3022
3521
|
},
|
|
3023
3522
|
children: [
|
|
3024
3523
|
"Name ",
|
|
3025
|
-
/* @__PURE__ */
|
|
3524
|
+
/* @__PURE__ */ jsx13("span", { style: { color: "#ff6363" }, children: "*" })
|
|
3026
3525
|
]
|
|
3027
3526
|
}
|
|
3028
3527
|
),
|
|
3029
|
-
/* @__PURE__ */
|
|
3528
|
+
/* @__PURE__ */ jsx13(
|
|
3030
3529
|
"input",
|
|
3031
3530
|
{
|
|
3032
3531
|
type: "text",
|
|
@@ -3046,7 +3545,7 @@ function PreChatForm({
|
|
|
3046
3545
|
autoComplete: "name"
|
|
3047
3546
|
}
|
|
3048
3547
|
),
|
|
3049
|
-
errors.name && /* @__PURE__ */
|
|
3548
|
+
errors.name && /* @__PURE__ */ jsx13(
|
|
3050
3549
|
"p",
|
|
3051
3550
|
{
|
|
3052
3551
|
className: "mt-1.5",
|
|
@@ -3060,8 +3559,8 @@ function PreChatForm({
|
|
|
3060
3559
|
}
|
|
3061
3560
|
)
|
|
3062
3561
|
] }),
|
|
3063
|
-
/* @__PURE__ */
|
|
3064
|
-
/* @__PURE__ */
|
|
3562
|
+
/* @__PURE__ */ jsxs12("div", { children: [
|
|
3563
|
+
/* @__PURE__ */ jsxs12(
|
|
3065
3564
|
"label",
|
|
3066
3565
|
{
|
|
3067
3566
|
htmlFor: "chat-email",
|
|
@@ -3073,11 +3572,11 @@ function PreChatForm({
|
|
|
3073
3572
|
},
|
|
3074
3573
|
children: [
|
|
3075
3574
|
"Email ",
|
|
3076
|
-
/* @__PURE__ */
|
|
3575
|
+
/* @__PURE__ */ jsx13("span", { style: { color: "#ff6363" }, children: "*" })
|
|
3077
3576
|
]
|
|
3078
3577
|
}
|
|
3079
3578
|
),
|
|
3080
|
-
/* @__PURE__ */
|
|
3579
|
+
/* @__PURE__ */ jsx13(
|
|
3081
3580
|
"input",
|
|
3082
3581
|
{
|
|
3083
3582
|
type: "email",
|
|
@@ -3097,7 +3596,7 @@ function PreChatForm({
|
|
|
3097
3596
|
autoComplete: "email"
|
|
3098
3597
|
}
|
|
3099
3598
|
),
|
|
3100
|
-
errors.email && /* @__PURE__ */
|
|
3599
|
+
errors.email && /* @__PURE__ */ jsx13(
|
|
3101
3600
|
"p",
|
|
3102
3601
|
{
|
|
3103
3602
|
className: "mt-1.5",
|
|
@@ -3111,7 +3610,7 @@ function PreChatForm({
|
|
|
3111
3610
|
}
|
|
3112
3611
|
)
|
|
3113
3612
|
] }),
|
|
3114
|
-
/* @__PURE__ */
|
|
3613
|
+
/* @__PURE__ */ jsx13(
|
|
3115
3614
|
"button",
|
|
3116
3615
|
{
|
|
3117
3616
|
type: "submit",
|
|
@@ -3133,8 +3632,8 @@ function PreChatForm({
|
|
|
3133
3632
|
onMouseLeave: (e) => {
|
|
3134
3633
|
e.currentTarget.style.boxShadow = "0 4px 16px -4px rgba(51,126,255,0.4)";
|
|
3135
3634
|
},
|
|
3136
|
-
children: isSubmitting ? /* @__PURE__ */
|
|
3137
|
-
/* @__PURE__ */
|
|
3635
|
+
children: isSubmitting ? /* @__PURE__ */ jsxs12("span", { className: "flex items-center justify-center gap-2", children: [
|
|
3636
|
+
/* @__PURE__ */ jsxs12(
|
|
3138
3637
|
"svg",
|
|
3139
3638
|
{
|
|
3140
3639
|
className: "animate-spin",
|
|
@@ -3146,8 +3645,8 @@ function PreChatForm({
|
|
|
3146
3645
|
strokeWidth: "2",
|
|
3147
3646
|
"aria-label": "Loading",
|
|
3148
3647
|
children: [
|
|
3149
|
-
/* @__PURE__ */
|
|
3150
|
-
/* @__PURE__ */
|
|
3648
|
+
/* @__PURE__ */ jsx13("title", { children: "Loading" }),
|
|
3649
|
+
/* @__PURE__ */ jsx13("path", { d: "M21 12a9 9 0 11-6.219-8.56" })
|
|
3151
3650
|
]
|
|
3152
3651
|
}
|
|
3153
3652
|
),
|
|
@@ -3155,7 +3654,7 @@ function PreChatForm({
|
|
|
3155
3654
|
] }) : "Start Chat"
|
|
3156
3655
|
}
|
|
3157
3656
|
),
|
|
3158
|
-
/* @__PURE__ */
|
|
3657
|
+
/* @__PURE__ */ jsx13(
|
|
3159
3658
|
"p",
|
|
3160
3659
|
{
|
|
3161
3660
|
className: "text-center",
|
|
@@ -3168,7 +3667,7 @@ function PreChatForm({
|
|
|
3168
3667
|
}
|
|
3169
3668
|
)
|
|
3170
3669
|
] }),
|
|
3171
|
-
/* @__PURE__ */
|
|
3670
|
+
/* @__PURE__ */ jsx13(
|
|
3172
3671
|
"div",
|
|
3173
3672
|
{
|
|
3174
3673
|
className: "px-4 py-1.5 text-center",
|
|
@@ -3176,7 +3675,7 @@ function PreChatForm({
|
|
|
3176
3675
|
borderTop: "1px solid rgba(255,255,255,0.06)",
|
|
3177
3676
|
backgroundColor: "rgba(0,0,0,0.2)"
|
|
3178
3677
|
},
|
|
3179
|
-
children: /* @__PURE__ */
|
|
3678
|
+
children: /* @__PURE__ */ jsxs12(
|
|
3180
3679
|
"p",
|
|
3181
3680
|
{
|
|
3182
3681
|
style: {
|
|
@@ -3187,7 +3686,7 @@ function PreChatForm({
|
|
|
3187
3686
|
children: [
|
|
3188
3687
|
"Powered by",
|
|
3189
3688
|
" ",
|
|
3190
|
-
/* @__PURE__ */
|
|
3689
|
+
/* @__PURE__ */ jsx13("span", { style: { fontWeight: 600, color: "rgba(247,247,248,0.45)" }, children: "Xcelsior" })
|
|
3191
3690
|
]
|
|
3192
3691
|
}
|
|
3193
3692
|
)
|
|
@@ -3199,8 +3698,15 @@ function PreChatForm({
|
|
|
3199
3698
|
}
|
|
3200
3699
|
|
|
3201
3700
|
// src/hooks/useDraggablePosition.ts
|
|
3202
|
-
import { useState as
|
|
3701
|
+
import { useState as useState10, useCallback as useCallback7, useRef as useRef8, useEffect as useEffect9 } from "react";
|
|
3203
3702
|
var STORAGE_KEY2 = "xcelsior-chat-position";
|
|
3703
|
+
var DRAG_THRESHOLD = 5;
|
|
3704
|
+
var FAB_SIZE = 64;
|
|
3705
|
+
var MARGIN = 16;
|
|
3706
|
+
var BOTTOM = 20;
|
|
3707
|
+
var VELOCITY_THRESHOLD = 0.4;
|
|
3708
|
+
var SETTLE_DURATION = 400;
|
|
3709
|
+
var SETTLE_EASING = "cubic-bezier(0.2, 0.9, 0.3, 1.1)";
|
|
3204
3710
|
function getStoredPosition() {
|
|
3205
3711
|
try {
|
|
3206
3712
|
const stored = localStorage.getItem(STORAGE_KEY2);
|
|
@@ -3209,20 +3715,32 @@ function getStoredPosition() {
|
|
|
3209
3715
|
}
|
|
3210
3716
|
return "right";
|
|
3211
3717
|
}
|
|
3212
|
-
function storePosition(
|
|
3718
|
+
function storePosition(pos) {
|
|
3213
3719
|
try {
|
|
3214
|
-
localStorage.setItem(STORAGE_KEY2,
|
|
3720
|
+
localStorage.setItem(STORAGE_KEY2, pos);
|
|
3215
3721
|
} catch {
|
|
3216
3722
|
}
|
|
3217
3723
|
}
|
|
3724
|
+
function restingX(side) {
|
|
3725
|
+
return side === "left" ? MARGIN : window.innerWidth - MARGIN - FAB_SIZE;
|
|
3726
|
+
}
|
|
3727
|
+
function restingY() {
|
|
3728
|
+
return window.innerHeight - BOTTOM - FAB_SIZE;
|
|
3729
|
+
}
|
|
3218
3730
|
function useDraggablePosition(configPosition = "auto") {
|
|
3219
3731
|
const resolvedDefault = configPosition === "auto" ? getStoredPosition() : configPosition;
|
|
3220
|
-
const [position, setPosition] =
|
|
3221
|
-
const
|
|
3222
|
-
|
|
3223
|
-
const
|
|
3224
|
-
const
|
|
3225
|
-
const
|
|
3732
|
+
const [position, setPosition] = useState10(resolvedDefault);
|
|
3733
|
+
const positionRef = useRef8(position);
|
|
3734
|
+
positionRef.current = position;
|
|
3735
|
+
const [isDragging, setIsDragging] = useState10(false);
|
|
3736
|
+
const containerRef = useRef8(null);
|
|
3737
|
+
const draggingRef = useRef8(false);
|
|
3738
|
+
const didDragRef = useRef8(false);
|
|
3739
|
+
const startPointer = useRef8({ x: 0, y: 0 });
|
|
3740
|
+
const startRect = useRef8({ x: 0, y: 0 });
|
|
3741
|
+
const samples = useRef8([]);
|
|
3742
|
+
const hasShownHint = useRef8(false);
|
|
3743
|
+
const [showHint, setShowHint] = useState10(false);
|
|
3226
3744
|
useEffect9(() => {
|
|
3227
3745
|
try {
|
|
3228
3746
|
const hasVisited = localStorage.getItem("xcelsior-chat-hint-shown");
|
|
@@ -3238,53 +3756,115 @@ function useDraggablePosition(configPosition = "auto") {
|
|
|
3238
3756
|
} catch {
|
|
3239
3757
|
}
|
|
3240
3758
|
}, []);
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3759
|
+
useEffect9(() => {
|
|
3760
|
+
const handleMove = (e) => {
|
|
3761
|
+
if (!draggingRef.current) return;
|
|
3762
|
+
const el = containerRef.current;
|
|
3763
|
+
if (!el) return;
|
|
3764
|
+
const dx = e.clientX - startPointer.current.x;
|
|
3765
|
+
const dy = e.clientY - startPointer.current.y;
|
|
3766
|
+
if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {
|
|
3767
|
+
didDragRef.current = true;
|
|
3768
|
+
}
|
|
3769
|
+
el.style.transition = "none";
|
|
3770
|
+
el.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
3771
|
+
const now = performance.now();
|
|
3772
|
+
samples.current.push({ x: e.clientX, t: now });
|
|
3773
|
+
if (samples.current.length > 5) samples.current.shift();
|
|
3774
|
+
};
|
|
3775
|
+
const handleUp = (e) => {
|
|
3776
|
+
if (!draggingRef.current) return;
|
|
3777
|
+
draggingRef.current = false;
|
|
3778
|
+
const el = containerRef.current;
|
|
3779
|
+
if (!el) return;
|
|
3780
|
+
let velocityX = 0;
|
|
3781
|
+
const s = samples.current;
|
|
3782
|
+
if (s.length >= 2) {
|
|
3783
|
+
const first = s[0];
|
|
3784
|
+
const last = s[s.length - 1];
|
|
3785
|
+
const dt = last.t - first.t;
|
|
3786
|
+
if (dt > 0) velocityX = (last.x - first.x) / dt;
|
|
3787
|
+
}
|
|
3788
|
+
samples.current = [];
|
|
3789
|
+
const currentVisualX = startRect.current.x + (e.clientX - startPointer.current.x);
|
|
3790
|
+
const screenMid = window.innerWidth / 2;
|
|
3791
|
+
let targetSide;
|
|
3792
|
+
if (Math.abs(velocityX) > VELOCITY_THRESHOLD) {
|
|
3793
|
+
targetSide = velocityX < 0 ? "left" : "right";
|
|
3794
|
+
} else {
|
|
3795
|
+
targetSide = currentVisualX + FAB_SIZE / 2 < screenMid ? "left" : "right";
|
|
3796
|
+
}
|
|
3797
|
+
const oldBaseX = restingX(positionRef.current);
|
|
3798
|
+
const oldBaseY = restingY();
|
|
3799
|
+
const currentX = startRect.current.x + (e.clientX - startPointer.current.x);
|
|
3800
|
+
const currentY = startRect.current.y + (e.clientY - startPointer.current.y);
|
|
3801
|
+
const targetX = restingX(targetSide);
|
|
3802
|
+
const targetY = restingY();
|
|
3803
|
+
const fromTx = currentX - oldBaseX;
|
|
3804
|
+
const fromTy = currentY - oldBaseY;
|
|
3805
|
+
const toTx = targetX - oldBaseX;
|
|
3806
|
+
const toTy = targetY - oldBaseY;
|
|
3807
|
+
el.style.transition = "none";
|
|
3808
|
+
el.style.transform = `translate(${fromTx}px, ${fromTy}px)`;
|
|
3809
|
+
void el.offsetHeight;
|
|
3810
|
+
el.style.transition = `transform ${SETTLE_DURATION}ms ${SETTLE_EASING}`;
|
|
3811
|
+
el.style.transform = `translate(${toTx}px, ${toTy}px)`;
|
|
3812
|
+
const finish = () => {
|
|
3813
|
+
el.style.transition = "";
|
|
3814
|
+
el.style.transform = "";
|
|
3815
|
+
el.removeEventListener("transitionend", finish);
|
|
3816
|
+
setIsDragging(false);
|
|
3817
|
+
if (targetSide !== positionRef.current) {
|
|
3818
|
+
setPosition(targetSide);
|
|
3819
|
+
storePosition(targetSide);
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3822
|
+
el.addEventListener("transitionend", finish, { once: true });
|
|
3823
|
+
setTimeout(finish, SETTLE_DURATION + 50);
|
|
3824
|
+
};
|
|
3825
|
+
document.addEventListener("pointermove", handleMove);
|
|
3826
|
+
document.addEventListener("pointerup", handleUp);
|
|
3827
|
+
return () => {
|
|
3828
|
+
document.removeEventListener("pointermove", handleMove);
|
|
3829
|
+
document.removeEventListener("pointerup", handleUp);
|
|
3830
|
+
};
|
|
3245
3831
|
}, []);
|
|
3246
|
-
const
|
|
3832
|
+
const handlePointerDown = useCallback7((e) => {
|
|
3833
|
+
e.preventDefault();
|
|
3834
|
+
const el = containerRef.current;
|
|
3835
|
+
if (!el) return;
|
|
3836
|
+
const rect = el.getBoundingClientRect();
|
|
3837
|
+
startPointer.current = { x: e.clientX, y: e.clientY };
|
|
3838
|
+
startRect.current = { x: rect.left, y: rect.top };
|
|
3839
|
+
draggingRef.current = true;
|
|
3840
|
+
didDragRef.current = false;
|
|
3841
|
+
samples.current = [];
|
|
3842
|
+
setIsDragging(true);
|
|
3247
3843
|
}, []);
|
|
3248
|
-
const
|
|
3249
|
-
(e) => {
|
|
3250
|
-
setIsDragging(false);
|
|
3251
|
-
e.target.releasePointerCapture(e.pointerId);
|
|
3252
|
-
const deltaX = e.clientX - dragStartX.current;
|
|
3253
|
-
if (Math.abs(deltaX) > 50) {
|
|
3254
|
-
const screenMid = window.innerWidth / 2;
|
|
3255
|
-
const newPosition = e.clientX < screenMid ? "left" : "right";
|
|
3256
|
-
if (newPosition !== position) {
|
|
3257
|
-
setPosition(newPosition);
|
|
3258
|
-
storePosition(newPosition);
|
|
3259
|
-
}
|
|
3260
|
-
}
|
|
3261
|
-
},
|
|
3262
|
-
[position]
|
|
3263
|
-
);
|
|
3844
|
+
const shouldSuppressClick = useCallback7(() => didDragRef.current, []);
|
|
3264
3845
|
return {
|
|
3265
3846
|
position,
|
|
3266
3847
|
isDragging,
|
|
3267
3848
|
showHint,
|
|
3268
|
-
|
|
3849
|
+
containerRef,
|
|
3850
|
+
shouldSuppressClick,
|
|
3269
3851
|
handlers: {
|
|
3270
|
-
onPointerDown: handlePointerDown
|
|
3271
|
-
onPointerMove: handlePointerMove,
|
|
3272
|
-
onPointerUp: handlePointerUp
|
|
3852
|
+
onPointerDown: handlePointerDown
|
|
3273
3853
|
}
|
|
3274
3854
|
};
|
|
3275
3855
|
}
|
|
3276
3856
|
|
|
3277
3857
|
// src/hooks/useChatWidgetState.ts
|
|
3278
|
-
import { useCallback as
|
|
3858
|
+
import { useCallback as useCallback8, useState as useState11 } from "react";
|
|
3279
3859
|
function useChatWidgetState({
|
|
3280
3860
|
state: controlledState,
|
|
3281
3861
|
defaultState = "minimized",
|
|
3282
3862
|
onStateChange
|
|
3283
3863
|
}) {
|
|
3284
|
-
const [uncontrolledState, setUncontrolledState] =
|
|
3864
|
+
const [uncontrolledState, setUncontrolledState] = useState11(defaultState);
|
|
3285
3865
|
const isControlled = controlledState !== void 0 && controlledState !== "undefined";
|
|
3286
3866
|
const currentState = isControlled ? controlledState : uncontrolledState;
|
|
3287
|
-
const setState =
|
|
3867
|
+
const setState = useCallback8(
|
|
3288
3868
|
(newValue) => {
|
|
3289
3869
|
if (!isControlled) {
|
|
3290
3870
|
setUncontrolledState(newValue);
|
|
@@ -3301,7 +3881,7 @@ function useChatWidgetState({
|
|
|
3301
3881
|
}
|
|
3302
3882
|
|
|
3303
3883
|
// src/components/Chat.tsx
|
|
3304
|
-
import { jsx as
|
|
3884
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3305
3885
|
function generateSessionId() {
|
|
3306
3886
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
3307
3887
|
return crypto.randomUUID();
|
|
@@ -3317,12 +3897,12 @@ function Chat({
|
|
|
3317
3897
|
defaultState = "minimized",
|
|
3318
3898
|
onStateChange
|
|
3319
3899
|
}) {
|
|
3320
|
-
const [userInfo, setUserInfo] =
|
|
3321
|
-
const [conversationId, setConversationId] =
|
|
3322
|
-
const [isLoading, setIsLoading] =
|
|
3900
|
+
const [userInfo, setUserInfo] = useState12(null);
|
|
3901
|
+
const [conversationId, setConversationId] = useState12("");
|
|
3902
|
+
const [isLoading, setIsLoading] = useState12(true);
|
|
3323
3903
|
const identityMode = config.identityCollection || "progressive";
|
|
3324
|
-
const { position, isDragging, showHint, handlers } = useDraggablePosition(config.position);
|
|
3325
|
-
const sessionInitializedRef =
|
|
3904
|
+
const { position, isDragging, showHint, containerRef, shouldSuppressClick, handlers } = useDraggablePosition(config.position);
|
|
3905
|
+
const sessionInitializedRef = useRef9(false);
|
|
3326
3906
|
const { currentState, setState } = useChatWidgetState({
|
|
3327
3907
|
state,
|
|
3328
3908
|
defaultState,
|
|
@@ -3373,7 +3953,33 @@ function Chat({
|
|
|
3373
3953
|
const convId = configConversationId || generateSessionId();
|
|
3374
3954
|
setConversationId(convId);
|
|
3375
3955
|
if (identityMode === "progressive" || identityMode === "none") {
|
|
3376
|
-
|
|
3956
|
+
let anonId;
|
|
3957
|
+
try {
|
|
3958
|
+
const stored = localStorage.getItem("xcelsior-chat-anon-id");
|
|
3959
|
+
if (stored) {
|
|
3960
|
+
anonId = stored;
|
|
3961
|
+
} else {
|
|
3962
|
+
anonId = `anon-${crypto.randomUUID?.() || Math.random().toString(36).slice(2)}`;
|
|
3963
|
+
localStorage.setItem("xcelsior-chat-anon-id", anonId);
|
|
3964
|
+
}
|
|
3965
|
+
} catch {
|
|
3966
|
+
anonId = `anon-${Math.random().toString(36).slice(2)}`;
|
|
3967
|
+
}
|
|
3968
|
+
const anonEmail = `${anonId}@anonymous.xcelsior.co`;
|
|
3969
|
+
const anonUser = { name: "Visitor", email: anonEmail, type: "customer", status: "online" };
|
|
3970
|
+
setUserInfo(anonUser);
|
|
3971
|
+
try {
|
|
3972
|
+
localStorage.setItem(
|
|
3973
|
+
`${storageKeyPrefix}_user`,
|
|
3974
|
+
JSON.stringify({
|
|
3975
|
+
name: anonUser.name,
|
|
3976
|
+
email: anonUser.email,
|
|
3977
|
+
conversationId: convId,
|
|
3978
|
+
timestamp: Date.now()
|
|
3979
|
+
})
|
|
3980
|
+
);
|
|
3981
|
+
} catch {
|
|
3982
|
+
}
|
|
3377
3983
|
}
|
|
3378
3984
|
sessionInitializedRef.current = true;
|
|
3379
3985
|
} catch (error) {
|
|
@@ -3386,7 +3992,7 @@ function Chat({
|
|
|
3386
3992
|
};
|
|
3387
3993
|
initializeSession();
|
|
3388
3994
|
}, [configConversationId, configUserEmail, configUserName, configUserAvatar, configUserStatus, storageKeyPrefix, identityMode]);
|
|
3389
|
-
const handlePreChatSubmit =
|
|
3995
|
+
const handlePreChatSubmit = useCallback9(
|
|
3390
3996
|
(name, email) => {
|
|
3391
3997
|
const convId = conversationId || generateSessionId();
|
|
3392
3998
|
const user = { name, email, type: "customer", status: "online" };
|
|
@@ -3412,49 +4018,52 @@ function Chat({
|
|
|
3412
4018
|
const primaryColor = config.theme?.primary || "#337eff";
|
|
3413
4019
|
const primaryStrong = config.theme?.primaryStrong || "#005eff";
|
|
3414
4020
|
if (currentState === "minimized") {
|
|
3415
|
-
return /* @__PURE__ */
|
|
3416
|
-
"
|
|
4021
|
+
return /* @__PURE__ */ jsx14(
|
|
4022
|
+
"div",
|
|
3417
4023
|
{
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
4024
|
+
ref: containerRef,
|
|
4025
|
+
className: `fixed bottom-5 ${positionClass} z-50 ${className}`,
|
|
4026
|
+
children: /* @__PURE__ */ jsxs13(
|
|
4027
|
+
"button",
|
|
4028
|
+
{
|
|
4029
|
+
type: "button",
|
|
4030
|
+
onClick: () => {
|
|
4031
|
+
if (!shouldSuppressClick()) setState("open");
|
|
4032
|
+
},
|
|
4033
|
+
className: `group relative rounded-full text-white flex items-center justify-center touch-none select-none ${showHint ? "animate-bounce" : ""} ${isDragging ? "cursor-grabbing scale-110" : "cursor-grab hover:scale-[1.08] transition-transform duration-300"}`,
|
|
4034
|
+
style: {
|
|
4035
|
+
width: 64,
|
|
4036
|
+
height: 64,
|
|
4037
|
+
background: `linear-gradient(135deg, ${primaryColor}, ${primaryStrong})`,
|
|
4038
|
+
boxShadow: [
|
|
4039
|
+
`0 4px 24px 0 ${primaryColor}80`,
|
|
4040
|
+
`0 8px 32px -4px rgba(0,0,0,0.3)`,
|
|
4041
|
+
`inset 0 1px 0 0 rgba(255,255,255,0.2)`
|
|
4042
|
+
].join(", ")
|
|
4043
|
+
},
|
|
4044
|
+
"aria-label": "Open chat",
|
|
4045
|
+
...handlers,
|
|
4046
|
+
children: [
|
|
4047
|
+
/* @__PURE__ */ jsx14(ChatBubbleIcon, { size: 36, className: "pointer-events-none" }),
|
|
4048
|
+
/* @__PURE__ */ jsx14(
|
|
4049
|
+
"span",
|
|
4050
|
+
{
|
|
4051
|
+
className: "absolute inset-0 rounded-full animate-ping pointer-events-none",
|
|
4052
|
+
style: {
|
|
4053
|
+
backgroundColor: primaryColor,
|
|
4054
|
+
opacity: 0.15,
|
|
4055
|
+
animationDuration: "2.5s"
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
)
|
|
4059
|
+
]
|
|
4060
|
+
}
|
|
4061
|
+
)
|
|
3453
4062
|
}
|
|
3454
|
-
)
|
|
4063
|
+
);
|
|
3455
4064
|
}
|
|
3456
4065
|
if (identityMode === "form" && (!userInfo || !userInfo.email || !userInfo.name)) {
|
|
3457
|
-
return /* @__PURE__ */
|
|
4066
|
+
return /* @__PURE__ */ jsx14(
|
|
3458
4067
|
PreChatForm,
|
|
3459
4068
|
{
|
|
3460
4069
|
onSubmit: handlePreChatSubmit,
|
|
@@ -3471,7 +4080,7 @@ function Chat({
|
|
|
3471
4080
|
conversationId,
|
|
3472
4081
|
currentUser: userInfo || void 0
|
|
3473
4082
|
};
|
|
3474
|
-
return /* @__PURE__ */
|
|
4083
|
+
return /* @__PURE__ */ jsx14(
|
|
3475
4084
|
ChatWidget,
|
|
3476
4085
|
{
|
|
3477
4086
|
config: fullConfig,
|
|
@@ -3484,12 +4093,12 @@ function Chat({
|
|
|
3484
4093
|
}
|
|
3485
4094
|
|
|
3486
4095
|
// src/components/TypingIndicator.tsx
|
|
3487
|
-
import { jsx as
|
|
4096
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3488
4097
|
function TypingIndicator({ isTyping, userName }) {
|
|
3489
4098
|
if (!isTyping) {
|
|
3490
4099
|
return null;
|
|
3491
4100
|
}
|
|
3492
|
-
return /* @__PURE__ */
|
|
4101
|
+
return /* @__PURE__ */ jsx15(
|
|
3493
4102
|
"div",
|
|
3494
4103
|
{
|
|
3495
4104
|
className: "px-4 py-2",
|
|
@@ -3497,8 +4106,8 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
3497
4106
|
borderTop: "1px solid rgba(255,255,255,0.06)",
|
|
3498
4107
|
backgroundColor: "rgba(0,0,0,0.15)"
|
|
3499
4108
|
},
|
|
3500
|
-
children: /* @__PURE__ */
|
|
3501
|
-
/* @__PURE__ */
|
|
4109
|
+
children: /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
4110
|
+
/* @__PURE__ */ jsx15("div", { className: "flex gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx15(
|
|
3502
4111
|
"span",
|
|
3503
4112
|
{
|
|
3504
4113
|
className: "rounded-full animate-bounce",
|
|
@@ -3512,7 +4121,7 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
3512
4121
|
},
|
|
3513
4122
|
i
|
|
3514
4123
|
)) }),
|
|
3515
|
-
/* @__PURE__ */
|
|
4124
|
+
/* @__PURE__ */ jsx15(
|
|
3516
4125
|
"span",
|
|
3517
4126
|
{
|
|
3518
4127
|
style: {
|
|
@@ -3528,6 +4137,9 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
3528
4137
|
);
|
|
3529
4138
|
}
|
|
3530
4139
|
export {
|
|
4140
|
+
BookingCancelledCard,
|
|
4141
|
+
BookingConfirmationCard,
|
|
4142
|
+
BookingSlotPicker,
|
|
3531
4143
|
Chat,
|
|
3532
4144
|
ChatHeader,
|
|
3533
4145
|
ChatInput,
|