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