@uptrademedia/site-kit 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6ODMCZHH.js → chunk-CTKXFLIF.js} +150 -32
- package/dist/chunk-CTKXFLIF.js.map +1 -0
- package/dist/{chunk-E6L6AY2Q.mjs → chunk-TOUIKTXU.mjs} +150 -32
- package/dist/chunk-TOUIKTXU.mjs.map +1 -0
- package/dist/cli/index.js +331 -800
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +325 -792
- package/dist/cli/index.mjs.map +1 -1
- package/dist/engage/index.d.mts +3 -1
- package/dist/engage/index.d.ts +3 -1
- package/dist/engage/index.js +4 -4
- package/dist/engage/index.mjs +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -3
- package/dist/index.mjs.map +1 -1
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.d.ts +1 -1
- package/dist/seo/index.js +0 -3
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/sitemap/index.d.mts +1 -1
- package/dist/sitemap/index.d.ts +1 -1
- package/dist/sitemap/index.js +0 -4
- package/dist/sitemap/index.js.map +1 -1
- package/dist/sitemap/index.mjs +1 -2
- package/dist/sitemap/index.mjs.map +1 -1
- package/package.json +39 -39
- package/dist/chunk-6ODMCZHH.js.map +0 -1
- package/dist/chunk-E6L6AY2Q.mjs.map +0 -1
|
@@ -10,9 +10,13 @@ var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
|
10
10
|
|
|
11
11
|
// src/engage/ChatWidget.tsx
|
|
12
12
|
function getApiConfig() {
|
|
13
|
-
const
|
|
13
|
+
const isDev = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
14
|
+
const defaultApiUrl = isDev && process.env?.NEXT_PUBLIC_UPTRADE_API_URL ? process.env.NEXT_PUBLIC_UPTRADE_API_URL : "https://api.uptrademedia.com";
|
|
15
|
+
const defaultSignalUrl = isDev && process.env?.NEXT_PUBLIC_SIGNAL_API_URL ? process.env.NEXT_PUBLIC_SIGNAL_API_URL : "https://signal.uptrademedia.com";
|
|
16
|
+
const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || defaultApiUrl : defaultApiUrl;
|
|
14
17
|
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
|
|
15
|
-
|
|
18
|
+
const signalUrl = typeof window !== "undefined" ? window.__SITE_KIT_SIGNAL_URL__ || defaultSignalUrl : defaultSignalUrl;
|
|
19
|
+
return { apiUrl, apiKey, signalUrl };
|
|
16
20
|
}
|
|
17
21
|
function generateVisitorId() {
|
|
18
22
|
const stored = typeof localStorage !== "undefined" ? localStorage.getItem("engage_visitor_id") : null;
|
|
@@ -35,7 +39,7 @@ function isLightColor(hex) {
|
|
|
35
39
|
const b = num & 255;
|
|
36
40
|
return (r * 299 + g * 587 + b * 114) / 1e3 > 160;
|
|
37
41
|
}
|
|
38
|
-
function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
42
|
+
function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, signalUrl: propSignalUrl }) {
|
|
39
43
|
const [resolvedProjectId, setResolvedProjectId] = React2.useState(propProjectId || null);
|
|
40
44
|
const projectId = propProjectId || resolvedProjectId || "";
|
|
41
45
|
const [isOpen, setIsOpen] = React2.useState(false);
|
|
@@ -56,6 +60,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
56
60
|
const [lastFailedSend, setLastFailedSend] = React2.useState(null);
|
|
57
61
|
const [showWelcome, setShowWelcome] = React2.useState(true);
|
|
58
62
|
const [checkingAvailability, setCheckingAvailability] = React2.useState(false);
|
|
63
|
+
const [checkingHandoff, setCheckingHandoff] = React2.useState(false);
|
|
59
64
|
React2.useRef(null);
|
|
60
65
|
const pendingInitialMessageRef = React2.useRef(null);
|
|
61
66
|
const apiKeyMissingWarnedRef = React2.useRef(false);
|
|
@@ -83,6 +88,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
83
88
|
const offlineHeading = widgetConfig?.offline_heading ?? "No agents available right now";
|
|
84
89
|
const offlineSubheading = handoffOfflinePrompt ?? widgetConfig?.offline_subheading ?? widgetConfig?.form_description ?? widgetConfig?.offline_message ?? config?.offlineMessage ?? "Leave us a message and we'll get back to you!";
|
|
85
90
|
const baseUrl = propApiUrl || getApiConfig().apiUrl;
|
|
91
|
+
const signalUrl = propSignalUrl || getApiConfig().signalUrl;
|
|
86
92
|
React2.useEffect(() => {
|
|
87
93
|
if (propProjectId || resolvedProjectId) return;
|
|
88
94
|
const { apiKey } = getApiConfig();
|
|
@@ -155,6 +161,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
155
161
|
const { data } = await response.json();
|
|
156
162
|
const session = data.session ?? data;
|
|
157
163
|
const sid = session?.id ?? session?.session_id ?? data.id ?? data.session_id;
|
|
164
|
+
const signalEnabled = session?.chat_mode === "ai" || session?.chat_mode === "hybrid";
|
|
158
165
|
setSessionId(sid);
|
|
159
166
|
const messages2 = session?.messages ?? data.messages ?? [];
|
|
160
167
|
if (messages2.length > 0) {
|
|
@@ -168,13 +175,40 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
168
175
|
}))
|
|
169
176
|
);
|
|
170
177
|
}
|
|
171
|
-
return sid;
|
|
178
|
+
return { id: sid, signalEnabled };
|
|
172
179
|
}
|
|
173
180
|
} catch (error) {
|
|
174
181
|
console.error("[ChatWidget] Session init failed:", error);
|
|
175
182
|
}
|
|
176
183
|
return null;
|
|
177
184
|
}, [projectId, visitorId, baseUrl]);
|
|
185
|
+
const sendToSignalApi = React2.useCallback(
|
|
186
|
+
async (content, conversationId) => {
|
|
187
|
+
const url = `${signalUrl.replace(/\/$/, "")}/echo/public/chat`;
|
|
188
|
+
const body = {
|
|
189
|
+
message: content,
|
|
190
|
+
projectId,
|
|
191
|
+
visitorId,
|
|
192
|
+
...conversationId ? { conversationId } : {},
|
|
193
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : void 0
|
|
194
|
+
};
|
|
195
|
+
const res = await fetch(url, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: { "Content-Type": "application/json" },
|
|
198
|
+
body: JSON.stringify(body)
|
|
199
|
+
});
|
|
200
|
+
const json = await res.json();
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
throw new Error(json?.error?.message || json?.message || `Signal API error: ${res.status}`);
|
|
203
|
+
}
|
|
204
|
+
const data = json?.data ?? json;
|
|
205
|
+
const aiContent = data?.content ?? data?.response ?? data?.message ?? "I'm sorry, I couldn't process that.";
|
|
206
|
+
const suggestions = data?.suggestions;
|
|
207
|
+
const newConversationId = data?.conversationId ?? conversationId;
|
|
208
|
+
return { content: aiContent, suggestions, conversationId: newConversationId };
|
|
209
|
+
},
|
|
210
|
+
[signalUrl, projectId, visitorId]
|
|
211
|
+
);
|
|
178
212
|
const handleSocketMessage = React2.useCallback((data) => {
|
|
179
213
|
switch (data.type || data.event) {
|
|
180
214
|
case "message": {
|
|
@@ -363,13 +397,50 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
363
397
|
}, [fetchWidgetConfig, checkAvailability]);
|
|
364
398
|
React2.useEffect(() => {
|
|
365
399
|
if (isOpen && !showWelcome && !checkingAvailability && !showOfflineForm && !sessionId) {
|
|
366
|
-
initSession().then(async (
|
|
367
|
-
if (!id) return;
|
|
368
|
-
|
|
369
|
-
await connectSocket(id);
|
|
400
|
+
initSession().then(async (result) => {
|
|
401
|
+
if (!result?.id) return;
|
|
402
|
+
const { id, signalEnabled } = result;
|
|
370
403
|
const pending = pendingInitialMessageRef.current;
|
|
371
404
|
if (pending) {
|
|
372
405
|
pendingInitialMessageRef.current = null;
|
|
406
|
+
}
|
|
407
|
+
if (signalEnabled && pending) {
|
|
408
|
+
setIsLoading(true);
|
|
409
|
+
setMessages((prev) => [
|
|
410
|
+
...prev,
|
|
411
|
+
{ id: `u-${Date.now()}`, role: "user", content: pending, timestamp: /* @__PURE__ */ new Date() }
|
|
412
|
+
]);
|
|
413
|
+
try {
|
|
414
|
+
const { content: aiContent, suggestions } = await sendToSignalApi(pending, id);
|
|
415
|
+
setMessages((prev) => [
|
|
416
|
+
...prev,
|
|
417
|
+
{
|
|
418
|
+
id: `ai-${Date.now()}`,
|
|
419
|
+
role: "assistant",
|
|
420
|
+
content: aiContent,
|
|
421
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
422
|
+
...suggestions?.length ? { suggestions } : {}
|
|
423
|
+
}
|
|
424
|
+
]);
|
|
425
|
+
} catch (err) {
|
|
426
|
+
console.error("[ChatWidget] Signal API error:", err);
|
|
427
|
+
setMessages((prev) => [
|
|
428
|
+
...prev,
|
|
429
|
+
{
|
|
430
|
+
id: `err-${Date.now()}`,
|
|
431
|
+
role: "assistant",
|
|
432
|
+
content: "I apologize, but I encountered an error. Would you like to speak with a team member?",
|
|
433
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
434
|
+
suggestions: ["Talk to a person", "Try again"]
|
|
435
|
+
}
|
|
436
|
+
]);
|
|
437
|
+
} finally {
|
|
438
|
+
setIsLoading(false);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
setConnectionStatus("connecting");
|
|
442
|
+
await connectSocket(id);
|
|
443
|
+
if (!signalEnabled && pending) {
|
|
373
444
|
const waitForSocket = () => new Promise((resolve) => {
|
|
374
445
|
const check = (attempts = 0) => {
|
|
375
446
|
if (socketRef.current?.connected || attempts > 20) {
|
|
@@ -390,7 +461,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
390
461
|
return () => {
|
|
391
462
|
if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current);
|
|
392
463
|
};
|
|
393
|
-
}, [isOpen, showWelcome, checkingAvailability, showOfflineForm, sessionId, initSession, connectSocket]);
|
|
464
|
+
}, [isOpen, showWelcome, checkingAvailability, showOfflineForm, sessionId, initSession, connectSocket, sendToSignalApi]);
|
|
394
465
|
const handleToggle = React2.useCallback(() => {
|
|
395
466
|
setIsOpen((prev) => !prev);
|
|
396
467
|
}, []);
|
|
@@ -480,6 +551,36 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
480
551
|
setMessages((prev) => [...prev, userMessage]);
|
|
481
552
|
setInputValue("");
|
|
482
553
|
setIsLoading(true);
|
|
554
|
+
if (widgetConfig?.signal_enabled && hasText) {
|
|
555
|
+
try {
|
|
556
|
+
const { content: aiContent, suggestions } = await sendToSignalApi(content, sessionId);
|
|
557
|
+
setMessages((prev) => [
|
|
558
|
+
...prev,
|
|
559
|
+
{
|
|
560
|
+
id: `ai-${Date.now()}`,
|
|
561
|
+
role: "assistant",
|
|
562
|
+
content: aiContent,
|
|
563
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
564
|
+
...suggestions?.length ? { suggestions } : {}
|
|
565
|
+
}
|
|
566
|
+
]);
|
|
567
|
+
} catch (err) {
|
|
568
|
+
console.error("[ChatWidget] Signal API error:", err);
|
|
569
|
+
setMessages((prev) => [
|
|
570
|
+
...prev,
|
|
571
|
+
{
|
|
572
|
+
id: `err-${Date.now()}`,
|
|
573
|
+
role: "assistant",
|
|
574
|
+
content: "I apologize, but I encountered an error. Would you like to speak with a team member?",
|
|
575
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
576
|
+
suggestions: ["Talk to a person", "Try again"]
|
|
577
|
+
}
|
|
578
|
+
]);
|
|
579
|
+
} finally {
|
|
580
|
+
setIsLoading(false);
|
|
581
|
+
}
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
483
584
|
const socket = socketRef.current;
|
|
484
585
|
if (socket?.connected) {
|
|
485
586
|
socket.emit("visitor:message", { content: userMessage.content, attachments: attachments.length ? attachments : void 0 });
|
|
@@ -497,7 +598,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
497
598
|
}
|
|
498
599
|
}, 3e3);
|
|
499
600
|
},
|
|
500
|
-
[inputValue, isLoading, pendingFiles, uploadWidgetFile]
|
|
601
|
+
[inputValue, isLoading, pendingFiles, uploadWidgetFile, widgetConfig?.signal_enabled, sendToSignalApi, sessionId]
|
|
501
602
|
);
|
|
502
603
|
const retryFailedSend = React2.useCallback(() => {
|
|
503
604
|
if (!lastFailedSend || !sessionId) return;
|
|
@@ -515,33 +616,48 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
515
616
|
if (!sessionId) return;
|
|
516
617
|
const apiKey = ensureApiKey();
|
|
517
618
|
if (!apiKey) return;
|
|
619
|
+
setCheckingHandoff(true);
|
|
518
620
|
try {
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
621
|
+
const firstAvail = await checkAvailability();
|
|
622
|
+
if (firstAvail?.agentsOnline && firstAvail.agentsOnline > 0) {
|
|
623
|
+
setCheckingHandoff(false);
|
|
624
|
+
await fetch(`${baseUrl}/engage/widget/handoff`, {
|
|
625
|
+
method: "POST",
|
|
626
|
+
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
627
|
+
body: JSON.stringify({ sessionId })
|
|
628
|
+
});
|
|
629
|
+
setMessages((prev) => [
|
|
630
|
+
...prev,
|
|
631
|
+
{ id: `handoff-${Date.now()}`, role: "system", content: "Connecting you with a team member. Please hold on!", timestamp: /* @__PURE__ */ new Date() }
|
|
632
|
+
]);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
636
|
+
const secondAvail = await checkAvailability();
|
|
637
|
+
setCheckingHandoff(false);
|
|
638
|
+
if (secondAvail?.agentsOnline && secondAvail.agentsOnline > 0) {
|
|
639
|
+
await fetch(`${baseUrl}/engage/widget/handoff`, {
|
|
640
|
+
method: "POST",
|
|
641
|
+
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
642
|
+
body: JSON.stringify({ sessionId })
|
|
643
|
+
});
|
|
644
|
+
setMessages((prev) => [
|
|
645
|
+
...prev,
|
|
646
|
+
{ id: `handoff-${Date.now()}`, role: "system", content: "Connecting you with a team member. Please hold on!", timestamp: /* @__PURE__ */ new Date() }
|
|
647
|
+
]);
|
|
648
|
+
} else {
|
|
524
649
|
setHandoffOfflinePrompt(widgetConfig?.offline_subheading ?? "Nobody is online right now. Leave your details and we'll get back to you.");
|
|
525
650
|
setShowOfflineForm(true);
|
|
526
651
|
setMessages((prev) => [
|
|
527
652
|
...prev,
|
|
528
653
|
{ id: `handoff-offline-${Date.now()}`, role: "system", content: offlineHeading, timestamp: /* @__PURE__ */ new Date() }
|
|
529
654
|
]);
|
|
530
|
-
return;
|
|
531
655
|
}
|
|
532
|
-
await fetch(`${baseUrl}/engage/widget/handoff`, {
|
|
533
|
-
method: "POST",
|
|
534
|
-
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
535
|
-
body: JSON.stringify({ sessionId })
|
|
536
|
-
});
|
|
537
|
-
setMessages((prev) => [
|
|
538
|
-
...prev,
|
|
539
|
-
{ id: `handoff-${Date.now()}`, role: "system", content: "Connecting you with a team member. Please hold on!", timestamp: /* @__PURE__ */ new Date() }
|
|
540
|
-
]);
|
|
541
656
|
} catch (error) {
|
|
657
|
+
setCheckingHandoff(false);
|
|
542
658
|
console.error("[ChatWidget] Handoff request failed:", error);
|
|
543
659
|
}
|
|
544
|
-
}, [sessionId, baseUrl, projectId, widgetConfig, offlineHeading]);
|
|
660
|
+
}, [sessionId, baseUrl, projectId, widgetConfig, offlineHeading, checkAvailability]);
|
|
545
661
|
const [offlineError, setOfflineError] = React2.useState(null);
|
|
546
662
|
const handleOfflineSubmit = React2.useCallback(
|
|
547
663
|
async (e) => {
|
|
@@ -842,7 +958,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
842
958
|
)
|
|
843
959
|
] }),
|
|
844
960
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
845
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 16, fontWeight: 600, color: "#111827", marginBottom: 6 }, children: "Checking for a team member" }),
|
|
961
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 16, fontWeight: 600, color: "#111827", marginBottom: 6 }, children: checkingHandoff ? "Looking for online support" : "Checking for a team member" }),
|
|
846
962
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 14, color: "#6b7280", lineHeight: 1.5 }, children: [
|
|
847
963
|
"One moment please",
|
|
848
964
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-flex", width: 20 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { animation: "checkDots 1.5s infinite steps(4, end)" }, children: "..." }) })
|
|
@@ -1045,11 +1161,11 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
1045
1161
|
},
|
|
1046
1162
|
children: [
|
|
1047
1163
|
Header,
|
|
1048
|
-
checkingAvailability ? CheckingScreen : showOfflineForm ? OfflineFormView : showWelcome && welcomeEnabled && messages.length === 0 ? WelcomeScreen : MessagesView,
|
|
1164
|
+
checkingAvailability || checkingHandoff ? CheckingScreen : showOfflineForm ? OfflineFormView : showWelcome && welcomeEnabled && messages.length === 0 ? WelcomeScreen : MessagesView,
|
|
1049
1165
|
showPoweredBy && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "6px 0", textAlign: "center", fontSize: 11, color: "#9ca3af", backgroundColor: "#ffffff", borderTop: "1px solid #f3f4f6" }, children: [
|
|
1050
1166
|
"Powered by",
|
|
1051
1167
|
" ",
|
|
1052
|
-
/* @__PURE__ */ jsxRuntime.jsx("a", { href: "https://uptrademedia.com", target: "_blank", rel: "noopener noreferrer", style: { color: "#6b7280", textDecoration: "none", fontWeight: 500 }, children: "
|
|
1168
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: "https://uptrademedia.com", target: "_blank", rel: "noopener noreferrer", style: { color: "#6b7280", textDecoration: "none", fontWeight: 500 }, children: "Sonor" })
|
|
1053
1169
|
] }),
|
|
1054
1170
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
1055
1171
|
@keyframes chatSlideUp {
|
|
@@ -1344,7 +1460,9 @@ function DesignRenderer({
|
|
|
1344
1460
|
)) });
|
|
1345
1461
|
}
|
|
1346
1462
|
function getApiConfig2() {
|
|
1347
|
-
const
|
|
1463
|
+
const isDev = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
1464
|
+
const defaultApiUrl = isDev && process.env?.NEXT_PUBLIC_UPTRADE_API_URL ? process.env.NEXT_PUBLIC_UPTRADE_API_URL : "https://api.uptrademedia.com";
|
|
1465
|
+
const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || defaultApiUrl : defaultApiUrl;
|
|
1348
1466
|
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
|
|
1349
1467
|
return { apiUrl, apiKey };
|
|
1350
1468
|
}
|
|
@@ -1705,5 +1823,5 @@ function getDeviceType() {
|
|
|
1705
1823
|
exports.ChatWidget = ChatWidget;
|
|
1706
1824
|
exports.DesignRenderer = DesignRenderer;
|
|
1707
1825
|
exports.EngageWidget = EngageWidget;
|
|
1708
|
-
//# sourceMappingURL=chunk-
|
|
1709
|
-
//# sourceMappingURL=chunk-
|
|
1826
|
+
//# sourceMappingURL=chunk-CTKXFLIF.js.map
|
|
1827
|
+
//# sourceMappingURL=chunk-CTKXFLIF.js.map
|