@xcelsior/ui-chat 2.0.3 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/preview.tsx +2 -1
- package/dist/index.d.mts +58 -5
- package/dist/index.d.ts +58 -5
- package/dist/index.js +1073 -473
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1060 -463
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -2
- package/src/components/BookingCancelledCard.tsx +103 -0
- package/src/components/BookingCards.stories.tsx +102 -0
- package/src/components/BookingConfirmationCard.tsx +170 -0
- package/src/components/BookingSlotPicker.stories.tsx +87 -0
- package/src/components/BookingSlotPicker.tsx +253 -0
- package/src/components/BrandIcons.stories.tsx +32 -1
- package/src/components/BrandIcons.tsx +21 -17
- package/src/components/Chat.tsx +78 -83
- package/src/components/ChatWidget.tsx +30 -3
- 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 +119 -45
- 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-BMnd3YrF.js → Color-YHDXOIA2-azE51u2m.js} +1 -1
- package/storybook-static/assets/{DocsRenderer-CFRXHY34-i_W8iCu9.js → DocsRenderer-CFRXHY34-jTmzKIDk.js} +3 -3
- package/storybook-static/assets/MessageItem-pEOwuLyh.js +34 -0
- package/storybook-static/assets/MessageItem.stories-Cs5Vtkle.js +422 -0
- package/storybook-static/assets/{entry-preview-oDnntGcx.js → entry-preview-vcpiajAT.js} +1 -1
- package/storybook-static/assets/globe-BtMvkLMD.js +31 -0
- package/storybook-static/assets/{iframe-CGBtu2Se.js → iframe-Cx1n-SeE.js} +2 -2
- package/storybook-static/assets/preview-B8y-wc-n.css +1 -0
- package/storybook-static/assets/preview-CC4t7T7W.js +1 -0
- package/storybook-static/assets/{preview-BRpahs9B.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/tsconfig.json +4 -0
- 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-J_Yp51wU.js +0 -803
- package/storybook-static/assets/MessageItem-DAaKZ9s9.js +0 -14
- package/storybook-static/assets/MessageItem.stories-Ckr1_scc.js +0 -255
- package/storybook-static/assets/ToastContext-Bty1K7ya.js +0 -1
- package/storybook-static/assets/preview-DUOvJmsz.js +0 -1
- package/storybook-static/assets/preview-DcGwT3kv.css +0 -1
package/dist/index.js
CHANGED
|
@@ -30,6 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.tsx
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
BookingCancelledCard: () => BookingCancelledCard,
|
|
34
|
+
BookingConfirmationCard: () => BookingConfirmationCard,
|
|
35
|
+
BookingSlotPicker: () => BookingSlotPicker,
|
|
33
36
|
Chat: () => Chat,
|
|
34
37
|
ChatHeader: () => ChatHeader,
|
|
35
38
|
ChatInput: () => ChatInput,
|
|
@@ -50,7 +53,7 @@ __export(index_exports, {
|
|
|
50
53
|
module.exports = __toCommonJS(index_exports);
|
|
51
54
|
|
|
52
55
|
// src/components/ChatWidget.tsx
|
|
53
|
-
var
|
|
56
|
+
var import_react11 = require("react");
|
|
54
57
|
|
|
55
58
|
// src/hooks/useWebSocket.ts
|
|
56
59
|
var import_react = require("react");
|
|
@@ -63,6 +66,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
63
66
|
const reconnectAttemptsRef = (0, import_react.useRef)(0);
|
|
64
67
|
const messageHandlerRef = (0, import_react.useRef)(null);
|
|
65
68
|
const abortedRef = (0, import_react.useRef)(false);
|
|
69
|
+
const connectionGenerationRef = (0, import_react.useRef)(0);
|
|
66
70
|
const maxReconnectAttempts = 5;
|
|
67
71
|
const reconnectDelay = 3e3;
|
|
68
72
|
const isUsingExternalWs = !!externalWebSocket;
|
|
@@ -74,9 +78,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
74
78
|
try {
|
|
75
79
|
const message = JSON.parse(event.data);
|
|
76
80
|
setLastMessage(message);
|
|
77
|
-
if (message.type === "
|
|
78
|
-
config.onMessageReceived?.(message.data);
|
|
79
|
-
} else if (message.type === "error") {
|
|
81
|
+
if (message.type === "error") {
|
|
80
82
|
const err = new Error(message.data?.message || "WebSocket error");
|
|
81
83
|
setError(err);
|
|
82
84
|
config.onError?.(err);
|
|
@@ -96,6 +98,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
96
98
|
}, []);
|
|
97
99
|
const connect = (0, import_react.useCallback)(() => {
|
|
98
100
|
if (abortedRef.current) return;
|
|
101
|
+
const generation = ++connectionGenerationRef.current;
|
|
99
102
|
console.log("connecting to WebSocket...", config.currentUser, config.conversationId);
|
|
100
103
|
try {
|
|
101
104
|
if (wsRef.current) {
|
|
@@ -115,6 +118,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
115
118
|
}
|
|
116
119
|
const ws = new WebSocket(url.toString());
|
|
117
120
|
ws.onopen = () => {
|
|
121
|
+
if (generation !== connectionGenerationRef.current) return;
|
|
118
122
|
if (abortedRef.current) {
|
|
119
123
|
ws.close(1e3, "Effect cleaned up");
|
|
120
124
|
return;
|
|
@@ -126,6 +130,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
126
130
|
config.onConnectionChange?.(true);
|
|
127
131
|
};
|
|
128
132
|
ws.onerror = (event) => {
|
|
133
|
+
if (generation !== connectionGenerationRef.current) return;
|
|
129
134
|
if (abortedRef.current) return;
|
|
130
135
|
console.error("WebSocket error:", event);
|
|
131
136
|
const err = new Error("WebSocket connection error");
|
|
@@ -133,6 +138,7 @@ function useWebSocket(config, externalWebSocket) {
|
|
|
133
138
|
config.onError?.(err);
|
|
134
139
|
};
|
|
135
140
|
ws.onclose = (event) => {
|
|
141
|
+
if (generation !== connectionGenerationRef.current) return;
|
|
136
142
|
if (abortedRef.current) return;
|
|
137
143
|
console.log("WebSocket closed:", event.code, event.reason);
|
|
138
144
|
setIsConnected(false);
|
|
@@ -234,7 +240,8 @@ async function fetchMessages(baseUrl, params, headers) {
|
|
|
234
240
|
headers: {
|
|
235
241
|
"Content-Type": "application/json",
|
|
236
242
|
...headers
|
|
237
|
-
}
|
|
243
|
+
},
|
|
244
|
+
timeout: 8e3
|
|
238
245
|
});
|
|
239
246
|
return {
|
|
240
247
|
data: response.data.data ?? [],
|
|
@@ -251,6 +258,8 @@ async function fetchMessages(baseUrl, params, headers) {
|
|
|
251
258
|
}
|
|
252
259
|
|
|
253
260
|
// src/hooks/useMessages.ts
|
|
261
|
+
var BOT_THINKING_TIMEOUT = 15e3;
|
|
262
|
+
var AGENT_STATUSES = /* @__PURE__ */ new Set(["agent_active", "pending_agent"]);
|
|
254
263
|
function useMessages(websocket, config) {
|
|
255
264
|
const [messages, setMessages] = (0, import_react2.useState)([]);
|
|
256
265
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
|
|
@@ -259,6 +268,8 @@ function useMessages(websocket, config) {
|
|
|
259
268
|
const [hasMore, setHasMore] = (0, import_react2.useState)(true);
|
|
260
269
|
const [isLoadingMore, setIsLoadingMore] = (0, import_react2.useState)(false);
|
|
261
270
|
const [isBotThinking, setIsBotThinking] = (0, import_react2.useState)(false);
|
|
271
|
+
const botThinkingTimerRef = (0, import_react2.useRef)(null);
|
|
272
|
+
const agentActiveRef = (0, import_react2.useRef)(false);
|
|
262
273
|
const { httpApiUrl, conversationId, headers, onError, toast } = config;
|
|
263
274
|
const headersWithApiKey = (0, import_react2.useMemo)(
|
|
264
275
|
() => ({
|
|
@@ -267,11 +278,33 @@ function useMessages(websocket, config) {
|
|
|
267
278
|
}),
|
|
268
279
|
[headers, config.apiKey]
|
|
269
280
|
);
|
|
281
|
+
const clearBotThinking = (0, import_react2.useCallback)(() => {
|
|
282
|
+
setIsBotThinking(false);
|
|
283
|
+
if (botThinkingTimerRef.current) {
|
|
284
|
+
clearTimeout(botThinkingTimerRef.current);
|
|
285
|
+
botThinkingTimerRef.current = null;
|
|
286
|
+
}
|
|
287
|
+
}, []);
|
|
270
288
|
(0, import_react2.useEffect)(() => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
289
|
+
if (!httpApiUrl || !conversationId) return;
|
|
290
|
+
const fetchStatus = async () => {
|
|
291
|
+
try {
|
|
292
|
+
const url = `${httpApiUrl}/conversation?conversationId=${encodeURIComponent(conversationId)}`;
|
|
293
|
+
const res = await fetch(url, { headers: headersWithApiKey });
|
|
294
|
+
if (!res.ok) return;
|
|
295
|
+
const json = await res.json();
|
|
296
|
+
const conv = json.data?.conversation ?? json.data;
|
|
297
|
+
if (conv?.status && AGENT_STATUSES.has(conv.status)) {
|
|
298
|
+
agentActiveRef.current = true;
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
274
301
|
}
|
|
302
|
+
};
|
|
303
|
+
fetchStatus();
|
|
304
|
+
}, [httpApiUrl, conversationId, headersWithApiKey]);
|
|
305
|
+
(0, import_react2.useEffect)(() => {
|
|
306
|
+
if (!httpApiUrl || !conversationId) return;
|
|
307
|
+
const loadMessages = async () => {
|
|
275
308
|
setIsLoading(true);
|
|
276
309
|
setError(null);
|
|
277
310
|
try {
|
|
@@ -283,6 +316,9 @@ function useMessages(websocket, config) {
|
|
|
283
316
|
setMessages(result.data);
|
|
284
317
|
setNextPageToken(result.nextPageToken);
|
|
285
318
|
setHasMore(!!result.nextPageToken);
|
|
319
|
+
if (result.data.some((m) => m.senderType === "agent")) {
|
|
320
|
+
agentActiveRef.current = true;
|
|
321
|
+
}
|
|
286
322
|
} catch (err) {
|
|
287
323
|
const error2 = err instanceof Error ? err : new Error("Failed to load messages");
|
|
288
324
|
setError(error2);
|
|
@@ -298,30 +334,60 @@ function useMessages(websocket, config) {
|
|
|
298
334
|
(0, import_react2.useEffect)(() => {
|
|
299
335
|
if (websocket.lastMessage?.type === "message" && websocket.lastMessage.data) {
|
|
300
336
|
const newMessage = websocket.lastMessage.data;
|
|
301
|
-
if (conversationId && newMessage.conversationId !== conversationId)
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
337
|
+
if (conversationId && newMessage.conversationId !== conversationId) return;
|
|
304
338
|
setMessages((prev) => {
|
|
305
|
-
if (prev.some((msg) => msg.id === newMessage.id))
|
|
306
|
-
|
|
339
|
+
if (prev.some((msg) => msg.id === newMessage.id)) return prev;
|
|
340
|
+
const newMsgTime = new Date(newMessage.createdAt).getTime();
|
|
341
|
+
const tempIdx = prev.findIndex(
|
|
342
|
+
(msg) => msg.id.startsWith("temp-") && msg.content === newMessage.content && msg.senderId === newMessage.senderId && Math.abs(new Date(msg.createdAt).getTime() - newMsgTime) < 1e4
|
|
343
|
+
);
|
|
344
|
+
if (tempIdx >= 0) {
|
|
345
|
+
const next = [...prev];
|
|
346
|
+
next[tempIdx] = newMessage;
|
|
347
|
+
return next;
|
|
307
348
|
}
|
|
308
349
|
return [...prev, newMessage];
|
|
309
350
|
});
|
|
310
|
-
if (newMessage.senderType === "
|
|
311
|
-
|
|
351
|
+
if (newMessage.senderType === "agent") {
|
|
352
|
+
agentActiveRef.current = true;
|
|
353
|
+
}
|
|
354
|
+
if (newMessage.senderType !== "customer") {
|
|
355
|
+
clearBotThinking();
|
|
312
356
|
}
|
|
313
357
|
onMessageReceived?.(newMessage);
|
|
314
358
|
}
|
|
315
|
-
}, [websocket.lastMessage, onMessageReceived, conversationId]);
|
|
359
|
+
}, [websocket.lastMessage, onMessageReceived, conversationId, clearBotThinking]);
|
|
360
|
+
(0, import_react2.useEffect)(() => {
|
|
361
|
+
if (!websocket.lastMessage) return;
|
|
362
|
+
const { type, data } = websocket.lastMessage;
|
|
363
|
+
if (type === "bot_not_responding") {
|
|
364
|
+
if (data?.conversationId && conversationId && data.conversationId !== conversationId) return;
|
|
365
|
+
clearBotThinking();
|
|
366
|
+
agentActiveRef.current = true;
|
|
367
|
+
}
|
|
368
|
+
if (type === "conversation_updated") {
|
|
369
|
+
if (data?.conversationId && conversationId && data.conversationId !== conversationId) return;
|
|
370
|
+
if (data?.status && AGENT_STATUSES.has(data.status)) {
|
|
371
|
+
agentActiveRef.current = true;
|
|
372
|
+
clearBotThinking();
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}, [websocket.lastMessage, conversationId, clearBotThinking]);
|
|
316
376
|
const addMessage = (0, import_react2.useCallback)((message) => {
|
|
377
|
+
const isAgentActive = agentActiveRef.current;
|
|
317
378
|
setMessages((prev) => {
|
|
318
|
-
if (prev.some((msg) => msg.id === message.id))
|
|
319
|
-
return prev;
|
|
320
|
-
}
|
|
379
|
+
if (prev.some((msg) => msg.id === message.id)) return prev;
|
|
321
380
|
return [...prev, message];
|
|
322
381
|
});
|
|
323
|
-
if (message.senderType === "
|
|
382
|
+
if (message.senderType === "agent") {
|
|
383
|
+
agentActiveRef.current = true;
|
|
384
|
+
}
|
|
385
|
+
if (message.senderType === "customer" && !isAgentActive) {
|
|
324
386
|
setIsBotThinking(true);
|
|
387
|
+
if (botThinkingTimerRef.current) clearTimeout(botThinkingTimerRef.current);
|
|
388
|
+
botThinkingTimerRef.current = setTimeout(() => {
|
|
389
|
+
setIsBotThinking(false);
|
|
390
|
+
}, BOT_THINKING_TIMEOUT);
|
|
325
391
|
}
|
|
326
392
|
}, []);
|
|
327
393
|
const updateMessageStatus = (0, import_react2.useCallback)((messageId, status) => {
|
|
@@ -329,21 +395,16 @@ function useMessages(websocket, config) {
|
|
|
329
395
|
}, []);
|
|
330
396
|
const clearMessages = (0, import_react2.useCallback)(() => {
|
|
331
397
|
setMessages([]);
|
|
398
|
+
agentActiveRef.current = false;
|
|
332
399
|
}, []);
|
|
333
400
|
const loadMore = (0, import_react2.useCallback)(async () => {
|
|
334
|
-
if (!hasMore || isLoadingMore || !httpApiUrl || !conversationId || !nextPageToken)
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
401
|
+
if (!hasMore || isLoadingMore || !httpApiUrl || !conversationId || !nextPageToken) return;
|
|
337
402
|
setIsLoadingMore(true);
|
|
338
403
|
setError(null);
|
|
339
404
|
try {
|
|
340
405
|
const result = await fetchMessages(
|
|
341
406
|
httpApiUrl,
|
|
342
|
-
{
|
|
343
|
-
conversationId,
|
|
344
|
-
limit: 20,
|
|
345
|
-
pageToken: nextPageToken
|
|
346
|
-
},
|
|
407
|
+
{ conversationId, limit: 20, pageToken: nextPageToken },
|
|
347
408
|
headersWithApiKey
|
|
348
409
|
);
|
|
349
410
|
setMessages((prev) => [...result.data, ...prev]);
|
|
@@ -356,15 +417,12 @@ function useMessages(websocket, config) {
|
|
|
356
417
|
} finally {
|
|
357
418
|
setIsLoadingMore(false);
|
|
358
419
|
}
|
|
359
|
-
}, [
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
headersWithApiKey,
|
|
366
|
-
onError
|
|
367
|
-
]);
|
|
420
|
+
}, [hasMore, isLoadingMore, httpApiUrl, conversationId, nextPageToken, headersWithApiKey, onError]);
|
|
421
|
+
(0, import_react2.useEffect)(() => {
|
|
422
|
+
return () => {
|
|
423
|
+
if (botThinkingTimerRef.current) clearTimeout(botThinkingTimerRef.current);
|
|
424
|
+
};
|
|
425
|
+
}, []);
|
|
368
426
|
return {
|
|
369
427
|
messages,
|
|
370
428
|
addMessage,
|
|
@@ -792,24 +850,20 @@ function XcelsiorSymbol({ size = 24, color = "white", className = "", style }) {
|
|
|
792
850
|
}
|
|
793
851
|
);
|
|
794
852
|
}
|
|
795
|
-
function ChatBubbleIcon({ size =
|
|
796
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.
|
|
797
|
-
"
|
|
798
|
-
{
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
"aria-hidden": "true",
|
|
810
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.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" })
|
|
811
|
-
}
|
|
812
|
-
);
|
|
853
|
+
function ChatBubbleIcon({ size = 36, color = "white", className = "", style }) {
|
|
854
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.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: [
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "3.5", r: "3", fill: "#67e8f9", opacity: 0.4 }),
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "3.5", r: "1.5", fill: "#67e8f9" }),
|
|
857
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "15", y: "4.5", width: "2", height: "4", rx: "1", fill: color }),
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1", y: "15", width: "4", height: "6", rx: "2", fill: color, opacity: 0.5 }),
|
|
859
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "27", y: "15", width: "4", height: "6", rx: "2", fill: color, opacity: 0.5 }),
|
|
860
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "5", y: "8.5", width: "22", height: "19", rx: "5", fill: color }),
|
|
861
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "16.5", r: "3", fill: "#1a4fd0" }),
|
|
862
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "20", cy: "16.5", r: "3", fill: "#1a4fd0" }),
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "13", cy: "15.5", r: "1.2", fill: color }),
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "21", cy: "15.5", r: "1.2", fill: color }),
|
|
865
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M13 22.5q3 2.5 6 0", stroke: "#1a4fd0", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
866
|
+
] });
|
|
813
867
|
}
|
|
814
868
|
function XcelsiorAvatar({ size = 40, className = "" }) {
|
|
815
869
|
const iconSize = Math.round(size * 0.55);
|
|
@@ -973,7 +1027,7 @@ function ChatHeader({ agent, onClose, onMinimize, theme }) {
|
|
|
973
1027
|
}
|
|
974
1028
|
|
|
975
1029
|
// src/components/MessageList.tsx
|
|
976
|
-
var
|
|
1030
|
+
var import_react8 = require("react");
|
|
977
1031
|
|
|
978
1032
|
// src/components/Spinner.tsx
|
|
979
1033
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -1021,6 +1075,7 @@ function Spinner({ size = "md", color = "currentColor" }) {
|
|
|
1021
1075
|
|
|
1022
1076
|
// src/components/MessageItem.tsx
|
|
1023
1077
|
var import_date_fns = require("date-fns");
|
|
1078
|
+
var import_lucide_react4 = require("lucide-react");
|
|
1024
1079
|
|
|
1025
1080
|
// src/components/MarkdownMessage.tsx
|
|
1026
1081
|
var import_react_markdown = __toESM(require("react-markdown"));
|
|
@@ -1260,14 +1315,481 @@ function MarkdownMessage({ content, theme }) {
|
|
|
1260
1315
|
) });
|
|
1261
1316
|
}
|
|
1262
1317
|
|
|
1263
|
-
// src/components/
|
|
1318
|
+
// src/components/BookingSlotPicker.tsx
|
|
1319
|
+
var import_react6 = require("react");
|
|
1320
|
+
var import_lucide_react = require("lucide-react");
|
|
1264
1321
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1322
|
+
function BookingSlotPicker({ data, theme, onSlotSelected, disabled = false }) {
|
|
1323
|
+
const [selectedDateIndex, setSelectedDateIndex] = (0, import_react6.useState)(0);
|
|
1324
|
+
const [selectedSlot, setSelectedSlot] = (0, import_react6.useState)(null);
|
|
1325
|
+
const bgColor = theme?.background || "#00001a";
|
|
1326
|
+
const isLightTheme = (() => {
|
|
1327
|
+
if (!bgColor.startsWith("#")) return false;
|
|
1328
|
+
const hex = bgColor.replace("#", "");
|
|
1329
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1330
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1331
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1332
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
1333
|
+
})();
|
|
1334
|
+
const primaryColor = theme?.primary || "#337eff";
|
|
1335
|
+
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1336
|
+
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)");
|
|
1337
|
+
const cardStyle = isLightTheme ? {
|
|
1338
|
+
backgroundColor: "rgba(0,0,0,0.03)",
|
|
1339
|
+
boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)",
|
|
1340
|
+
borderRadius: "14px",
|
|
1341
|
+
padding: "14px"
|
|
1342
|
+
} : {
|
|
1343
|
+
backgroundColor: "rgba(255,255,255,0.04)",
|
|
1344
|
+
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.08), inset 0 1px 0 0 rgba(255,255,255,0.1)",
|
|
1345
|
+
borderRadius: "14px",
|
|
1346
|
+
padding: "14px"
|
|
1347
|
+
};
|
|
1348
|
+
const activePillStyle = {
|
|
1349
|
+
backgroundColor: primaryColor,
|
|
1350
|
+
color: "#ffffff",
|
|
1351
|
+
borderRadius: "20px",
|
|
1352
|
+
padding: "5px 12px",
|
|
1353
|
+
fontSize: "12px",
|
|
1354
|
+
fontWeight: "600",
|
|
1355
|
+
letterSpacing: "0.01em",
|
|
1356
|
+
cursor: disabled ? "default" : "pointer",
|
|
1357
|
+
border: "none",
|
|
1358
|
+
flexShrink: 0
|
|
1359
|
+
};
|
|
1360
|
+
const inactivePillStyle = isLightTheme ? {
|
|
1361
|
+
backgroundColor: "rgba(0,0,0,0.04)",
|
|
1362
|
+
color: textColor,
|
|
1363
|
+
borderRadius: "20px",
|
|
1364
|
+
padding: "5px 12px",
|
|
1365
|
+
fontSize: "12px",
|
|
1366
|
+
fontWeight: "500",
|
|
1367
|
+
letterSpacing: "0.01em",
|
|
1368
|
+
cursor: disabled ? "default" : "pointer",
|
|
1369
|
+
border: "none",
|
|
1370
|
+
boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.1)",
|
|
1371
|
+
flexShrink: 0
|
|
1372
|
+
} : {
|
|
1373
|
+
backgroundColor: "rgba(255,255,255,0.05)",
|
|
1374
|
+
color: textColor,
|
|
1375
|
+
borderRadius: "20px",
|
|
1376
|
+
padding: "5px 12px",
|
|
1377
|
+
fontSize: "12px",
|
|
1378
|
+
fontWeight: "500",
|
|
1379
|
+
letterSpacing: "0.01em",
|
|
1380
|
+
cursor: disabled ? "default" : "pointer",
|
|
1381
|
+
border: "none",
|
|
1382
|
+
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.1)",
|
|
1383
|
+
flexShrink: 0
|
|
1384
|
+
};
|
|
1385
|
+
const activeSlot = selectedSlot?.date === data.availableDates[selectedDateIndex]?.date ? selectedSlot.time : null;
|
|
1386
|
+
const [confirming, setConfirming] = (0, import_react6.useState)(false);
|
|
1387
|
+
const handleSlotClick = (date, time) => {
|
|
1388
|
+
if (disabled || confirming) return;
|
|
1389
|
+
setSelectedSlot({ date, time });
|
|
1390
|
+
};
|
|
1391
|
+
const handleConfirm = (0, import_react6.useCallback)(() => {
|
|
1392
|
+
if (!selectedSlot || disabled || confirming) return;
|
|
1393
|
+
setConfirming(true);
|
|
1394
|
+
onSlotSelected(selectedSlot.date, selectedSlot.time);
|
|
1395
|
+
}, [selectedSlot, disabled, confirming, onSlotSelected]);
|
|
1396
|
+
const currentDate = data.availableDates[selectedDateIndex];
|
|
1397
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: cardStyle, children: [
|
|
1398
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1399
|
+
"p",
|
|
1400
|
+
{
|
|
1401
|
+
style: {
|
|
1402
|
+
fontSize: "11px",
|
|
1403
|
+
fontWeight: "600",
|
|
1404
|
+
letterSpacing: "0.06em",
|
|
1405
|
+
textTransform: "uppercase",
|
|
1406
|
+
color: textMuted,
|
|
1407
|
+
marginBottom: "10px"
|
|
1408
|
+
},
|
|
1409
|
+
children: "Select a date"
|
|
1410
|
+
}
|
|
1411
|
+
),
|
|
1412
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1413
|
+
"div",
|
|
1414
|
+
{
|
|
1415
|
+
style: {
|
|
1416
|
+
display: "flex",
|
|
1417
|
+
gap: "6px",
|
|
1418
|
+
overflowX: "auto",
|
|
1419
|
+
paddingBottom: "2px",
|
|
1420
|
+
scrollbarWidth: "none",
|
|
1421
|
+
msOverflowStyle: "none",
|
|
1422
|
+
marginBottom: "14px"
|
|
1423
|
+
},
|
|
1424
|
+
children: data.availableDates.map((dateSlot, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1425
|
+
"button",
|
|
1426
|
+
{
|
|
1427
|
+
type: "button",
|
|
1428
|
+
onClick: () => !disabled && setSelectedDateIndex(index),
|
|
1429
|
+
style: index === selectedDateIndex ? activePillStyle : inactivePillStyle,
|
|
1430
|
+
title: dateSlot.dayName,
|
|
1431
|
+
"aria-pressed": index === selectedDateIndex,
|
|
1432
|
+
disabled,
|
|
1433
|
+
children: dateSlot.dayLabel
|
|
1434
|
+
},
|
|
1435
|
+
dateSlot.date
|
|
1436
|
+
))
|
|
1437
|
+
}
|
|
1438
|
+
),
|
|
1439
|
+
currentDate && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1441
|
+
"p",
|
|
1442
|
+
{
|
|
1443
|
+
style: {
|
|
1444
|
+
fontSize: "11px",
|
|
1445
|
+
fontWeight: "600",
|
|
1446
|
+
letterSpacing: "0.06em",
|
|
1447
|
+
textTransform: "uppercase",
|
|
1448
|
+
color: textMuted,
|
|
1449
|
+
marginBottom: "8px"
|
|
1450
|
+
},
|
|
1451
|
+
children: currentDate.dayName
|
|
1452
|
+
}
|
|
1453
|
+
),
|
|
1454
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1455
|
+
"div",
|
|
1456
|
+
{
|
|
1457
|
+
style: {
|
|
1458
|
+
display: "grid",
|
|
1459
|
+
gridTemplateColumns: "repeat(3, 1fr)",
|
|
1460
|
+
gap: "6px",
|
|
1461
|
+
marginBottom: "14px"
|
|
1462
|
+
},
|
|
1463
|
+
children: currentDate.slots.map((time) => {
|
|
1464
|
+
const isSelected = activeSlot === time;
|
|
1465
|
+
const isConfirmed = selectedSlot?.date === currentDate.date && selectedSlot.time === time;
|
|
1466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1467
|
+
"button",
|
|
1468
|
+
{
|
|
1469
|
+
type: "button",
|
|
1470
|
+
onClick: () => handleSlotClick(currentDate.date, time),
|
|
1471
|
+
disabled: disabled && !isConfirmed,
|
|
1472
|
+
"aria-pressed": isSelected,
|
|
1473
|
+
style: {
|
|
1474
|
+
...isSelected ? activePillStyle : inactivePillStyle,
|
|
1475
|
+
padding: "7px 8px",
|
|
1476
|
+
borderRadius: "8px",
|
|
1477
|
+
fontSize: "13px",
|
|
1478
|
+
fontWeight: isSelected ? "600" : "400",
|
|
1479
|
+
textAlign: "center",
|
|
1480
|
+
opacity: disabled && !isConfirmed ? 0.4 : 1,
|
|
1481
|
+
transition: "opacity 0.15s ease"
|
|
1482
|
+
},
|
|
1483
|
+
children: time
|
|
1484
|
+
},
|
|
1485
|
+
time
|
|
1486
|
+
);
|
|
1487
|
+
})
|
|
1488
|
+
}
|
|
1489
|
+
)
|
|
1490
|
+
] }),
|
|
1491
|
+
selectedSlot && !disabled && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1492
|
+
"button",
|
|
1493
|
+
{
|
|
1494
|
+
type: "button",
|
|
1495
|
+
onClick: handleConfirm,
|
|
1496
|
+
disabled: confirming,
|
|
1497
|
+
style: {
|
|
1498
|
+
width: "100%",
|
|
1499
|
+
padding: "10px 16px",
|
|
1500
|
+
borderRadius: "10px",
|
|
1501
|
+
border: "none",
|
|
1502
|
+
backgroundColor: confirming ? `${primaryColor}80` : primaryColor,
|
|
1503
|
+
color: "#ffffff",
|
|
1504
|
+
fontSize: "13px",
|
|
1505
|
+
fontWeight: "600",
|
|
1506
|
+
letterSpacing: "0.01em",
|
|
1507
|
+
cursor: confirming ? "default" : "pointer",
|
|
1508
|
+
marginBottom: "12px",
|
|
1509
|
+
display: "flex",
|
|
1510
|
+
alignItems: "center",
|
|
1511
|
+
justifyContent: "center",
|
|
1512
|
+
gap: "6px",
|
|
1513
|
+
transition: "background-color 0.15s ease"
|
|
1514
|
+
},
|
|
1515
|
+
children: [
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Calendar, { size: 14, "aria-hidden": "true" }),
|
|
1517
|
+
confirming ? "Booking..." : `Book ${selectedSlot.time} on ${data.availableDates.find((d) => d.date === selectedSlot.date)?.dayLabel ?? selectedSlot.date}`
|
|
1518
|
+
]
|
|
1519
|
+
}
|
|
1520
|
+
),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1522
|
+
"div",
|
|
1523
|
+
{
|
|
1524
|
+
style: {
|
|
1525
|
+
display: "flex",
|
|
1526
|
+
alignItems: "center",
|
|
1527
|
+
justifyContent: "space-between",
|
|
1528
|
+
gap: "8px"
|
|
1529
|
+
},
|
|
1530
|
+
children: [
|
|
1531
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "5px" }, children: [
|
|
1532
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Globe, { size: 11, color: textMuted, "aria-hidden": "true" }),
|
|
1533
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: "11px", color: textMuted, letterSpacing: "0.01em" }, children: data.timezone })
|
|
1534
|
+
] }),
|
|
1535
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "5px" }, children: [
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.Clock, { size: 11, color: textMuted, "aria-hidden": "true" }),
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontSize: "11px", color: textMuted, letterSpacing: "0.01em" }, children: [
|
|
1538
|
+
data.meetingDuration,
|
|
1539
|
+
" min"
|
|
1540
|
+
] })
|
|
1541
|
+
] })
|
|
1542
|
+
]
|
|
1543
|
+
}
|
|
1544
|
+
)
|
|
1545
|
+
] });
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/components/BookingConfirmationCard.tsx
|
|
1549
|
+
var import_lucide_react2 = require("lucide-react");
|
|
1550
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1551
|
+
function BookingConfirmationCard({ data, theme }) {
|
|
1552
|
+
const bgColor = theme?.background || "#00001a";
|
|
1553
|
+
const isLightTheme = (() => {
|
|
1554
|
+
if (!bgColor.startsWith("#")) return false;
|
|
1555
|
+
const hex = bgColor.replace("#", "");
|
|
1556
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1557
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1558
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1559
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
1560
|
+
})();
|
|
1561
|
+
const primaryColor = theme?.primary || "#337eff";
|
|
1562
|
+
const successColor = theme?.statusPositive || "#22c55e";
|
|
1563
|
+
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1564
|
+
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)");
|
|
1565
|
+
const cardStyle = isLightTheme ? {
|
|
1566
|
+
backgroundColor: "rgba(0,0,0,0.03)",
|
|
1567
|
+
boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.08)",
|
|
1568
|
+
borderRadius: "14px",
|
|
1569
|
+
padding: "16px",
|
|
1570
|
+
overflow: "hidden"
|
|
1571
|
+
} : {
|
|
1572
|
+
backgroundColor: "rgba(255,255,255,0.04)",
|
|
1573
|
+
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.08), inset 0 1px 0 0 rgba(255,255,255,0.1)",
|
|
1574
|
+
borderRadius: "14px",
|
|
1575
|
+
padding: "16px",
|
|
1576
|
+
overflow: "hidden"
|
|
1577
|
+
};
|
|
1578
|
+
const detailRowStyle = {
|
|
1579
|
+
display: "flex",
|
|
1580
|
+
alignItems: "flex-start",
|
|
1581
|
+
gap: "8px",
|
|
1582
|
+
marginBottom: "8px"
|
|
1583
|
+
};
|
|
1584
|
+
const dividerStyle = {
|
|
1585
|
+
height: "1px",
|
|
1586
|
+
backgroundColor: isLightTheme ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.06)",
|
|
1587
|
+
margin: "12px 0"
|
|
1588
|
+
};
|
|
1589
|
+
const actionButtonBase = {
|
|
1590
|
+
display: "flex",
|
|
1591
|
+
alignItems: "center",
|
|
1592
|
+
justifyContent: "center",
|
|
1593
|
+
gap: "6px",
|
|
1594
|
+
flex: 1,
|
|
1595
|
+
padding: "9px 12px",
|
|
1596
|
+
borderRadius: "9px",
|
|
1597
|
+
fontSize: "12px",
|
|
1598
|
+
fontWeight: "600",
|
|
1599
|
+
letterSpacing: "0.01em",
|
|
1600
|
+
cursor: "pointer",
|
|
1601
|
+
textDecoration: "none",
|
|
1602
|
+
border: "none",
|
|
1603
|
+
transition: "opacity 0.15s ease"
|
|
1604
|
+
};
|
|
1605
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: cardStyle, children: [
|
|
1606
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" }, children: [
|
|
1607
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.CircleCheck, { size: 22, color: successColor, "aria-hidden": "true" }),
|
|
1608
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1609
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1610
|
+
"p",
|
|
1611
|
+
{
|
|
1612
|
+
style: {
|
|
1613
|
+
fontSize: "14px",
|
|
1614
|
+
fontWeight: "700",
|
|
1615
|
+
color: textColor,
|
|
1616
|
+
letterSpacing: "0.006em",
|
|
1617
|
+
lineHeight: 1.3
|
|
1618
|
+
},
|
|
1619
|
+
children: "Meeting Confirmed"
|
|
1620
|
+
}
|
|
1621
|
+
),
|
|
1622
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { fontSize: "11px", color: successColor, letterSpacing: "0.01em", marginTop: "1px" }, children: "You'll receive a calendar invite shortly" })
|
|
1623
|
+
] })
|
|
1624
|
+
] }),
|
|
1625
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: detailRowStyle, children: [
|
|
1627
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Calendar, { size: 14, color: primaryColor, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1628
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: "13px", color: textColor, lineHeight: 1.4 }, children: [
|
|
1629
|
+
data.meetingDate,
|
|
1630
|
+
" at ",
|
|
1631
|
+
data.meetingTime
|
|
1632
|
+
] })
|
|
1633
|
+
] }),
|
|
1634
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: detailRowStyle, children: [
|
|
1635
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Clock, { size: 14, color: primaryColor, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1636
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: "13px", color: textColor, lineHeight: 1.4 }, children: [
|
|
1637
|
+
data.meetingDuration,
|
|
1638
|
+
" minutes"
|
|
1639
|
+
] })
|
|
1640
|
+
] }),
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...detailRowStyle, marginBottom: "0" }, children: [
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.MessageSquare, { size: 14, color: primaryColor, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1643
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: "13px", color: textColor, lineHeight: 1.4 }, children: data.meetingPurpose })
|
|
1644
|
+
] })
|
|
1645
|
+
] }),
|
|
1646
|
+
data.timezone && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "5px", marginTop: "8px" }, children: [
|
|
1647
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Globe, { size: 11, color: textMuted, "aria-hidden": "true" }),
|
|
1648
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: "11px", color: textMuted, letterSpacing: "0.01em" }, children: data.timezone })
|
|
1649
|
+
] }),
|
|
1650
|
+
(data.meetLink || data.calendarLink) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1651
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: dividerStyle }),
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
|
|
1653
|
+
data.meetLink && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1654
|
+
"a",
|
|
1655
|
+
{
|
|
1656
|
+
href: data.meetLink,
|
|
1657
|
+
target: "_blank",
|
|
1658
|
+
rel: "noopener noreferrer",
|
|
1659
|
+
style: {
|
|
1660
|
+
...actionButtonBase,
|
|
1661
|
+
background: `linear-gradient(135deg, ${primaryColor}, ${theme?.primaryStrong || "#005eff"})`,
|
|
1662
|
+
color: "#ffffff",
|
|
1663
|
+
boxShadow: `0 2px 8px -2px ${primaryColor}50`
|
|
1664
|
+
},
|
|
1665
|
+
children: [
|
|
1666
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Video, { size: 13, "aria-hidden": "true" }),
|
|
1667
|
+
"Join Meet"
|
|
1668
|
+
]
|
|
1669
|
+
}
|
|
1670
|
+
),
|
|
1671
|
+
data.calendarLink && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1672
|
+
"a",
|
|
1673
|
+
{
|
|
1674
|
+
href: data.calendarLink,
|
|
1675
|
+
target: "_blank",
|
|
1676
|
+
rel: "noopener noreferrer",
|
|
1677
|
+
style: {
|
|
1678
|
+
...actionButtonBase,
|
|
1679
|
+
backgroundColor: isLightTheme ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.07)",
|
|
1680
|
+
color: textColor,
|
|
1681
|
+
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)"
|
|
1682
|
+
},
|
|
1683
|
+
children: [
|
|
1684
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.CalendarPlus, { size: 13, "aria-hidden": "true" }),
|
|
1685
|
+
"Add to Calendar"
|
|
1686
|
+
]
|
|
1687
|
+
}
|
|
1688
|
+
)
|
|
1689
|
+
] })
|
|
1690
|
+
] })
|
|
1691
|
+
] });
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/components/BookingCancelledCard.tsx
|
|
1695
|
+
var import_lucide_react3 = require("lucide-react");
|
|
1696
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1697
|
+
function BookingCancelledCard({ data, theme }) {
|
|
1698
|
+
const bgColor = theme?.background || "#00001a";
|
|
1699
|
+
const isLightTheme = (() => {
|
|
1700
|
+
if (!bgColor.startsWith("#")) return false;
|
|
1701
|
+
const hex = bgColor.replace("#", "");
|
|
1702
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
1703
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
1704
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
1705
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
1706
|
+
})();
|
|
1707
|
+
const negativeColor = theme?.statusNegative || "#ef4444";
|
|
1708
|
+
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1709
|
+
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)");
|
|
1710
|
+
const cardStyle = isLightTheme ? {
|
|
1711
|
+
backgroundColor: "rgba(239,68,68,0.04)",
|
|
1712
|
+
boxShadow: "inset 0 0 0 1px rgba(239,68,68,0.12)",
|
|
1713
|
+
borderRadius: "14px",
|
|
1714
|
+
padding: "16px"
|
|
1715
|
+
} : {
|
|
1716
|
+
backgroundColor: "rgba(239,68,68,0.06)",
|
|
1717
|
+
boxShadow: "inset 0 0 0 0.5px rgba(239,68,68,0.18)",
|
|
1718
|
+
borderRadius: "14px",
|
|
1719
|
+
padding: "16px"
|
|
1720
|
+
};
|
|
1721
|
+
const strikethroughTextStyle = {
|
|
1722
|
+
fontSize: "13px",
|
|
1723
|
+
color: textMuted,
|
|
1724
|
+
lineHeight: 1.4,
|
|
1725
|
+
textDecoration: "line-through",
|
|
1726
|
+
textDecorationColor: isLightTheme ? "rgba(0,0,0,0.25)" : "rgba(255,255,255,0.2)"
|
|
1727
|
+
};
|
|
1728
|
+
const detailRowStyle = {
|
|
1729
|
+
display: "flex",
|
|
1730
|
+
alignItems: "flex-start",
|
|
1731
|
+
gap: "8px",
|
|
1732
|
+
marginBottom: "8px"
|
|
1733
|
+
};
|
|
1734
|
+
const cancelledByLabel = data.cancelledBy === "visitor" ? "Cancelled by you" : "Cancelled by support";
|
|
1735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: cardStyle, children: [
|
|
1736
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginBottom: "14px" }, children: [
|
|
1737
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.CircleX, { size: 22, color: negativeColor, "aria-hidden": "true" }),
|
|
1738
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1739
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1740
|
+
"p",
|
|
1741
|
+
{
|
|
1742
|
+
style: {
|
|
1743
|
+
fontSize: "14px",
|
|
1744
|
+
fontWeight: "700",
|
|
1745
|
+
color: textColor,
|
|
1746
|
+
letterSpacing: "0.006em",
|
|
1747
|
+
lineHeight: 1.3
|
|
1748
|
+
},
|
|
1749
|
+
children: "Meeting Cancelled"
|
|
1750
|
+
}
|
|
1751
|
+
),
|
|
1752
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1753
|
+
"p",
|
|
1754
|
+
{
|
|
1755
|
+
style: {
|
|
1756
|
+
fontSize: "11px",
|
|
1757
|
+
color: negativeColor,
|
|
1758
|
+
letterSpacing: "0.01em",
|
|
1759
|
+
marginTop: "1px",
|
|
1760
|
+
opacity: 0.8
|
|
1761
|
+
},
|
|
1762
|
+
children: cancelledByLabel
|
|
1763
|
+
}
|
|
1764
|
+
)
|
|
1765
|
+
] })
|
|
1766
|
+
] }),
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: detailRowStyle, children: [
|
|
1769
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.Calendar, { size: 14, color: textMuted, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1770
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: strikethroughTextStyle, children: [
|
|
1771
|
+
data.meetingDate,
|
|
1772
|
+
" at ",
|
|
1773
|
+
data.meetingTime
|
|
1774
|
+
] })
|
|
1775
|
+
] }),
|
|
1776
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...detailRowStyle, marginBottom: "0" }, children: [
|
|
1777
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react3.MessageSquare, { size: 14, color: textMuted, "aria-hidden": "true", style: { marginTop: "1px", flexShrink: 0 } }),
|
|
1778
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: strikethroughTextStyle, children: data.meetingPurpose })
|
|
1779
|
+
] })
|
|
1780
|
+
] })
|
|
1781
|
+
] });
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// src/components/MessageItem.tsx
|
|
1785
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1265
1786
|
function MessageItem({
|
|
1266
1787
|
message,
|
|
1267
1788
|
currentUser,
|
|
1268
1789
|
showAvatar = true,
|
|
1269
1790
|
showTimestamp = true,
|
|
1270
|
-
theme
|
|
1791
|
+
theme,
|
|
1792
|
+
onBookingSlotSelected
|
|
1271
1793
|
}) {
|
|
1272
1794
|
const isOwnMessage = message.senderType === currentUser.type;
|
|
1273
1795
|
const isSystemMessage = message.senderType === "system";
|
|
@@ -1286,8 +1808,63 @@ function MessageItem({
|
|
|
1286
1808
|
const primaryStrong = theme?.primaryStrong || "#005eff";
|
|
1287
1809
|
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1288
1810
|
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.35)" : "rgba(247,247,248,0.35)");
|
|
1811
|
+
if (message.messageType === "booking_slots" || message.messageType === "booking_confirmation" || message.messageType === "booking_cancelled") {
|
|
1812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-2.5 mb-3 flex-row", children: [
|
|
1813
|
+
showAvatar && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-shrink-0 mt-auto mb-5", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(XcelsiorAvatar, { size: 28 }) }),
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col max-w-[85%] items-start", children: [
|
|
1815
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1816
|
+
"span",
|
|
1817
|
+
{
|
|
1818
|
+
className: "mb-1 px-1 font-medium",
|
|
1819
|
+
style: {
|
|
1820
|
+
color: isLightTheme ? "rgba(0,0,0,0.45)" : "rgba(247,247,248,0.4)",
|
|
1821
|
+
fontSize: "11px",
|
|
1822
|
+
letterSpacing: "0.019em"
|
|
1823
|
+
},
|
|
1824
|
+
children: "AI Assistant"
|
|
1825
|
+
}
|
|
1826
|
+
),
|
|
1827
|
+
message.messageType === "booking_slots" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1828
|
+
BookingSlotPicker,
|
|
1829
|
+
{
|
|
1830
|
+
data: message.metadata?.bookingData,
|
|
1831
|
+
theme,
|
|
1832
|
+
onSlotSelected: (date, time) => {
|
|
1833
|
+
onBookingSlotSelected?.(date, time, message.id);
|
|
1834
|
+
},
|
|
1835
|
+
disabled: !!message.metadata?.slotSelected
|
|
1836
|
+
}
|
|
1837
|
+
),
|
|
1838
|
+
message.messageType === "booking_confirmation" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1839
|
+
BookingConfirmationCard,
|
|
1840
|
+
{
|
|
1841
|
+
data: message.metadata?.bookingConfirmation,
|
|
1842
|
+
theme
|
|
1843
|
+
}
|
|
1844
|
+
),
|
|
1845
|
+
message.messageType === "booking_cancelled" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1846
|
+
BookingCancelledCard,
|
|
1847
|
+
{
|
|
1848
|
+
data: message.metadata?.bookingCancelled,
|
|
1849
|
+
theme
|
|
1850
|
+
}
|
|
1851
|
+
),
|
|
1852
|
+
showTimestamp && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-1.5 mt-1 px-1", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1853
|
+
"span",
|
|
1854
|
+
{
|
|
1855
|
+
style: {
|
|
1856
|
+
fontSize: "11px",
|
|
1857
|
+
letterSpacing: "0.019em",
|
|
1858
|
+
color: textMuted
|
|
1859
|
+
},
|
|
1860
|
+
children: (0, import_date_fns.formatDistanceToNow)(new Date(message.createdAt), { addSuffix: true })
|
|
1861
|
+
}
|
|
1862
|
+
) })
|
|
1863
|
+
] })
|
|
1864
|
+
] });
|
|
1865
|
+
}
|
|
1289
1866
|
if (isSystemMessage) {
|
|
1290
|
-
return /* @__PURE__ */ (0,
|
|
1867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex justify-center my-3", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1291
1868
|
"div",
|
|
1292
1869
|
{
|
|
1293
1870
|
className: "px-4 py-1.5 rounded-full",
|
|
@@ -1295,7 +1872,7 @@ function MessageItem({
|
|
|
1295
1872
|
backgroundColor: isLightTheme ? "rgba(0,0,0,0.04)" : "rgba(255,255,255,0.03)",
|
|
1296
1873
|
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)"
|
|
1297
1874
|
},
|
|
1298
|
-
children: /* @__PURE__ */ (0,
|
|
1875
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1299
1876
|
"p",
|
|
1300
1877
|
{
|
|
1301
1878
|
style: {
|
|
@@ -1334,12 +1911,12 @@ function MessageItem({
|
|
|
1334
1911
|
borderRadius: "18px 18px 18px 4px",
|
|
1335
1912
|
boxShadow: "inset 0 0 0 0.5px rgba(255,255,255,0.06), inset 0 1px 0 0 rgba(255,255,255,0.08)"
|
|
1336
1913
|
};
|
|
1337
|
-
return /* @__PURE__ */ (0,
|
|
1914
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1338
1915
|
"div",
|
|
1339
1916
|
{
|
|
1340
1917
|
className: `flex gap-2.5 mb-3 ${isOwnMessage ? "flex-row-reverse" : "flex-row"}`,
|
|
1341
1918
|
children: [
|
|
1342
|
-
showAvatar && !isOwnMessage && /* @__PURE__ */ (0,
|
|
1919
|
+
showAvatar && !isOwnMessage && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-shrink-0 mt-auto mb-5", children: isBotMessage || isAIMessage ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(XcelsiorAvatar, { size: 28 }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1343
1920
|
"div",
|
|
1344
1921
|
{
|
|
1345
1922
|
className: "h-7 w-7 rounded-full flex items-center justify-center",
|
|
@@ -1347,34 +1924,24 @@ function MessageItem({
|
|
|
1347
1924
|
background: isLightTheme ? `linear-gradient(135deg, ${primaryColor}30, rgba(0,0,0,0.04))` : `linear-gradient(135deg, ${primaryColor}60, rgba(255,255,255,0.06))`,
|
|
1348
1925
|
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)"
|
|
1349
1926
|
},
|
|
1350
|
-
children: /* @__PURE__ */ (0,
|
|
1351
|
-
|
|
1927
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1928
|
+
import_lucide_react4.Headphones,
|
|
1352
1929
|
{
|
|
1353
|
-
|
|
1354
|
-
height: "14",
|
|
1355
|
-
viewBox: "0 0 24 24",
|
|
1356
|
-
fill: "none",
|
|
1930
|
+
size: 14,
|
|
1357
1931
|
stroke: isLightTheme ? primaryColor : "white",
|
|
1358
|
-
strokeWidth:
|
|
1359
|
-
|
|
1360
|
-
strokeLinejoin: "round",
|
|
1361
|
-
"aria-hidden": "true",
|
|
1362
|
-
children: [
|
|
1363
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("title", { children: "Agent" }),
|
|
1364
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M3 18v-6a9 9 0 0 1 18 0v6" }),
|
|
1365
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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" })
|
|
1366
|
-
]
|
|
1932
|
+
strokeWidth: 2,
|
|
1933
|
+
"aria-hidden": "true"
|
|
1367
1934
|
}
|
|
1368
1935
|
)
|
|
1369
1936
|
}
|
|
1370
1937
|
) }),
|
|
1371
|
-
showAvatar && isOwnMessage && /* @__PURE__ */ (0,
|
|
1372
|
-
/* @__PURE__ */ (0,
|
|
1938
|
+
showAvatar && isOwnMessage && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "w-7 flex-shrink-0" }),
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1373
1940
|
"div",
|
|
1374
1941
|
{
|
|
1375
1942
|
className: `flex flex-col max-w-[75%] ${isOwnMessage ? "items-end" : "items-start"}`,
|
|
1376
1943
|
children: [
|
|
1377
|
-
senderLabel && /* @__PURE__ */ (0,
|
|
1944
|
+
senderLabel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1378
1945
|
"span",
|
|
1379
1946
|
{
|
|
1380
1947
|
className: "mb-1 px-1 font-medium",
|
|
@@ -1386,7 +1953,7 @@ function MessageItem({
|
|
|
1386
1953
|
children: senderLabel
|
|
1387
1954
|
}
|
|
1388
1955
|
),
|
|
1389
|
-
/* @__PURE__ */ (0,
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1390
1957
|
"div",
|
|
1391
1958
|
{
|
|
1392
1959
|
className: "px-4 py-2.5",
|
|
@@ -1399,12 +1966,12 @@ function MessageItem({
|
|
|
1399
1966
|
children: [
|
|
1400
1967
|
message.messageType === "text" && (isOwnMessage ? (
|
|
1401
1968
|
// User messages: plain text — no markdown parsing
|
|
1402
|
-
/* @__PURE__ */ (0,
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: message.content })
|
|
1403
1970
|
) : (
|
|
1404
1971
|
// Bot / agent messages: full markdown rendering
|
|
1405
|
-
/* @__PURE__ */ (0,
|
|
1972
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MarkdownMessage, { content: message.content, theme })
|
|
1406
1973
|
)),
|
|
1407
|
-
message.messageType === "image" && /* @__PURE__ */ (0,
|
|
1974
|
+
message.messageType === "image" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1408
1975
|
"img",
|
|
1409
1976
|
{
|
|
1410
1977
|
src: message.content,
|
|
@@ -1413,26 +1980,9 @@ function MessageItem({
|
|
|
1413
1980
|
loading: "lazy"
|
|
1414
1981
|
}
|
|
1415
1982
|
) }),
|
|
1416
|
-
message.messageType === "file" && /* @__PURE__ */ (0,
|
|
1417
|
-
/* @__PURE__ */ (0,
|
|
1418
|
-
|
|
1419
|
-
{
|
|
1420
|
-
width: "18",
|
|
1421
|
-
height: "18",
|
|
1422
|
-
viewBox: "0 0 24 24",
|
|
1423
|
-
fill: "none",
|
|
1424
|
-
stroke: "currentColor",
|
|
1425
|
-
strokeWidth: "1.75",
|
|
1426
|
-
strokeLinecap: "round",
|
|
1427
|
-
strokeLinejoin: "round",
|
|
1428
|
-
"aria-hidden": "true",
|
|
1429
|
-
children: [
|
|
1430
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("title", { children: "File" }),
|
|
1431
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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" })
|
|
1432
|
-
]
|
|
1433
|
-
}
|
|
1434
|
-
),
|
|
1435
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1983
|
+
message.messageType === "file" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Paperclip, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" }),
|
|
1985
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1436
1986
|
"a",
|
|
1437
1987
|
{
|
|
1438
1988
|
href: message.content,
|
|
@@ -1450,12 +2000,12 @@ function MessageItem({
|
|
|
1450
2000
|
]
|
|
1451
2001
|
}
|
|
1452
2002
|
),
|
|
1453
|
-
showTimestamp && /* @__PURE__ */ (0,
|
|
2003
|
+
showTimestamp && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1454
2004
|
"div",
|
|
1455
2005
|
{
|
|
1456
2006
|
className: `flex items-center gap-1.5 mt-1 px-1 ${isOwnMessage ? "flex-row-reverse" : "flex-row"}`,
|
|
1457
2007
|
children: [
|
|
1458
|
-
/* @__PURE__ */ (0,
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1459
2009
|
"span",
|
|
1460
2010
|
{
|
|
1461
2011
|
style: {
|
|
@@ -1468,63 +2018,10 @@ function MessageItem({
|
|
|
1468
2018
|
})
|
|
1469
2019
|
}
|
|
1470
2020
|
),
|
|
1471
|
-
isOwnMessage && message.status && /* @__PURE__ */ (0,
|
|
1472
|
-
message.status === "sent" && /* @__PURE__ */ (0,
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
width: "14",
|
|
1476
|
-
height: "14",
|
|
1477
|
-
viewBox: "0 0 24 24",
|
|
1478
|
-
fill: "none",
|
|
1479
|
-
stroke: "currentColor",
|
|
1480
|
-
strokeWidth: "2.5",
|
|
1481
|
-
strokeLinecap: "round",
|
|
1482
|
-
strokeLinejoin: "round",
|
|
1483
|
-
"aria-hidden": "true",
|
|
1484
|
-
children: [
|
|
1485
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("title", { children: "Sent" }),
|
|
1486
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "20 6 9 17 4 12" })
|
|
1487
|
-
]
|
|
1488
|
-
}
|
|
1489
|
-
),
|
|
1490
|
-
message.status === "delivered" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1491
|
-
"svg",
|
|
1492
|
-
{
|
|
1493
|
-
width: "14",
|
|
1494
|
-
height: "14",
|
|
1495
|
-
viewBox: "0 0 24 24",
|
|
1496
|
-
fill: "none",
|
|
1497
|
-
stroke: "currentColor",
|
|
1498
|
-
strokeWidth: "2.5",
|
|
1499
|
-
strokeLinecap: "round",
|
|
1500
|
-
strokeLinejoin: "round",
|
|
1501
|
-
"aria-hidden": "true",
|
|
1502
|
-
children: [
|
|
1503
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("title", { children: "Delivered" }),
|
|
1504
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "18 6 7 17 2 12" }),
|
|
1505
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "22 6 11 17" })
|
|
1506
|
-
]
|
|
1507
|
-
}
|
|
1508
|
-
),
|
|
1509
|
-
message.status === "read" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1510
|
-
"svg",
|
|
1511
|
-
{
|
|
1512
|
-
width: "14",
|
|
1513
|
-
height: "14",
|
|
1514
|
-
viewBox: "0 0 24 24",
|
|
1515
|
-
fill: "none",
|
|
1516
|
-
stroke: primaryColor,
|
|
1517
|
-
strokeWidth: "2.5",
|
|
1518
|
-
strokeLinecap: "round",
|
|
1519
|
-
strokeLinejoin: "round",
|
|
1520
|
-
"aria-hidden": "true",
|
|
1521
|
-
children: [
|
|
1522
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("title", { children: "Read" }),
|
|
1523
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "18 6 7 17 2 12" }),
|
|
1524
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "22 6 11 17" })
|
|
1525
|
-
]
|
|
1526
|
-
}
|
|
1527
|
-
)
|
|
2021
|
+
isOwnMessage && message.status && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { color: textMuted }, children: [
|
|
2022
|
+
message.status === "sent" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.Check, { size: 14, strokeWidth: 2.5, "aria-hidden": "true" }),
|
|
2023
|
+
message.status === "delivered" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.CheckCheck, { size: 14, strokeWidth: 2.5, "aria-hidden": "true" }),
|
|
2024
|
+
message.status === "read" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.CheckCheck, { size: 14, strokeWidth: 2.5, stroke: primaryColor, "aria-hidden": "true" })
|
|
1528
2025
|
] })
|
|
1529
2026
|
]
|
|
1530
2027
|
}
|
|
@@ -1538,8 +2035,8 @@ function MessageItem({
|
|
|
1538
2035
|
}
|
|
1539
2036
|
|
|
1540
2037
|
// src/components/ThinkingIndicator.tsx
|
|
1541
|
-
var
|
|
1542
|
-
var
|
|
2038
|
+
var import_react7 = require("react");
|
|
2039
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1543
2040
|
var PHRASE_POOLS = {
|
|
1544
2041
|
// ── Greetings & small talk ──
|
|
1545
2042
|
greeting: [
|
|
@@ -1722,17 +2219,17 @@ function ThinkingIndicator({
|
|
|
1722
2219
|
const isLightTheme = computeIsLightTheme2(theme?.background);
|
|
1723
2220
|
const primaryColor = theme?.primary || "#337eff";
|
|
1724
2221
|
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.4)" : "rgba(247,247,248,0.45)");
|
|
1725
|
-
const [phraseIndex, setPhraseIndex] = (0,
|
|
1726
|
-
const [displayText, setDisplayText] = (0,
|
|
1727
|
-
const [isDeleting, setIsDeleting] = (0,
|
|
1728
|
-
const phrasesRef = (0,
|
|
1729
|
-
(0,
|
|
2222
|
+
const [phraseIndex, setPhraseIndex] = (0, import_react7.useState)(0);
|
|
2223
|
+
const [displayText, setDisplayText] = (0, import_react7.useState)("");
|
|
2224
|
+
const [isDeleting, setIsDeleting] = (0, import_react7.useState)(false);
|
|
2225
|
+
const phrasesRef = (0, import_react7.useRef)(getShuffledPhrases(detectContext(lastUserMessage)));
|
|
2226
|
+
(0, import_react7.useEffect)(() => {
|
|
1730
2227
|
phrasesRef.current = getShuffledPhrases(detectContext(lastUserMessage));
|
|
1731
2228
|
setPhraseIndex(0);
|
|
1732
2229
|
setDisplayText("");
|
|
1733
2230
|
setIsDeleting(false);
|
|
1734
2231
|
}, [lastUserMessage]);
|
|
1735
|
-
(0,
|
|
2232
|
+
(0, import_react7.useEffect)(() => {
|
|
1736
2233
|
const phrases = phrasesRef.current;
|
|
1737
2234
|
const phrase = phrases[phraseIndex % phrases.length];
|
|
1738
2235
|
let timeout;
|
|
@@ -1758,7 +2255,7 @@ function ThinkingIndicator({
|
|
|
1758
2255
|
}
|
|
1759
2256
|
return () => clearTimeout(timeout);
|
|
1760
2257
|
}, [displayText, isDeleting, phraseIndex]);
|
|
1761
|
-
return /* @__PURE__ */ (0,
|
|
2258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1762
2259
|
"div",
|
|
1763
2260
|
{
|
|
1764
2261
|
className: "flex gap-2.5 mb-3",
|
|
@@ -1766,8 +2263,8 @@ function ThinkingIndicator({
|
|
|
1766
2263
|
"aria-live": "polite",
|
|
1767
2264
|
"aria-label": "Xcelsior is thinking",
|
|
1768
2265
|
children: [
|
|
1769
|
-
showAvatar && /* @__PURE__ */ (0,
|
|
1770
|
-
/* @__PURE__ */ (0,
|
|
2266
|
+
showAvatar && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex-shrink-0 mt-auto mb-5", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(XcelsiorAvatar, { size: 28 }) }),
|
|
2267
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex flex-col items-start", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1771
2268
|
"div",
|
|
1772
2269
|
{
|
|
1773
2270
|
style: {
|
|
@@ -1777,8 +2274,8 @@ function ThinkingIndicator({
|
|
|
1777
2274
|
padding: "12px 16px",
|
|
1778
2275
|
minWidth: 160
|
|
1779
2276
|
},
|
|
1780
|
-
children: /* @__PURE__ */ (0,
|
|
1781
|
-
/* @__PURE__ */ (0,
|
|
2277
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
2278
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: { display: "flex", gap: 4, alignItems: "center" }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1782
2279
|
"span",
|
|
1783
2280
|
{
|
|
1784
2281
|
style: {
|
|
@@ -1792,7 +2289,7 @@ function ThinkingIndicator({
|
|
|
1792
2289
|
},
|
|
1793
2290
|
i
|
|
1794
2291
|
)) }),
|
|
1795
|
-
/* @__PURE__ */ (0,
|
|
2292
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1796
2293
|
"span",
|
|
1797
2294
|
{
|
|
1798
2295
|
style: {
|
|
@@ -1803,7 +2300,7 @@ function ThinkingIndicator({
|
|
|
1803
2300
|
},
|
|
1804
2301
|
children: [
|
|
1805
2302
|
displayText,
|
|
1806
|
-
/* @__PURE__ */ (0,
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1807
2304
|
"span",
|
|
1808
2305
|
{
|
|
1809
2306
|
style: {
|
|
@@ -1829,7 +2326,7 @@ function ThinkingIndicator({
|
|
|
1829
2326
|
}
|
|
1830
2327
|
|
|
1831
2328
|
// src/components/MessageList.tsx
|
|
1832
|
-
var
|
|
2329
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1833
2330
|
function MessageList({
|
|
1834
2331
|
messages,
|
|
1835
2332
|
currentUser,
|
|
@@ -1842,15 +2339,16 @@ function MessageList({
|
|
|
1842
2339
|
isLoadingMore = false,
|
|
1843
2340
|
theme,
|
|
1844
2341
|
onQuickAction,
|
|
1845
|
-
isBotThinking = false
|
|
2342
|
+
isBotThinking = false,
|
|
2343
|
+
onBookingSlotSelected
|
|
1846
2344
|
}) {
|
|
1847
|
-
const messagesEndRef = (0,
|
|
1848
|
-
const containerRef = (0,
|
|
1849
|
-
const prevLengthRef = (0,
|
|
1850
|
-
const loadMoreTriggerRef = (0,
|
|
1851
|
-
const prevScrollHeightRef = (0,
|
|
1852
|
-
const hasInitialScrolledRef = (0,
|
|
1853
|
-
const isUserScrollingRef = (0,
|
|
2345
|
+
const messagesEndRef = (0, import_react8.useRef)(null);
|
|
2346
|
+
const containerRef = (0, import_react8.useRef)(null);
|
|
2347
|
+
const prevLengthRef = (0, import_react8.useRef)(messages.length);
|
|
2348
|
+
const loadMoreTriggerRef = (0, import_react8.useRef)(null);
|
|
2349
|
+
const prevScrollHeightRef = (0, import_react8.useRef)(0);
|
|
2350
|
+
const hasInitialScrolledRef = (0, import_react8.useRef)(false);
|
|
2351
|
+
const isUserScrollingRef = (0, import_react8.useRef)(false);
|
|
1854
2352
|
const bgColor = theme?.background || "#00001a";
|
|
1855
2353
|
const isLightTheme = (() => {
|
|
1856
2354
|
if (!bgColor.startsWith("#")) return false;
|
|
@@ -1863,7 +2361,7 @@ function MessageList({
|
|
|
1863
2361
|
const primaryColor = theme?.primary || "#337eff";
|
|
1864
2362
|
const textColor = theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
1865
2363
|
const textMuted = theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.4)" : "rgba(247,247,248,0.45)");
|
|
1866
|
-
(0,
|
|
2364
|
+
(0, import_react8.useEffect)(() => {
|
|
1867
2365
|
if (autoScroll && messagesEndRef.current) {
|
|
1868
2366
|
if (messages.length > prevLengthRef.current && !isLoadingMore) {
|
|
1869
2367
|
messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
|
|
@@ -1871,7 +2369,7 @@ function MessageList({
|
|
|
1871
2369
|
prevLengthRef.current = messages.length;
|
|
1872
2370
|
}
|
|
1873
2371
|
}, [messages.length, autoScroll, isLoadingMore]);
|
|
1874
|
-
(0,
|
|
2372
|
+
(0, import_react8.useEffect)(() => {
|
|
1875
2373
|
if (messages.length > 0 && messagesEndRef.current && !isLoading && !hasInitialScrolledRef.current) {
|
|
1876
2374
|
setTimeout(() => {
|
|
1877
2375
|
messagesEndRef.current?.scrollIntoView({ behavior: "auto" });
|
|
@@ -1885,7 +2383,7 @@ function MessageList({
|
|
|
1885
2383
|
hasInitialScrolledRef.current = true;
|
|
1886
2384
|
}
|
|
1887
2385
|
}, [isLoading, messages.length]);
|
|
1888
|
-
(0,
|
|
2386
|
+
(0, import_react8.useEffect)(() => {
|
|
1889
2387
|
if (isLoadingMore) {
|
|
1890
2388
|
prevScrollHeightRef.current = containerRef.current?.scrollHeight || 0;
|
|
1891
2389
|
} else if (prevScrollHeightRef.current > 0 && containerRef.current) {
|
|
@@ -1895,7 +2393,7 @@ function MessageList({
|
|
|
1895
2393
|
prevScrollHeightRef.current = 0;
|
|
1896
2394
|
}
|
|
1897
2395
|
}, [isLoadingMore]);
|
|
1898
|
-
const handleScroll = (0,
|
|
2396
|
+
const handleScroll = (0, import_react8.useCallback)(() => {
|
|
1899
2397
|
if (!containerRef.current || !onLoadMore || !hasMore || isLoadingMore) return;
|
|
1900
2398
|
if (!isUserScrollingRef.current) return;
|
|
1901
2399
|
const { scrollTop } = containerRef.current;
|
|
@@ -1903,18 +2401,18 @@ function MessageList({
|
|
|
1903
2401
|
onLoadMore();
|
|
1904
2402
|
}
|
|
1905
2403
|
}, [onLoadMore, hasMore, isLoadingMore]);
|
|
1906
|
-
(0,
|
|
2404
|
+
(0, import_react8.useEffect)(() => {
|
|
1907
2405
|
const container = containerRef.current;
|
|
1908
2406
|
if (!container) return;
|
|
1909
2407
|
container.addEventListener("scroll", handleScroll);
|
|
1910
2408
|
return () => container.removeEventListener("scroll", handleScroll);
|
|
1911
2409
|
}, [handleScroll]);
|
|
1912
2410
|
if (isLoading) {
|
|
1913
|
-
return /* @__PURE__ */ (0,
|
|
2411
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { size: "lg" }) });
|
|
1914
2412
|
}
|
|
1915
2413
|
if (messages.length === 0) {
|
|
1916
|
-
return /* @__PURE__ */ (0,
|
|
1917
|
-
/* @__PURE__ */ (0,
|
|
2414
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-center justify-center h-full text-center", style: { padding: "40px 32px" }, children: [
|
|
2415
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1918
2416
|
"h3",
|
|
1919
2417
|
{
|
|
1920
2418
|
className: "font-semibold mb-2",
|
|
@@ -1926,7 +2424,7 @@ function MessageList({
|
|
|
1926
2424
|
children: "How can we help?"
|
|
1927
2425
|
}
|
|
1928
2426
|
),
|
|
1929
|
-
/* @__PURE__ */ (0,
|
|
2427
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1930
2428
|
"p",
|
|
1931
2429
|
{
|
|
1932
2430
|
className: "max-w-[240px]",
|
|
@@ -1940,11 +2438,11 @@ function MessageList({
|
|
|
1940
2438
|
children: "Ask us anything. We are here to help you get the most out of Xcelsior."
|
|
1941
2439
|
}
|
|
1942
2440
|
),
|
|
1943
|
-
/* @__PURE__ */ (0,
|
|
2441
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap justify-center gap-2", children: [
|
|
1944
2442
|
{ label: "Our services", message: "What services does Xcelsior offer?" },
|
|
1945
2443
|
{ label: "Get a quote", message: "I would like to get a quote for a project" },
|
|
1946
2444
|
{ label: "Support", message: "I need help with something" }
|
|
1947
|
-
].map((action) => /* @__PURE__ */ (0,
|
|
2445
|
+
].map((action) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1948
2446
|
"button",
|
|
1949
2447
|
{
|
|
1950
2448
|
type: "button",
|
|
@@ -1976,14 +2474,14 @@ function MessageList({
|
|
|
1976
2474
|
)) })
|
|
1977
2475
|
] });
|
|
1978
2476
|
}
|
|
1979
|
-
return /* @__PURE__ */ (0,
|
|
2477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1980
2478
|
"div",
|
|
1981
2479
|
{
|
|
1982
2480
|
ref: containerRef,
|
|
1983
2481
|
className: "flex-1 overflow-y-auto px-4 py-3",
|
|
1984
2482
|
style: { scrollBehavior: "smooth" },
|
|
1985
2483
|
children: [
|
|
1986
|
-
/* @__PURE__ */ (0,
|
|
2484
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("style", { children: `
|
|
1987
2485
|
@keyframes thinkingPulse {
|
|
1988
2486
|
0%, 60%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
1989
2487
|
30% { opacity: 1; transform: scale(1); }
|
|
@@ -1993,23 +2491,24 @@ function MessageList({
|
|
|
1993
2491
|
50% { opacity: 0; }
|
|
1994
2492
|
}
|
|
1995
2493
|
` }),
|
|
1996
|
-
isLoadingMore && /* @__PURE__ */ (0,
|
|
1997
|
-
/* @__PURE__ */ (0,
|
|
1998
|
-
messages.map((message) => /* @__PURE__ */ (0,
|
|
2494
|
+
isLoadingMore && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex justify-center py-3", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Spinner, { size: "sm" }) }),
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: loadMoreTriggerRef }),
|
|
2496
|
+
messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1999
2497
|
MessageItem,
|
|
2000
2498
|
{
|
|
2001
2499
|
message,
|
|
2002
2500
|
currentUser,
|
|
2003
2501
|
showAvatar: true,
|
|
2004
2502
|
showTimestamp: true,
|
|
2005
|
-
theme
|
|
2503
|
+
theme,
|
|
2504
|
+
onBookingSlotSelected
|
|
2006
2505
|
},
|
|
2007
2506
|
message.id
|
|
2008
2507
|
)),
|
|
2009
|
-
isTyping && /* @__PURE__ */ (0,
|
|
2010
|
-
/* @__PURE__ */ (0,
|
|
2011
|
-
/* @__PURE__ */ (0,
|
|
2012
|
-
/* @__PURE__ */ (0,
|
|
2508
|
+
isTyping && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex gap-2.5 mb-3", children: [
|
|
2509
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-shrink-0 mt-auto mb-5", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(XcelsiorAvatar, { size: 28 }) }),
|
|
2510
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-start", children: [
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2013
2512
|
"div",
|
|
2014
2513
|
{
|
|
2015
2514
|
className: "px-4 py-3",
|
|
@@ -2018,7 +2517,7 @@ function MessageList({
|
|
|
2018
2517
|
borderRadius: "18px 18px 18px 4px",
|
|
2019
2518
|
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)"
|
|
2020
2519
|
},
|
|
2021
|
-
children: /* @__PURE__ */ (0,
|
|
2520
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex gap-1.5 items-center", style: { height: 16 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2022
2521
|
"span",
|
|
2023
2522
|
{
|
|
2024
2523
|
className: "rounded-full animate-bounce",
|
|
@@ -2034,7 +2533,7 @@ function MessageList({
|
|
|
2034
2533
|
)) })
|
|
2035
2534
|
}
|
|
2036
2535
|
),
|
|
2037
|
-
typingUser && /* @__PURE__ */ (0,
|
|
2536
|
+
typingUser && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2038
2537
|
"span",
|
|
2039
2538
|
{
|
|
2040
2539
|
className: "mt-1 px-1",
|
|
@@ -2051,24 +2550,24 @@ function MessageList({
|
|
|
2051
2550
|
)
|
|
2052
2551
|
] })
|
|
2053
2552
|
] }),
|
|
2054
|
-
isBotThinking && !isTyping && /* @__PURE__ */ (0,
|
|
2553
|
+
isBotThinking && !isTyping && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2055
2554
|
ThinkingIndicator,
|
|
2056
2555
|
{
|
|
2057
2556
|
theme,
|
|
2058
2557
|
lastUserMessage: messages.filter((m) => m.senderType === "customer").pop()?.content
|
|
2059
2558
|
}
|
|
2060
2559
|
),
|
|
2061
|
-
/* @__PURE__ */ (0,
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { ref: messagesEndRef })
|
|
2062
2561
|
]
|
|
2063
2562
|
}
|
|
2064
2563
|
);
|
|
2065
2564
|
}
|
|
2066
2565
|
|
|
2067
2566
|
// src/components/ChatInput.tsx
|
|
2068
|
-
var
|
|
2567
|
+
var import_react9 = require("react");
|
|
2069
2568
|
var import_react_dom = require("react-dom");
|
|
2070
|
-
var
|
|
2071
|
-
var
|
|
2569
|
+
var import_react10 = __toESM(require("@emoji-mart/react"));
|
|
2570
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2072
2571
|
function isLightColor(color) {
|
|
2073
2572
|
let r = 0, g = 0, b = 0;
|
|
2074
2573
|
if (color.startsWith("#")) {
|
|
@@ -2086,18 +2585,18 @@ function ChatInput({
|
|
|
2086
2585
|
fileUpload,
|
|
2087
2586
|
disabled = false
|
|
2088
2587
|
}) {
|
|
2089
|
-
const [message, setMessage] = (0,
|
|
2090
|
-
const [showEmojiPicker, setShowEmojiPicker] = (0,
|
|
2091
|
-
const [emojiData, setEmojiData] = (0,
|
|
2092
|
-
const [emojiPickerPosition, setEmojiPickerPosition] = (0,
|
|
2093
|
-
const [isFocused, setIsFocused] = (0,
|
|
2094
|
-
const textAreaRef = (0,
|
|
2095
|
-
const emojiPickerRef = (0,
|
|
2096
|
-
const emojiButtonRef = (0,
|
|
2097
|
-
const fileInputRef = (0,
|
|
2098
|
-
const typingTimeoutRef = (0,
|
|
2099
|
-
const startTypingTimeoutRef = (0,
|
|
2100
|
-
const isTypingRef = (0,
|
|
2588
|
+
const [message, setMessage] = (0, import_react9.useState)("");
|
|
2589
|
+
const [showEmojiPicker, setShowEmojiPicker] = (0, import_react9.useState)(false);
|
|
2590
|
+
const [emojiData, setEmojiData] = (0, import_react9.useState)();
|
|
2591
|
+
const [emojiPickerPosition, setEmojiPickerPosition] = (0, import_react9.useState)(null);
|
|
2592
|
+
const [isFocused, setIsFocused] = (0, import_react9.useState)(false);
|
|
2593
|
+
const textAreaRef = (0, import_react9.useRef)(null);
|
|
2594
|
+
const emojiPickerRef = (0, import_react9.useRef)(null);
|
|
2595
|
+
const emojiButtonRef = (0, import_react9.useRef)(null);
|
|
2596
|
+
const fileInputRef = (0, import_react9.useRef)(null);
|
|
2597
|
+
const typingTimeoutRef = (0, import_react9.useRef)(null);
|
|
2598
|
+
const startTypingTimeoutRef = (0, import_react9.useRef)(null);
|
|
2599
|
+
const isTypingRef = (0, import_react9.useRef)(false);
|
|
2101
2600
|
const enableEmoji = config.enableEmoji ?? true;
|
|
2102
2601
|
const enableFileUpload = config.enableFileUpload ?? true;
|
|
2103
2602
|
const bgColor = config.theme?.background || "#00001a";
|
|
@@ -2105,7 +2604,7 @@ function ChatInput({
|
|
|
2105
2604
|
const textColor = config.theme?.text || (isLightTheme ? "#1a1a2e" : "#f7f7f8");
|
|
2106
2605
|
const textMuted = config.theme?.textMuted || (isLightTheme ? "rgba(0,0,0,0.4)" : "rgba(247,247,248,0.45)");
|
|
2107
2606
|
const primaryColor = config.theme?.primary || "#337eff";
|
|
2108
|
-
(0,
|
|
2607
|
+
(0, import_react9.useEffect)(() => {
|
|
2109
2608
|
if (!enableEmoji) return;
|
|
2110
2609
|
(async () => {
|
|
2111
2610
|
try {
|
|
@@ -2116,7 +2615,7 @@ function ChatInput({
|
|
|
2116
2615
|
}
|
|
2117
2616
|
})();
|
|
2118
2617
|
}, [enableEmoji]);
|
|
2119
|
-
(0,
|
|
2618
|
+
(0, import_react9.useEffect)(() => {
|
|
2120
2619
|
const handleClickOutside = (event) => {
|
|
2121
2620
|
if (emojiPickerRef.current && !emojiPickerRef.current.contains(event.target)) {
|
|
2122
2621
|
setShowEmojiPicker(false);
|
|
@@ -2129,7 +2628,7 @@ function ChatInput({
|
|
|
2129
2628
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
2130
2629
|
};
|
|
2131
2630
|
}, [showEmojiPicker]);
|
|
2132
|
-
(0,
|
|
2631
|
+
(0, import_react9.useEffect)(() => {
|
|
2133
2632
|
return () => {
|
|
2134
2633
|
if (typingTimeoutRef.current) {
|
|
2135
2634
|
clearTimeout(typingTimeoutRef.current);
|
|
@@ -2139,7 +2638,7 @@ function ChatInput({
|
|
|
2139
2638
|
}
|
|
2140
2639
|
};
|
|
2141
2640
|
}, []);
|
|
2142
|
-
(0,
|
|
2641
|
+
(0, import_react9.useEffect)(() => {
|
|
2143
2642
|
if (!showEmojiPicker) return;
|
|
2144
2643
|
const updatePosition = () => {
|
|
2145
2644
|
if (emojiButtonRef.current) {
|
|
@@ -2246,7 +2745,7 @@ ${uploadedFile.markdown}
|
|
|
2246
2745
|
};
|
|
2247
2746
|
const canSend = message.trim().length > 0 && !disabled;
|
|
2248
2747
|
const placeholderColor = isLightTheme ? "rgba(0,0,0,0.35)" : "rgba(247,247,248,0.3)";
|
|
2249
|
-
return /* @__PURE__ */ (0,
|
|
2748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2250
2749
|
"div",
|
|
2251
2750
|
{
|
|
2252
2751
|
className: "px-4 py-3",
|
|
@@ -2254,7 +2753,7 @@ ${uploadedFile.markdown}
|
|
|
2254
2753
|
borderTop: isLightTheme ? "1px solid rgba(0,0,0,0.06)" : "1px solid rgba(255,255,255,0.06)"
|
|
2255
2754
|
},
|
|
2256
2755
|
children: [
|
|
2257
|
-
/* @__PURE__ */ (0,
|
|
2756
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2258
2757
|
"div",
|
|
2259
2758
|
{
|
|
2260
2759
|
className: "relative flex items-center rounded-full transition-all duration-200",
|
|
@@ -2266,8 +2765,8 @@ ${uploadedFile.markdown}
|
|
|
2266
2765
|
backdropFilter: "blur(32px)"
|
|
2267
2766
|
},
|
|
2268
2767
|
children: [
|
|
2269
|
-
/* @__PURE__ */ (0,
|
|
2270
|
-
/* @__PURE__ */ (0,
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: `.xchat-input::placeholder { color: ${placeholderColor}; }` }),
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2271
2770
|
"textarea",
|
|
2272
2771
|
{
|
|
2273
2772
|
ref: textAreaRef,
|
|
@@ -2296,8 +2795,8 @@ ${uploadedFile.markdown}
|
|
|
2296
2795
|
disabled
|
|
2297
2796
|
}
|
|
2298
2797
|
),
|
|
2299
|
-
/* @__PURE__ */ (0,
|
|
2300
|
-
enableEmoji && /* @__PURE__ */ (0,
|
|
2798
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1 px-2 shrink-0", children: [
|
|
2799
|
+
enableEmoji && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2301
2800
|
"button",
|
|
2302
2801
|
{
|
|
2303
2802
|
ref: emojiButtonRef,
|
|
@@ -2327,7 +2826,7 @@ ${uploadedFile.markdown}
|
|
|
2327
2826
|
},
|
|
2328
2827
|
disabled,
|
|
2329
2828
|
"aria-label": "Add emoji",
|
|
2330
|
-
children: /* @__PURE__ */ (0,
|
|
2829
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2331
2830
|
"svg",
|
|
2332
2831
|
{
|
|
2333
2832
|
width: "18",
|
|
@@ -2340,18 +2839,18 @@ ${uploadedFile.markdown}
|
|
|
2340
2839
|
strokeLinejoin: "round",
|
|
2341
2840
|
"aria-hidden": "true",
|
|
2342
2841
|
children: [
|
|
2343
|
-
/* @__PURE__ */ (0,
|
|
2344
|
-
/* @__PURE__ */ (0,
|
|
2345
|
-
/* @__PURE__ */ (0,
|
|
2346
|
-
/* @__PURE__ */ (0,
|
|
2347
|
-
/* @__PURE__ */ (0,
|
|
2842
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("title", { children: "Emoji" }),
|
|
2843
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
|
|
2844
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
|
|
2845
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "9", y1: "9", x2: "9.01", y2: "9" }),
|
|
2846
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "15", y1: "9", x2: "15.01", y2: "9" })
|
|
2348
2847
|
]
|
|
2349
2848
|
}
|
|
2350
2849
|
)
|
|
2351
2850
|
}
|
|
2352
2851
|
),
|
|
2353
|
-
enableFileUpload && fileUpload.canUpload && /* @__PURE__ */ (0,
|
|
2354
|
-
/* @__PURE__ */ (0,
|
|
2852
|
+
enableFileUpload && fileUpload.canUpload && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2853
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2355
2854
|
"button",
|
|
2356
2855
|
{
|
|
2357
2856
|
type: "button",
|
|
@@ -2371,7 +2870,7 @@ ${uploadedFile.markdown}
|
|
|
2371
2870
|
},
|
|
2372
2871
|
disabled: disabled || fileUpload.isUploading,
|
|
2373
2872
|
"aria-label": "Attach file",
|
|
2374
|
-
children: fileUpload.isUploading ? /* @__PURE__ */ (0,
|
|
2873
|
+
children: fileUpload.isUploading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2375
2874
|
"svg",
|
|
2376
2875
|
{
|
|
2377
2876
|
width: "18",
|
|
@@ -2383,11 +2882,11 @@ ${uploadedFile.markdown}
|
|
|
2383
2882
|
className: "animate-spin",
|
|
2384
2883
|
"aria-hidden": "true",
|
|
2385
2884
|
children: [
|
|
2386
|
-
/* @__PURE__ */ (0,
|
|
2387
|
-
/* @__PURE__ */ (0,
|
|
2885
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("title", { children: "Uploading" }),
|
|
2886
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M21 12a9 9 0 11-6.219-8.56" })
|
|
2388
2887
|
]
|
|
2389
2888
|
}
|
|
2390
|
-
) : /* @__PURE__ */ (0,
|
|
2889
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2391
2890
|
"svg",
|
|
2392
2891
|
{
|
|
2393
2892
|
width: "18",
|
|
@@ -2400,14 +2899,14 @@ ${uploadedFile.markdown}
|
|
|
2400
2899
|
strokeLinejoin: "round",
|
|
2401
2900
|
"aria-hidden": "true",
|
|
2402
2901
|
children: [
|
|
2403
|
-
/* @__PURE__ */ (0,
|
|
2404
|
-
/* @__PURE__ */ (0,
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("title", { children: "Attach file" }),
|
|
2903
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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" })
|
|
2405
2904
|
]
|
|
2406
2905
|
}
|
|
2407
2906
|
)
|
|
2408
2907
|
}
|
|
2409
2908
|
),
|
|
2410
|
-
/* @__PURE__ */ (0,
|
|
2909
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2411
2910
|
"input",
|
|
2412
2911
|
{
|
|
2413
2912
|
ref: fileInputRef,
|
|
@@ -2418,7 +2917,7 @@ ${uploadedFile.markdown}
|
|
|
2418
2917
|
}
|
|
2419
2918
|
)
|
|
2420
2919
|
] }),
|
|
2421
|
-
/* @__PURE__ */ (0,
|
|
2920
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2422
2921
|
"button",
|
|
2423
2922
|
{
|
|
2424
2923
|
type: "button",
|
|
@@ -2433,7 +2932,7 @@ ${uploadedFile.markdown}
|
|
|
2433
2932
|
boxShadow: canSend ? `0 2px 8px -2px ${primaryColor}60` : "none"
|
|
2434
2933
|
},
|
|
2435
2934
|
"aria-label": "Send message",
|
|
2436
|
-
children: /* @__PURE__ */ (0,
|
|
2935
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2437
2936
|
"svg",
|
|
2438
2937
|
{
|
|
2439
2938
|
width: "18",
|
|
@@ -2446,9 +2945,9 @@ ${uploadedFile.markdown}
|
|
|
2446
2945
|
strokeLinejoin: "round",
|
|
2447
2946
|
"aria-hidden": "true",
|
|
2448
2947
|
children: [
|
|
2449
|
-
/* @__PURE__ */ (0,
|
|
2450
|
-
/* @__PURE__ */ (0,
|
|
2451
|
-
/* @__PURE__ */ (0,
|
|
2948
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("title", { children: "Send" }),
|
|
2949
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
2950
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
2452
2951
|
]
|
|
2453
2952
|
}
|
|
2454
2953
|
)
|
|
@@ -2458,8 +2957,8 @@ ${uploadedFile.markdown}
|
|
|
2458
2957
|
]
|
|
2459
2958
|
}
|
|
2460
2959
|
),
|
|
2461
|
-
fileUpload.isUploading && /* @__PURE__ */ (0,
|
|
2462
|
-
/* @__PURE__ */ (0,
|
|
2960
|
+
fileUpload.isUploading && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-2 px-1", children: [
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2463
2962
|
"div",
|
|
2464
2963
|
{
|
|
2465
2964
|
className: "w-full rounded-full overflow-hidden",
|
|
@@ -2467,7 +2966,7 @@ ${uploadedFile.markdown}
|
|
|
2467
2966
|
height: 3,
|
|
2468
2967
|
backgroundColor: isLightTheme ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.06)"
|
|
2469
2968
|
},
|
|
2470
|
-
children: /* @__PURE__ */ (0,
|
|
2969
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2471
2970
|
"div",
|
|
2472
2971
|
{
|
|
2473
2972
|
className: "h-full rounded-full transition-all duration-300",
|
|
@@ -2479,7 +2978,7 @@ ${uploadedFile.markdown}
|
|
|
2479
2978
|
)
|
|
2480
2979
|
}
|
|
2481
2980
|
),
|
|
2482
|
-
/* @__PURE__ */ (0,
|
|
2981
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
2483
2982
|
"p",
|
|
2484
2983
|
{
|
|
2485
2984
|
className: "mt-1",
|
|
@@ -2497,7 +2996,7 @@ ${uploadedFile.markdown}
|
|
|
2497
2996
|
)
|
|
2498
2997
|
] }),
|
|
2499
2998
|
showEmojiPicker && emojiData && emojiPickerPosition && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
|
|
2500
|
-
/* @__PURE__ */ (0,
|
|
2999
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2501
3000
|
"div",
|
|
2502
3001
|
{
|
|
2503
3002
|
ref: emojiPickerRef,
|
|
@@ -2512,8 +3011,8 @@ ${uploadedFile.markdown}
|
|
|
2512
3011
|
"0 16px 48px -8px rgba(0,0,0,0.5)"
|
|
2513
3012
|
].join(", ")
|
|
2514
3013
|
},
|
|
2515
|
-
children: /* @__PURE__ */ (0,
|
|
2516
|
-
|
|
3014
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3015
|
+
import_react10.default,
|
|
2517
3016
|
{
|
|
2518
3017
|
data: emojiData,
|
|
2519
3018
|
onEmojiSelect: (emoji) => {
|
|
@@ -2538,7 +3037,7 @@ ${uploadedFile.markdown}
|
|
|
2538
3037
|
}
|
|
2539
3038
|
|
|
2540
3039
|
// src/components/ChatWidget.tsx
|
|
2541
|
-
var
|
|
3040
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2542
3041
|
function getAnonymousUser() {
|
|
2543
3042
|
let anonId;
|
|
2544
3043
|
try {
|
|
@@ -2584,14 +3083,20 @@ function ChatWidget({
|
|
|
2584
3083
|
maxHeight: 900,
|
|
2585
3084
|
enabled: !isFullPage
|
|
2586
3085
|
});
|
|
2587
|
-
const
|
|
3086
|
+
const lastSentRef = (0, import_react11.useRef)(null);
|
|
3087
|
+
const handleSendMessage = (0, import_react11.useCallback)(
|
|
2588
3088
|
(content) => {
|
|
2589
3089
|
if (!websocket.isConnected) {
|
|
2590
3090
|
config.toast?.error("Not connected to chat server");
|
|
2591
3091
|
return;
|
|
2592
3092
|
}
|
|
3093
|
+
const now = Date.now();
|
|
3094
|
+
if (lastSentRef.current && lastSentRef.current.content === content && now - lastSentRef.current.time < 1e3) {
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3097
|
+
lastSentRef.current = { content, time: now };
|
|
2593
3098
|
const optimisticMessage = {
|
|
2594
|
-
id: `temp-${
|
|
3099
|
+
id: `temp-${now}`,
|
|
2595
3100
|
conversationId: config.conversationId || "",
|
|
2596
3101
|
senderId: effectiveUser.email,
|
|
2597
3102
|
senderType: effectiveUser.type,
|
|
@@ -2610,7 +3115,18 @@ function ChatWidget({
|
|
|
2610
3115
|
},
|
|
2611
3116
|
[websocket, config, addMessage, effectiveUser]
|
|
2612
3117
|
);
|
|
2613
|
-
const
|
|
3118
|
+
const handleBookingSlotSelected = (0, import_react11.useCallback)(
|
|
3119
|
+
(date, time, _messageId) => {
|
|
3120
|
+
if (!websocket.isConnected) return;
|
|
3121
|
+
websocket.sendMessage("bookSlot", {
|
|
3122
|
+
conversationId: config.conversationId,
|
|
3123
|
+
date,
|
|
3124
|
+
time
|
|
3125
|
+
});
|
|
3126
|
+
},
|
|
3127
|
+
[websocket, config]
|
|
3128
|
+
);
|
|
3129
|
+
const handleTyping = (0, import_react11.useCallback)(
|
|
2614
3130
|
(isTyping2) => {
|
|
2615
3131
|
if (!websocket.isConnected || config.enableTypingIndicator === false) return;
|
|
2616
3132
|
websocket.sendMessage("typing", {
|
|
@@ -2620,7 +3136,7 @@ function ChatWidget({
|
|
|
2620
3136
|
},
|
|
2621
3137
|
[websocket, config]
|
|
2622
3138
|
);
|
|
2623
|
-
(0,
|
|
3139
|
+
(0, import_react11.useEffect)(() => {
|
|
2624
3140
|
if (websocket.error) {
|
|
2625
3141
|
config.toast?.error(websocket.error.message || "An error occurred");
|
|
2626
3142
|
}
|
|
@@ -2640,7 +3156,6 @@ function ChatWidget({
|
|
|
2640
3156
|
})();
|
|
2641
3157
|
const positionClass = resolvedPosition === "left" ? "left-4" : "right-4";
|
|
2642
3158
|
const containerStyle = isFullPage ? { backgroundColor: bgColor, color: textColor } : {
|
|
2643
|
-
position: "relative",
|
|
2644
3159
|
width,
|
|
2645
3160
|
height,
|
|
2646
3161
|
maxHeight: "calc(100vh - 100px)",
|
|
@@ -2672,14 +3187,14 @@ function ChatWidget({
|
|
|
2672
3187
|
outlineOffset: -1,
|
|
2673
3188
|
transition: "outline 150ms ease"
|
|
2674
3189
|
};
|
|
2675
|
-
return /* @__PURE__ */ (0,
|
|
3190
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2676
3191
|
"div",
|
|
2677
3192
|
{
|
|
2678
3193
|
className: isFullPage ? `flex flex-col h-full ${className}` : `fixed bottom-20 ${positionClass} z-50 flex flex-col overflow-hidden ${className}`,
|
|
2679
3194
|
style: { ...containerStyle, ...dotGridBg, ...edgeHintStyle },
|
|
2680
3195
|
...!isFullPage ? containerResizeProps : {},
|
|
2681
3196
|
children: [
|
|
2682
|
-
!isFullPage && /* @__PURE__ */ (0,
|
|
3197
|
+
!isFullPage && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2683
3198
|
ChatHeader,
|
|
2684
3199
|
{
|
|
2685
3200
|
agent: effectiveUser.type === "customer" ? {
|
|
@@ -2693,7 +3208,7 @@ function ChatWidget({
|
|
|
2693
3208
|
theme: config.theme
|
|
2694
3209
|
}
|
|
2695
3210
|
),
|
|
2696
|
-
!websocket.isConnected && /* @__PURE__ */ (0,
|
|
3211
|
+
!websocket.isConnected && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2697
3212
|
"div",
|
|
2698
3213
|
{
|
|
2699
3214
|
className: "px-4 py-2",
|
|
@@ -2701,15 +3216,15 @@ function ChatWidget({
|
|
|
2701
3216
|
backgroundColor: isLightTheme ? "rgba(255,169,56,0.08)" : "rgba(255,169,56,0.06)",
|
|
2702
3217
|
borderBottom: `1px solid rgba(255,169,56,${isLightTheme ? "0.15" : "0.12"})`
|
|
2703
3218
|
},
|
|
2704
|
-
children: /* @__PURE__ */ (0,
|
|
2705
|
-
/* @__PURE__ */ (0,
|
|
3219
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3220
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2706
3221
|
"div",
|
|
2707
3222
|
{
|
|
2708
3223
|
className: "w-1.5 h-1.5 rounded-full animate-pulse",
|
|
2709
3224
|
style: { backgroundColor: config.theme?.statusCaution || "#ffa938" }
|
|
2710
3225
|
}
|
|
2711
3226
|
),
|
|
2712
|
-
/* @__PURE__ */ (0,
|
|
3227
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2713
3228
|
"span",
|
|
2714
3229
|
{
|
|
2715
3230
|
style: {
|
|
@@ -2720,7 +3235,7 @@ function ChatWidget({
|
|
|
2720
3235
|
children: "Reconnecting..."
|
|
2721
3236
|
}
|
|
2722
3237
|
),
|
|
2723
|
-
/* @__PURE__ */ (0,
|
|
3238
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2724
3239
|
"button",
|
|
2725
3240
|
{
|
|
2726
3241
|
type: "button",
|
|
@@ -2744,8 +3259,8 @@ function ChatWidget({
|
|
|
2744
3259
|
] })
|
|
2745
3260
|
}
|
|
2746
3261
|
),
|
|
2747
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
2748
|
-
/* @__PURE__ */ (0,
|
|
3262
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-center", children: [
|
|
3263
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2749
3264
|
"div",
|
|
2750
3265
|
{
|
|
2751
3266
|
className: "w-7 h-7 border-2 border-t-transparent rounded-full animate-spin mx-auto mb-3",
|
|
@@ -2755,7 +3270,7 @@ function ChatWidget({
|
|
|
2755
3270
|
}
|
|
2756
3271
|
}
|
|
2757
3272
|
),
|
|
2758
|
-
/* @__PURE__ */ (0,
|
|
3273
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2759
3274
|
"p",
|
|
2760
3275
|
{
|
|
2761
3276
|
style: {
|
|
@@ -2766,7 +3281,7 @@ function ChatWidget({
|
|
|
2766
3281
|
children: "Loading messages..."
|
|
2767
3282
|
}
|
|
2768
3283
|
)
|
|
2769
|
-
] }) }) : /* @__PURE__ */ (0,
|
|
3284
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2770
3285
|
MessageList,
|
|
2771
3286
|
{
|
|
2772
3287
|
messages,
|
|
@@ -2779,10 +3294,11 @@ function ChatWidget({
|
|
|
2779
3294
|
isLoadingMore,
|
|
2780
3295
|
theme: config.theme,
|
|
2781
3296
|
onQuickAction: handleSendMessage,
|
|
2782
|
-
isBotThinking
|
|
3297
|
+
isBotThinking,
|
|
3298
|
+
onBookingSlotSelected: handleBookingSlotSelected
|
|
2783
3299
|
}
|
|
2784
3300
|
),
|
|
2785
|
-
/* @__PURE__ */ (0,
|
|
3301
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2786
3302
|
ChatInput,
|
|
2787
3303
|
{
|
|
2788
3304
|
onSend: handleSendMessage,
|
|
@@ -2792,7 +3308,7 @@ function ChatWidget({
|
|
|
2792
3308
|
disabled: !websocket.isConnected
|
|
2793
3309
|
}
|
|
2794
3310
|
),
|
|
2795
|
-
!isFullPage && /* @__PURE__ */ (0,
|
|
3311
|
+
!isFullPage && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2796
3312
|
"div",
|
|
2797
3313
|
{
|
|
2798
3314
|
className: "px-4 py-1.5 text-center",
|
|
@@ -2800,7 +3316,7 @@ function ChatWidget({
|
|
|
2800
3316
|
borderTop: isLightTheme ? "1px solid rgba(0,0,0,0.06)" : "1px solid rgba(255,255,255,0.06)",
|
|
2801
3317
|
backgroundColor: isLightTheme ? "rgba(0,0,0,0.03)" : "rgba(0,0,0,0.2)"
|
|
2802
3318
|
},
|
|
2803
|
-
children: /* @__PURE__ */ (0,
|
|
3319
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2804
3320
|
"p",
|
|
2805
3321
|
{
|
|
2806
3322
|
style: {
|
|
@@ -2811,7 +3327,7 @@ function ChatWidget({
|
|
|
2811
3327
|
children: [
|
|
2812
3328
|
"Powered by",
|
|
2813
3329
|
" ",
|
|
2814
|
-
/* @__PURE__ */ (0,
|
|
3330
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: {
|
|
2815
3331
|
fontWeight: 600,
|
|
2816
3332
|
color: isLightTheme ? "rgba(0,0,0,0.5)" : "rgba(247,247,248,0.45)"
|
|
2817
3333
|
}, children: "Xcelsior" })
|
|
@@ -2826,11 +3342,11 @@ function ChatWidget({
|
|
|
2826
3342
|
}
|
|
2827
3343
|
|
|
2828
3344
|
// src/components/Chat.tsx
|
|
2829
|
-
var
|
|
3345
|
+
var import_react15 = require("react");
|
|
2830
3346
|
|
|
2831
3347
|
// src/components/PreChatForm.tsx
|
|
2832
|
-
var
|
|
2833
|
-
var
|
|
3348
|
+
var import_react12 = require("react");
|
|
3349
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2834
3350
|
function PreChatForm({
|
|
2835
3351
|
onSubmit,
|
|
2836
3352
|
className = "",
|
|
@@ -2839,11 +3355,11 @@ function PreChatForm({
|
|
|
2839
3355
|
onMinimize,
|
|
2840
3356
|
onClose
|
|
2841
3357
|
}) {
|
|
2842
|
-
const [name, setName] = (0,
|
|
2843
|
-
const [email, setEmail] = (0,
|
|
2844
|
-
const [errors, setErrors] = (0,
|
|
2845
|
-
const [isSubmitting, setIsSubmitting] = (0,
|
|
2846
|
-
const [focusedField, setFocusedField] = (0,
|
|
3358
|
+
const [name, setName] = (0, import_react12.useState)(initialName);
|
|
3359
|
+
const [email, setEmail] = (0, import_react12.useState)(initialEmail);
|
|
3360
|
+
const [errors, setErrors] = (0, import_react12.useState)({});
|
|
3361
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react12.useState)(false);
|
|
3362
|
+
const [focusedField, setFocusedField] = (0, import_react12.useState)(null);
|
|
2847
3363
|
const validateForm = () => {
|
|
2848
3364
|
const newErrors = {};
|
|
2849
3365
|
if (!name.trim()) {
|
|
@@ -2891,7 +3407,7 @@ function PreChatForm({
|
|
|
2891
3407
|
transition: "box-shadow 0.2s ease",
|
|
2892
3408
|
caretColor: "#337eff"
|
|
2893
3409
|
});
|
|
2894
|
-
return /* @__PURE__ */ (0,
|
|
3410
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2895
3411
|
"div",
|
|
2896
3412
|
{
|
|
2897
3413
|
className: `fixed bottom-4 right-4 z-50 flex flex-col overflow-hidden ${className}`,
|
|
@@ -2911,7 +3427,7 @@ function PreChatForm({
|
|
|
2911
3427
|
backgroundSize: "24px 24px"
|
|
2912
3428
|
},
|
|
2913
3429
|
children: [
|
|
2914
|
-
/* @__PURE__ */ (0,
|
|
3430
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2915
3431
|
"div",
|
|
2916
3432
|
{
|
|
2917
3433
|
className: "relative text-white overflow-hidden",
|
|
@@ -2919,7 +3435,7 @@ function PreChatForm({
|
|
|
2919
3435
|
background: "linear-gradient(135deg, #337eff 0%, #005eff 50%, #001a66 100%)"
|
|
2920
3436
|
},
|
|
2921
3437
|
children: [
|
|
2922
|
-
/* @__PURE__ */ (0,
|
|
3438
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2923
3439
|
"div",
|
|
2924
3440
|
{
|
|
2925
3441
|
className: "absolute inset-0 pointer-events-none",
|
|
@@ -2928,9 +3444,9 @@ function PreChatForm({
|
|
|
2928
3444
|
}
|
|
2929
3445
|
}
|
|
2930
3446
|
),
|
|
2931
|
-
/* @__PURE__ */ (0,
|
|
2932
|
-
/* @__PURE__ */ (0,
|
|
2933
|
-
/* @__PURE__ */ (0,
|
|
3447
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "relative px-5 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-start justify-between", children: [
|
|
3448
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1", children: [
|
|
3449
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2934
3450
|
"h2",
|
|
2935
3451
|
{
|
|
2936
3452
|
className: "font-semibold",
|
|
@@ -2941,7 +3457,7 @@ function PreChatForm({
|
|
|
2941
3457
|
children: "Start a Conversation"
|
|
2942
3458
|
}
|
|
2943
3459
|
),
|
|
2944
|
-
/* @__PURE__ */ (0,
|
|
3460
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2945
3461
|
"p",
|
|
2946
3462
|
{
|
|
2947
3463
|
className: "mt-1",
|
|
@@ -2954,8 +3470,8 @@ function PreChatForm({
|
|
|
2954
3470
|
}
|
|
2955
3471
|
)
|
|
2956
3472
|
] }),
|
|
2957
|
-
/* @__PURE__ */ (0,
|
|
2958
|
-
/* @__PURE__ */ (0,
|
|
3473
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-1 ml-2", children: [
|
|
3474
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2959
3475
|
"button",
|
|
2960
3476
|
{
|
|
2961
3477
|
type: "button",
|
|
@@ -2969,7 +3485,7 @@ function PreChatForm({
|
|
|
2969
3485
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
2970
3486
|
},
|
|
2971
3487
|
"aria-label": "Minimize chat",
|
|
2972
|
-
children: /* @__PURE__ */ (0,
|
|
3488
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2973
3489
|
"svg",
|
|
2974
3490
|
{
|
|
2975
3491
|
width: "18",
|
|
@@ -2978,8 +3494,8 @@ function PreChatForm({
|
|
|
2978
3494
|
stroke: "currentColor",
|
|
2979
3495
|
viewBox: "0 0 24 24",
|
|
2980
3496
|
children: [
|
|
2981
|
-
/* @__PURE__ */ (0,
|
|
2982
|
-
/* @__PURE__ */ (0,
|
|
3497
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("title", { children: "Minimize" }),
|
|
3498
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2983
3499
|
"path",
|
|
2984
3500
|
{
|
|
2985
3501
|
strokeLinecap: "round",
|
|
@@ -2993,7 +3509,7 @@ function PreChatForm({
|
|
|
2993
3509
|
)
|
|
2994
3510
|
}
|
|
2995
3511
|
),
|
|
2996
|
-
/* @__PURE__ */ (0,
|
|
3512
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2997
3513
|
"button",
|
|
2998
3514
|
{
|
|
2999
3515
|
type: "button",
|
|
@@ -3007,7 +3523,7 @@ function PreChatForm({
|
|
|
3007
3523
|
e.currentTarget.style.backgroundColor = "transparent";
|
|
3008
3524
|
},
|
|
3009
3525
|
"aria-label": "Close chat",
|
|
3010
|
-
children: /* @__PURE__ */ (0,
|
|
3526
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
3011
3527
|
"svg",
|
|
3012
3528
|
{
|
|
3013
3529
|
width: "18",
|
|
@@ -3016,8 +3532,8 @@ function PreChatForm({
|
|
|
3016
3532
|
stroke: "currentColor",
|
|
3017
3533
|
viewBox: "0 0 24 24",
|
|
3018
3534
|
children: [
|
|
3019
|
-
/* @__PURE__ */ (0,
|
|
3020
|
-
/* @__PURE__ */ (0,
|
|
3535
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("title", { children: "Close" }),
|
|
3536
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3021
3537
|
"path",
|
|
3022
3538
|
{
|
|
3023
3539
|
strokeLinecap: "round",
|
|
@@ -3033,7 +3549,7 @@ function PreChatForm({
|
|
|
3033
3549
|
)
|
|
3034
3550
|
] })
|
|
3035
3551
|
] }) }),
|
|
3036
|
-
/* @__PURE__ */ (0,
|
|
3552
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3037
3553
|
"div",
|
|
3038
3554
|
{
|
|
3039
3555
|
className: "h-px",
|
|
@@ -3045,9 +3561,9 @@ function PreChatForm({
|
|
|
3045
3561
|
]
|
|
3046
3562
|
}
|
|
3047
3563
|
),
|
|
3048
|
-
/* @__PURE__ */ (0,
|
|
3049
|
-
/* @__PURE__ */ (0,
|
|
3050
|
-
/* @__PURE__ */ (0,
|
|
3564
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { onSubmit: handleSubmit, className: "p-5 flex flex-col gap-4", children: [
|
|
3565
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
3566
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
3051
3567
|
"label",
|
|
3052
3568
|
{
|
|
3053
3569
|
htmlFor: "chat-name",
|
|
@@ -3059,11 +3575,11 @@ function PreChatForm({
|
|
|
3059
3575
|
},
|
|
3060
3576
|
children: [
|
|
3061
3577
|
"Name ",
|
|
3062
|
-
/* @__PURE__ */ (0,
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { color: "#ff6363" }, children: "*" })
|
|
3063
3579
|
]
|
|
3064
3580
|
}
|
|
3065
3581
|
),
|
|
3066
|
-
/* @__PURE__ */ (0,
|
|
3582
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3067
3583
|
"input",
|
|
3068
3584
|
{
|
|
3069
3585
|
type: "text",
|
|
@@ -3083,7 +3599,7 @@ function PreChatForm({
|
|
|
3083
3599
|
autoComplete: "name"
|
|
3084
3600
|
}
|
|
3085
3601
|
),
|
|
3086
|
-
errors.name && /* @__PURE__ */ (0,
|
|
3602
|
+
errors.name && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3087
3603
|
"p",
|
|
3088
3604
|
{
|
|
3089
3605
|
className: "mt-1.5",
|
|
@@ -3097,8 +3613,8 @@ function PreChatForm({
|
|
|
3097
3613
|
}
|
|
3098
3614
|
)
|
|
3099
3615
|
] }),
|
|
3100
|
-
/* @__PURE__ */ (0,
|
|
3101
|
-
/* @__PURE__ */ (0,
|
|
3616
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
3617
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
3102
3618
|
"label",
|
|
3103
3619
|
{
|
|
3104
3620
|
htmlFor: "chat-email",
|
|
@@ -3110,11 +3626,11 @@ function PreChatForm({
|
|
|
3110
3626
|
},
|
|
3111
3627
|
children: [
|
|
3112
3628
|
"Email ",
|
|
3113
|
-
/* @__PURE__ */ (0,
|
|
3629
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { color: "#ff6363" }, children: "*" })
|
|
3114
3630
|
]
|
|
3115
3631
|
}
|
|
3116
3632
|
),
|
|
3117
|
-
/* @__PURE__ */ (0,
|
|
3633
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3118
3634
|
"input",
|
|
3119
3635
|
{
|
|
3120
3636
|
type: "email",
|
|
@@ -3134,7 +3650,7 @@ function PreChatForm({
|
|
|
3134
3650
|
autoComplete: "email"
|
|
3135
3651
|
}
|
|
3136
3652
|
),
|
|
3137
|
-
errors.email && /* @__PURE__ */ (0,
|
|
3653
|
+
errors.email && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3138
3654
|
"p",
|
|
3139
3655
|
{
|
|
3140
3656
|
className: "mt-1.5",
|
|
@@ -3148,7 +3664,7 @@ function PreChatForm({
|
|
|
3148
3664
|
}
|
|
3149
3665
|
)
|
|
3150
3666
|
] }),
|
|
3151
|
-
/* @__PURE__ */ (0,
|
|
3667
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3152
3668
|
"button",
|
|
3153
3669
|
{
|
|
3154
3670
|
type: "submit",
|
|
@@ -3170,8 +3686,8 @@ function PreChatForm({
|
|
|
3170
3686
|
onMouseLeave: (e) => {
|
|
3171
3687
|
e.currentTarget.style.boxShadow = "0 4px 16px -4px rgba(51,126,255,0.4)";
|
|
3172
3688
|
},
|
|
3173
|
-
children: isSubmitting ? /* @__PURE__ */ (0,
|
|
3174
|
-
/* @__PURE__ */ (0,
|
|
3689
|
+
children: isSubmitting ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [
|
|
3690
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
3175
3691
|
"svg",
|
|
3176
3692
|
{
|
|
3177
3693
|
className: "animate-spin",
|
|
@@ -3183,8 +3699,8 @@ function PreChatForm({
|
|
|
3183
3699
|
strokeWidth: "2",
|
|
3184
3700
|
"aria-label": "Loading",
|
|
3185
3701
|
children: [
|
|
3186
|
-
/* @__PURE__ */ (0,
|
|
3187
|
-
/* @__PURE__ */ (0,
|
|
3702
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("title", { children: "Loading" }),
|
|
3703
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M21 12a9 9 0 11-6.219-8.56" })
|
|
3188
3704
|
]
|
|
3189
3705
|
}
|
|
3190
3706
|
),
|
|
@@ -3192,7 +3708,7 @@ function PreChatForm({
|
|
|
3192
3708
|
] }) : "Start Chat"
|
|
3193
3709
|
}
|
|
3194
3710
|
),
|
|
3195
|
-
/* @__PURE__ */ (0,
|
|
3711
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3196
3712
|
"p",
|
|
3197
3713
|
{
|
|
3198
3714
|
className: "text-center",
|
|
@@ -3205,7 +3721,7 @@ function PreChatForm({
|
|
|
3205
3721
|
}
|
|
3206
3722
|
)
|
|
3207
3723
|
] }),
|
|
3208
|
-
/* @__PURE__ */ (0,
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
3209
3725
|
"div",
|
|
3210
3726
|
{
|
|
3211
3727
|
className: "px-4 py-1.5 text-center",
|
|
@@ -3213,7 +3729,7 @@ function PreChatForm({
|
|
|
3213
3729
|
borderTop: "1px solid rgba(255,255,255,0.06)",
|
|
3214
3730
|
backgroundColor: "rgba(0,0,0,0.2)"
|
|
3215
3731
|
},
|
|
3216
|
-
children: /* @__PURE__ */ (0,
|
|
3732
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
3217
3733
|
"p",
|
|
3218
3734
|
{
|
|
3219
3735
|
style: {
|
|
@@ -3224,7 +3740,7 @@ function PreChatForm({
|
|
|
3224
3740
|
children: [
|
|
3225
3741
|
"Powered by",
|
|
3226
3742
|
" ",
|
|
3227
|
-
/* @__PURE__ */ (0,
|
|
3743
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontWeight: 600, color: "rgba(247,247,248,0.45)" }, children: "Xcelsior" })
|
|
3228
3744
|
]
|
|
3229
3745
|
}
|
|
3230
3746
|
)
|
|
@@ -3236,8 +3752,15 @@ function PreChatForm({
|
|
|
3236
3752
|
}
|
|
3237
3753
|
|
|
3238
3754
|
// src/hooks/useDraggablePosition.ts
|
|
3239
|
-
var
|
|
3755
|
+
var import_react13 = require("react");
|
|
3240
3756
|
var STORAGE_KEY2 = "xcelsior-chat-position";
|
|
3757
|
+
var DRAG_THRESHOLD = 5;
|
|
3758
|
+
var FAB_SIZE = 64;
|
|
3759
|
+
var MARGIN = 16;
|
|
3760
|
+
var BOTTOM = 20;
|
|
3761
|
+
var VELOCITY_THRESHOLD = 0.4;
|
|
3762
|
+
var SETTLE_DURATION = 400;
|
|
3763
|
+
var SETTLE_EASING = "cubic-bezier(0.2, 0.9, 0.3, 1.1)";
|
|
3241
3764
|
function getStoredPosition() {
|
|
3242
3765
|
try {
|
|
3243
3766
|
const stored = localStorage.getItem(STORAGE_KEY2);
|
|
@@ -3246,21 +3769,33 @@ function getStoredPosition() {
|
|
|
3246
3769
|
}
|
|
3247
3770
|
return "right";
|
|
3248
3771
|
}
|
|
3249
|
-
function storePosition(
|
|
3772
|
+
function storePosition(pos) {
|
|
3250
3773
|
try {
|
|
3251
|
-
localStorage.setItem(STORAGE_KEY2,
|
|
3774
|
+
localStorage.setItem(STORAGE_KEY2, pos);
|
|
3252
3775
|
} catch {
|
|
3253
3776
|
}
|
|
3254
3777
|
}
|
|
3778
|
+
function restingX(side) {
|
|
3779
|
+
return side === "left" ? MARGIN : window.innerWidth - MARGIN - FAB_SIZE;
|
|
3780
|
+
}
|
|
3781
|
+
function restingY() {
|
|
3782
|
+
return window.innerHeight - BOTTOM - FAB_SIZE;
|
|
3783
|
+
}
|
|
3255
3784
|
function useDraggablePosition(configPosition = "auto") {
|
|
3256
3785
|
const resolvedDefault = configPosition === "auto" ? getStoredPosition() : configPosition;
|
|
3257
|
-
const [position, setPosition] = (0,
|
|
3258
|
-
const
|
|
3259
|
-
|
|
3260
|
-
const
|
|
3261
|
-
const
|
|
3262
|
-
const
|
|
3263
|
-
(0,
|
|
3786
|
+
const [position, setPosition] = (0, import_react13.useState)(resolvedDefault);
|
|
3787
|
+
const positionRef = (0, import_react13.useRef)(position);
|
|
3788
|
+
positionRef.current = position;
|
|
3789
|
+
const [isDragging, setIsDragging] = (0, import_react13.useState)(false);
|
|
3790
|
+
const containerRef = (0, import_react13.useRef)(null);
|
|
3791
|
+
const draggingRef = (0, import_react13.useRef)(false);
|
|
3792
|
+
const didDragRef = (0, import_react13.useRef)(false);
|
|
3793
|
+
const startPointer = (0, import_react13.useRef)({ x: 0, y: 0 });
|
|
3794
|
+
const startRect = (0, import_react13.useRef)({ x: 0, y: 0 });
|
|
3795
|
+
const samples = (0, import_react13.useRef)([]);
|
|
3796
|
+
const hasShownHint = (0, import_react13.useRef)(false);
|
|
3797
|
+
const [showHint, setShowHint] = (0, import_react13.useState)(false);
|
|
3798
|
+
(0, import_react13.useEffect)(() => {
|
|
3264
3799
|
try {
|
|
3265
3800
|
const hasVisited = localStorage.getItem("xcelsior-chat-hint-shown");
|
|
3266
3801
|
if (!hasVisited && !hasShownHint.current) {
|
|
@@ -3275,53 +3810,115 @@ function useDraggablePosition(configPosition = "auto") {
|
|
|
3275
3810
|
} catch {
|
|
3276
3811
|
}
|
|
3277
3812
|
}, []);
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3813
|
+
(0, import_react13.useEffect)(() => {
|
|
3814
|
+
const handleMove = (e) => {
|
|
3815
|
+
if (!draggingRef.current) return;
|
|
3816
|
+
const el = containerRef.current;
|
|
3817
|
+
if (!el) return;
|
|
3818
|
+
const dx = e.clientX - startPointer.current.x;
|
|
3819
|
+
const dy = e.clientY - startPointer.current.y;
|
|
3820
|
+
if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {
|
|
3821
|
+
didDragRef.current = true;
|
|
3822
|
+
}
|
|
3823
|
+
el.style.transition = "none";
|
|
3824
|
+
el.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
3825
|
+
const now = performance.now();
|
|
3826
|
+
samples.current.push({ x: e.clientX, t: now });
|
|
3827
|
+
if (samples.current.length > 5) samples.current.shift();
|
|
3828
|
+
};
|
|
3829
|
+
const handleUp = (e) => {
|
|
3830
|
+
if (!draggingRef.current) return;
|
|
3831
|
+
draggingRef.current = false;
|
|
3832
|
+
const el = containerRef.current;
|
|
3833
|
+
if (!el) return;
|
|
3834
|
+
let velocityX = 0;
|
|
3835
|
+
const s = samples.current;
|
|
3836
|
+
if (s.length >= 2) {
|
|
3837
|
+
const first = s[0];
|
|
3838
|
+
const last = s[s.length - 1];
|
|
3839
|
+
const dt = last.t - first.t;
|
|
3840
|
+
if (dt > 0) velocityX = (last.x - first.x) / dt;
|
|
3841
|
+
}
|
|
3842
|
+
samples.current = [];
|
|
3843
|
+
const currentVisualX = startRect.current.x + (e.clientX - startPointer.current.x);
|
|
3844
|
+
const screenMid = window.innerWidth / 2;
|
|
3845
|
+
let targetSide;
|
|
3846
|
+
if (Math.abs(velocityX) > VELOCITY_THRESHOLD) {
|
|
3847
|
+
targetSide = velocityX < 0 ? "left" : "right";
|
|
3848
|
+
} else {
|
|
3849
|
+
targetSide = currentVisualX + FAB_SIZE / 2 < screenMid ? "left" : "right";
|
|
3850
|
+
}
|
|
3851
|
+
const oldBaseX = restingX(positionRef.current);
|
|
3852
|
+
const oldBaseY = restingY();
|
|
3853
|
+
const currentX = startRect.current.x + (e.clientX - startPointer.current.x);
|
|
3854
|
+
const currentY = startRect.current.y + (e.clientY - startPointer.current.y);
|
|
3855
|
+
const targetX = restingX(targetSide);
|
|
3856
|
+
const targetY = restingY();
|
|
3857
|
+
const fromTx = currentX - oldBaseX;
|
|
3858
|
+
const fromTy = currentY - oldBaseY;
|
|
3859
|
+
const toTx = targetX - oldBaseX;
|
|
3860
|
+
const toTy = targetY - oldBaseY;
|
|
3861
|
+
el.style.transition = "none";
|
|
3862
|
+
el.style.transform = `translate(${fromTx}px, ${fromTy}px)`;
|
|
3863
|
+
void el.offsetHeight;
|
|
3864
|
+
el.style.transition = `transform ${SETTLE_DURATION}ms ${SETTLE_EASING}`;
|
|
3865
|
+
el.style.transform = `translate(${toTx}px, ${toTy}px)`;
|
|
3866
|
+
const finish = () => {
|
|
3867
|
+
el.style.transition = "";
|
|
3868
|
+
el.style.transform = "";
|
|
3869
|
+
el.removeEventListener("transitionend", finish);
|
|
3870
|
+
setIsDragging(false);
|
|
3871
|
+
if (targetSide !== positionRef.current) {
|
|
3872
|
+
setPosition(targetSide);
|
|
3873
|
+
storePosition(targetSide);
|
|
3874
|
+
}
|
|
3875
|
+
};
|
|
3876
|
+
el.addEventListener("transitionend", finish, { once: true });
|
|
3877
|
+
setTimeout(finish, SETTLE_DURATION + 50);
|
|
3878
|
+
};
|
|
3879
|
+
document.addEventListener("pointermove", handleMove);
|
|
3880
|
+
document.addEventListener("pointerup", handleUp);
|
|
3881
|
+
return () => {
|
|
3882
|
+
document.removeEventListener("pointermove", handleMove);
|
|
3883
|
+
document.removeEventListener("pointerup", handleUp);
|
|
3884
|
+
};
|
|
3282
3885
|
}, []);
|
|
3283
|
-
const
|
|
3886
|
+
const handlePointerDown = (0, import_react13.useCallback)((e) => {
|
|
3887
|
+
e.preventDefault();
|
|
3888
|
+
const el = containerRef.current;
|
|
3889
|
+
if (!el) return;
|
|
3890
|
+
const rect = el.getBoundingClientRect();
|
|
3891
|
+
startPointer.current = { x: e.clientX, y: e.clientY };
|
|
3892
|
+
startRect.current = { x: rect.left, y: rect.top };
|
|
3893
|
+
draggingRef.current = true;
|
|
3894
|
+
didDragRef.current = false;
|
|
3895
|
+
samples.current = [];
|
|
3896
|
+
setIsDragging(true);
|
|
3284
3897
|
}, []);
|
|
3285
|
-
const
|
|
3286
|
-
(e) => {
|
|
3287
|
-
setIsDragging(false);
|
|
3288
|
-
e.target.releasePointerCapture(e.pointerId);
|
|
3289
|
-
const deltaX = e.clientX - dragStartX.current;
|
|
3290
|
-
if (Math.abs(deltaX) > 50) {
|
|
3291
|
-
const screenMid = window.innerWidth / 2;
|
|
3292
|
-
const newPosition = e.clientX < screenMid ? "left" : "right";
|
|
3293
|
-
if (newPosition !== position) {
|
|
3294
|
-
setPosition(newPosition);
|
|
3295
|
-
storePosition(newPosition);
|
|
3296
|
-
}
|
|
3297
|
-
}
|
|
3298
|
-
},
|
|
3299
|
-
[position]
|
|
3300
|
-
);
|
|
3898
|
+
const shouldSuppressClick = (0, import_react13.useCallback)(() => didDragRef.current, []);
|
|
3301
3899
|
return {
|
|
3302
3900
|
position,
|
|
3303
3901
|
isDragging,
|
|
3304
3902
|
showHint,
|
|
3305
|
-
|
|
3903
|
+
containerRef,
|
|
3904
|
+
shouldSuppressClick,
|
|
3306
3905
|
handlers: {
|
|
3307
|
-
onPointerDown: handlePointerDown
|
|
3308
|
-
onPointerMove: handlePointerMove,
|
|
3309
|
-
onPointerUp: handlePointerUp
|
|
3906
|
+
onPointerDown: handlePointerDown
|
|
3310
3907
|
}
|
|
3311
3908
|
};
|
|
3312
3909
|
}
|
|
3313
3910
|
|
|
3314
3911
|
// src/hooks/useChatWidgetState.ts
|
|
3315
|
-
var
|
|
3912
|
+
var import_react14 = require("react");
|
|
3316
3913
|
function useChatWidgetState({
|
|
3317
3914
|
state: controlledState,
|
|
3318
3915
|
defaultState = "minimized",
|
|
3319
3916
|
onStateChange
|
|
3320
3917
|
}) {
|
|
3321
|
-
const [uncontrolledState, setUncontrolledState] = (0,
|
|
3918
|
+
const [uncontrolledState, setUncontrolledState] = (0, import_react14.useState)(defaultState);
|
|
3322
3919
|
const isControlled = controlledState !== void 0 && controlledState !== "undefined";
|
|
3323
3920
|
const currentState = isControlled ? controlledState : uncontrolledState;
|
|
3324
|
-
const setState = (0,
|
|
3921
|
+
const setState = (0, import_react14.useCallback)(
|
|
3325
3922
|
(newValue) => {
|
|
3326
3923
|
if (!isControlled) {
|
|
3327
3924
|
setUncontrolledState(newValue);
|
|
@@ -3338,7 +3935,7 @@ function useChatWidgetState({
|
|
|
3338
3935
|
}
|
|
3339
3936
|
|
|
3340
3937
|
// src/components/Chat.tsx
|
|
3341
|
-
var
|
|
3938
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
3342
3939
|
function generateSessionId() {
|
|
3343
3940
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
3344
3941
|
return crypto.randomUUID();
|
|
@@ -3354,62 +3951,39 @@ function Chat({
|
|
|
3354
3951
|
defaultState = "minimized",
|
|
3355
3952
|
onStateChange
|
|
3356
3953
|
}) {
|
|
3357
|
-
const [userInfo, setUserInfo] = (0,
|
|
3358
|
-
const [conversationId, setConversationId] = (0,
|
|
3359
|
-
const [isLoading, setIsLoading] = (0,
|
|
3360
|
-
const [isAnimating, setIsAnimating] = (0, import_react14.useState)(false);
|
|
3361
|
-
const [showWidget, setShowWidget] = (0, import_react14.useState)(false);
|
|
3954
|
+
const [userInfo, setUserInfo] = (0, import_react15.useState)(null);
|
|
3955
|
+
const [conversationId, setConversationId] = (0, import_react15.useState)("");
|
|
3956
|
+
const [isLoading, setIsLoading] = (0, import_react15.useState)(true);
|
|
3362
3957
|
const identityMode = config.identityCollection || "progressive";
|
|
3363
|
-
const { position, isDragging, showHint, handlers } = useDraggablePosition(config.position);
|
|
3364
|
-
const
|
|
3958
|
+
const { position, isDragging, showHint, containerRef, shouldSuppressClick, handlers } = useDraggablePosition(config.position);
|
|
3959
|
+
const sessionInitializedRef = (0, import_react15.useRef)(false);
|
|
3960
|
+
const { currentState, setState } = useChatWidgetState({
|
|
3365
3961
|
state,
|
|
3366
3962
|
defaultState,
|
|
3367
3963
|
onStateChange
|
|
3368
3964
|
});
|
|
3369
|
-
const
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
requestAnimationFrame(() => {
|
|
3377
|
-
setIsAnimating(false);
|
|
3378
|
-
});
|
|
3379
|
-
});
|
|
3380
|
-
} else if ((newState === "minimized" || newState === "closed") && currentState === "open") {
|
|
3381
|
-
setIsAnimating(true);
|
|
3382
|
-
setTimeout(() => {
|
|
3383
|
-
setShowWidget(false);
|
|
3384
|
-
setIsAnimating(false);
|
|
3385
|
-
setStateRaw(newState);
|
|
3386
|
-
}, 200);
|
|
3387
|
-
} else {
|
|
3388
|
-
setStateRaw(newState);
|
|
3389
|
-
}
|
|
3390
|
-
},
|
|
3391
|
-
[currentState, setStateRaw]
|
|
3392
|
-
);
|
|
3393
|
-
(0, import_react14.useEffect)(() => {
|
|
3394
|
-
if (currentState === "open") {
|
|
3395
|
-
setShowWidget(true);
|
|
3396
|
-
}
|
|
3397
|
-
}, [currentState]);
|
|
3398
|
-
(0, import_react14.useEffect)(() => {
|
|
3965
|
+
const configConversationId = config.conversationId;
|
|
3966
|
+
const configUserEmail = config.currentUser?.email;
|
|
3967
|
+
const configUserName = config.currentUser?.name;
|
|
3968
|
+
const configUserAvatar = config.currentUser?.avatar;
|
|
3969
|
+
const configUserStatus = config.currentUser?.status;
|
|
3970
|
+
(0, import_react15.useEffect)(() => {
|
|
3971
|
+
if (sessionInitializedRef.current) return;
|
|
3399
3972
|
const initializeSession = () => {
|
|
3400
3973
|
try {
|
|
3401
|
-
if (
|
|
3402
|
-
const convId2 =
|
|
3974
|
+
if (configUserEmail && configUserName) {
|
|
3975
|
+
const convId2 = configConversationId || generateSessionId();
|
|
3403
3976
|
const user = {
|
|
3404
|
-
name:
|
|
3405
|
-
email:
|
|
3406
|
-
avatar:
|
|
3977
|
+
name: configUserName,
|
|
3978
|
+
email: configUserEmail,
|
|
3979
|
+
avatar: configUserAvatar,
|
|
3407
3980
|
type: "customer",
|
|
3408
|
-
status:
|
|
3981
|
+
status: configUserStatus
|
|
3409
3982
|
};
|
|
3410
3983
|
setUserInfo(user);
|
|
3411
3984
|
setConversationId(convId2);
|
|
3412
3985
|
setIsLoading(false);
|
|
3986
|
+
sessionInitializedRef.current = true;
|
|
3413
3987
|
return;
|
|
3414
3988
|
}
|
|
3415
3989
|
const storedDataJson = localStorage.getItem(`${storageKeyPrefix}_user`);
|
|
@@ -3426,24 +4000,53 @@ function Chat({
|
|
|
3426
4000
|
setUserInfo(user);
|
|
3427
4001
|
setConversationId(storedData.conversationId);
|
|
3428
4002
|
setIsLoading(false);
|
|
4003
|
+
sessionInitializedRef.current = true;
|
|
3429
4004
|
return;
|
|
3430
4005
|
}
|
|
3431
4006
|
}
|
|
3432
|
-
const convId =
|
|
4007
|
+
const convId = configConversationId || generateSessionId();
|
|
3433
4008
|
setConversationId(convId);
|
|
3434
4009
|
if (identityMode === "progressive" || identityMode === "none") {
|
|
3435
|
-
|
|
4010
|
+
let anonId;
|
|
4011
|
+
try {
|
|
4012
|
+
const stored = localStorage.getItem("xcelsior-chat-anon-id");
|
|
4013
|
+
if (stored) {
|
|
4014
|
+
anonId = stored;
|
|
4015
|
+
} else {
|
|
4016
|
+
anonId = `anon-${crypto.randomUUID?.() || Math.random().toString(36).slice(2)}`;
|
|
4017
|
+
localStorage.setItem("xcelsior-chat-anon-id", anonId);
|
|
4018
|
+
}
|
|
4019
|
+
} catch {
|
|
4020
|
+
anonId = `anon-${Math.random().toString(36).slice(2)}`;
|
|
4021
|
+
}
|
|
4022
|
+
const anonEmail = `${anonId}@anonymous.xcelsior.co`;
|
|
4023
|
+
const anonUser = { name: "Visitor", email: anonEmail, type: "customer", status: "online" };
|
|
4024
|
+
setUserInfo(anonUser);
|
|
4025
|
+
try {
|
|
4026
|
+
localStorage.setItem(
|
|
4027
|
+
`${storageKeyPrefix}_user`,
|
|
4028
|
+
JSON.stringify({
|
|
4029
|
+
name: anonUser.name,
|
|
4030
|
+
email: anonUser.email,
|
|
4031
|
+
conversationId: convId,
|
|
4032
|
+
timestamp: Date.now()
|
|
4033
|
+
})
|
|
4034
|
+
);
|
|
4035
|
+
} catch {
|
|
4036
|
+
}
|
|
3436
4037
|
}
|
|
4038
|
+
sessionInitializedRef.current = true;
|
|
3437
4039
|
} catch (error) {
|
|
3438
4040
|
console.error("Error initializing chat session:", error);
|
|
3439
|
-
setConversationId(
|
|
4041
|
+
setConversationId(configConversationId || generateSessionId());
|
|
4042
|
+
sessionInitializedRef.current = true;
|
|
3440
4043
|
} finally {
|
|
3441
4044
|
setIsLoading(false);
|
|
3442
4045
|
}
|
|
3443
4046
|
};
|
|
3444
4047
|
initializeSession();
|
|
3445
|
-
}, [
|
|
3446
|
-
const handlePreChatSubmit = (0,
|
|
4048
|
+
}, [configConversationId, configUserEmail, configUserName, configUserAvatar, configUserStatus, storageKeyPrefix, identityMode]);
|
|
4049
|
+
const handlePreChatSubmit = (0, import_react15.useCallback)(
|
|
3447
4050
|
(name, email) => {
|
|
3448
4051
|
const convId = conversationId || generateSessionId();
|
|
3449
4052
|
const user = { name, email, type: "customer", status: "online" };
|
|
@@ -3469,49 +4072,52 @@ function Chat({
|
|
|
3469
4072
|
const primaryColor = config.theme?.primary || "#337eff";
|
|
3470
4073
|
const primaryStrong = config.theme?.primaryStrong || "#005eff";
|
|
3471
4074
|
if (currentState === "minimized") {
|
|
3472
|
-
return /* @__PURE__ */ (0,
|
|
3473
|
-
"
|
|
4075
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4076
|
+
"div",
|
|
3474
4077
|
{
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
4078
|
+
ref: containerRef,
|
|
4079
|
+
className: `fixed bottom-5 ${positionClass} z-50 ${className}`,
|
|
4080
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
4081
|
+
"button",
|
|
4082
|
+
{
|
|
4083
|
+
type: "button",
|
|
4084
|
+
onClick: () => {
|
|
4085
|
+
if (!shouldSuppressClick()) setState("open");
|
|
4086
|
+
},
|
|
4087
|
+
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"}`,
|
|
4088
|
+
style: {
|
|
4089
|
+
width: 64,
|
|
4090
|
+
height: 64,
|
|
4091
|
+
background: `linear-gradient(135deg, ${primaryColor}, ${primaryStrong})`,
|
|
4092
|
+
boxShadow: [
|
|
4093
|
+
`0 4px 24px 0 ${primaryColor}80`,
|
|
4094
|
+
`0 8px 32px -4px rgba(0,0,0,0.3)`,
|
|
4095
|
+
`inset 0 1px 0 0 rgba(255,255,255,0.2)`
|
|
4096
|
+
].join(", ")
|
|
4097
|
+
},
|
|
4098
|
+
"aria-label": "Open chat",
|
|
4099
|
+
...handlers,
|
|
4100
|
+
children: [
|
|
4101
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChatBubbleIcon, { size: 36, className: "pointer-events-none" }),
|
|
4102
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4103
|
+
"span",
|
|
4104
|
+
{
|
|
4105
|
+
className: "absolute inset-0 rounded-full animate-ping pointer-events-none",
|
|
4106
|
+
style: {
|
|
4107
|
+
backgroundColor: primaryColor,
|
|
4108
|
+
opacity: 0.15,
|
|
4109
|
+
animationDuration: "2.5s"
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
)
|
|
4113
|
+
]
|
|
4114
|
+
}
|
|
4115
|
+
)
|
|
3510
4116
|
}
|
|
3511
|
-
)
|
|
4117
|
+
);
|
|
3512
4118
|
}
|
|
3513
4119
|
if (identityMode === "form" && (!userInfo || !userInfo.email || !userInfo.name)) {
|
|
3514
|
-
return /* @__PURE__ */ (0,
|
|
4120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3515
4121
|
PreChatForm,
|
|
3516
4122
|
{
|
|
3517
4123
|
onSubmit: handlePreChatSubmit,
|
|
@@ -3528,16 +4134,7 @@ function Chat({
|
|
|
3528
4134
|
conversationId,
|
|
3529
4135
|
currentUser: userInfo || void 0
|
|
3530
4136
|
};
|
|
3531
|
-
|
|
3532
|
-
opacity: 1,
|
|
3533
|
-
transform: "translateY(0) scale(1)",
|
|
3534
|
-
transition: "opacity 0.25s ease-out, transform 0.25s ease-out"
|
|
3535
|
-
} : {
|
|
3536
|
-
opacity: 0,
|
|
3537
|
-
transform: "translateY(12px) scale(0.97)",
|
|
3538
|
-
transition: "opacity 0.2s ease-in, transform 0.2s ease-in"
|
|
3539
|
-
};
|
|
3540
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: widgetAnimationStyle, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
4137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3541
4138
|
ChatWidget,
|
|
3542
4139
|
{
|
|
3543
4140
|
config: fullConfig,
|
|
@@ -3546,16 +4143,16 @@ function Chat({
|
|
|
3546
4143
|
onMinimize: () => setState("minimized"),
|
|
3547
4144
|
resolvedPosition: position
|
|
3548
4145
|
}
|
|
3549
|
-
)
|
|
4146
|
+
);
|
|
3550
4147
|
}
|
|
3551
4148
|
|
|
3552
4149
|
// src/components/TypingIndicator.tsx
|
|
3553
|
-
var
|
|
4150
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3554
4151
|
function TypingIndicator({ isTyping, userName }) {
|
|
3555
4152
|
if (!isTyping) {
|
|
3556
4153
|
return null;
|
|
3557
4154
|
}
|
|
3558
|
-
return /* @__PURE__ */ (0,
|
|
4155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3559
4156
|
"div",
|
|
3560
4157
|
{
|
|
3561
4158
|
className: "px-4 py-2",
|
|
@@ -3563,8 +4160,8 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
3563
4160
|
borderTop: "1px solid rgba(255,255,255,0.06)",
|
|
3564
4161
|
backgroundColor: "rgba(0,0,0,0.15)"
|
|
3565
4162
|
},
|
|
3566
|
-
children: /* @__PURE__ */ (0,
|
|
3567
|
-
/* @__PURE__ */ (0,
|
|
4163
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4164
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3568
4165
|
"span",
|
|
3569
4166
|
{
|
|
3570
4167
|
className: "rounded-full animate-bounce",
|
|
@@ -3578,7 +4175,7 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
3578
4175
|
},
|
|
3579
4176
|
i
|
|
3580
4177
|
)) }),
|
|
3581
|
-
/* @__PURE__ */ (0,
|
|
4178
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3582
4179
|
"span",
|
|
3583
4180
|
{
|
|
3584
4181
|
style: {
|
|
@@ -3595,6 +4192,9 @@ function TypingIndicator({ isTyping, userName }) {
|
|
|
3595
4192
|
}
|
|
3596
4193
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3597
4194
|
0 && (module.exports = {
|
|
4195
|
+
BookingCancelledCard,
|
|
4196
|
+
BookingConfirmationCard,
|
|
4197
|
+
BookingSlotPicker,
|
|
3598
4198
|
Chat,
|
|
3599
4199
|
ChatHeader,
|
|
3600
4200
|
ChatInput,
|