@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
|
@@ -4,9 +4,13 @@ import { usePathname } from 'next/navigation';
|
|
|
4
4
|
|
|
5
5
|
// src/engage/ChatWidget.tsx
|
|
6
6
|
function getApiConfig() {
|
|
7
|
-
const
|
|
7
|
+
const isDev = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
8
|
+
const defaultApiUrl = isDev && process.env?.NEXT_PUBLIC_UPTRADE_API_URL ? process.env.NEXT_PUBLIC_UPTRADE_API_URL : "https://api.uptrademedia.com";
|
|
9
|
+
const defaultSignalUrl = isDev && process.env?.NEXT_PUBLIC_SIGNAL_API_URL ? process.env.NEXT_PUBLIC_SIGNAL_API_URL : "https://signal.uptrademedia.com";
|
|
10
|
+
const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || defaultApiUrl : defaultApiUrl;
|
|
8
11
|
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
|
|
9
|
-
|
|
12
|
+
const signalUrl = typeof window !== "undefined" ? window.__SITE_KIT_SIGNAL_URL__ || defaultSignalUrl : defaultSignalUrl;
|
|
13
|
+
return { apiUrl, apiKey, signalUrl };
|
|
10
14
|
}
|
|
11
15
|
function generateVisitorId() {
|
|
12
16
|
const stored = typeof localStorage !== "undefined" ? localStorage.getItem("engage_visitor_id") : null;
|
|
@@ -29,7 +33,7 @@ function isLightColor(hex) {
|
|
|
29
33
|
const b = num & 255;
|
|
30
34
|
return (r * 299 + g * 587 + b * 114) / 1e3 > 160;
|
|
31
35
|
}
|
|
32
|
-
function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
36
|
+
function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl, signalUrl: propSignalUrl }) {
|
|
33
37
|
const [resolvedProjectId, setResolvedProjectId] = useState(propProjectId || null);
|
|
34
38
|
const projectId = propProjectId || resolvedProjectId || "";
|
|
35
39
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -50,6 +54,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
50
54
|
const [lastFailedSend, setLastFailedSend] = useState(null);
|
|
51
55
|
const [showWelcome, setShowWelcome] = useState(true);
|
|
52
56
|
const [checkingAvailability, setCheckingAvailability] = useState(false);
|
|
57
|
+
const [checkingHandoff, setCheckingHandoff] = useState(false);
|
|
53
58
|
useRef(null);
|
|
54
59
|
const pendingInitialMessageRef = useRef(null);
|
|
55
60
|
const apiKeyMissingWarnedRef = useRef(false);
|
|
@@ -77,6 +82,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
77
82
|
const offlineHeading = widgetConfig?.offline_heading ?? "No agents available right now";
|
|
78
83
|
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!";
|
|
79
84
|
const baseUrl = propApiUrl || getApiConfig().apiUrl;
|
|
85
|
+
const signalUrl = propSignalUrl || getApiConfig().signalUrl;
|
|
80
86
|
useEffect(() => {
|
|
81
87
|
if (propProjectId || resolvedProjectId) return;
|
|
82
88
|
const { apiKey } = getApiConfig();
|
|
@@ -149,6 +155,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
149
155
|
const { data } = await response.json();
|
|
150
156
|
const session = data.session ?? data;
|
|
151
157
|
const sid = session?.id ?? session?.session_id ?? data.id ?? data.session_id;
|
|
158
|
+
const signalEnabled = session?.chat_mode === "ai" || session?.chat_mode === "hybrid";
|
|
152
159
|
setSessionId(sid);
|
|
153
160
|
const messages2 = session?.messages ?? data.messages ?? [];
|
|
154
161
|
if (messages2.length > 0) {
|
|
@@ -162,13 +169,40 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
162
169
|
}))
|
|
163
170
|
);
|
|
164
171
|
}
|
|
165
|
-
return sid;
|
|
172
|
+
return { id: sid, signalEnabled };
|
|
166
173
|
}
|
|
167
174
|
} catch (error) {
|
|
168
175
|
console.error("[ChatWidget] Session init failed:", error);
|
|
169
176
|
}
|
|
170
177
|
return null;
|
|
171
178
|
}, [projectId, visitorId, baseUrl]);
|
|
179
|
+
const sendToSignalApi = useCallback(
|
|
180
|
+
async (content, conversationId) => {
|
|
181
|
+
const url = `${signalUrl.replace(/\/$/, "")}/echo/public/chat`;
|
|
182
|
+
const body = {
|
|
183
|
+
message: content,
|
|
184
|
+
projectId,
|
|
185
|
+
visitorId,
|
|
186
|
+
...conversationId ? { conversationId } : {},
|
|
187
|
+
pageUrl: typeof window !== "undefined" ? window.location.href : void 0
|
|
188
|
+
};
|
|
189
|
+
const res = await fetch(url, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: { "Content-Type": "application/json" },
|
|
192
|
+
body: JSON.stringify(body)
|
|
193
|
+
});
|
|
194
|
+
const json = await res.json();
|
|
195
|
+
if (!res.ok) {
|
|
196
|
+
throw new Error(json?.error?.message || json?.message || `Signal API error: ${res.status}`);
|
|
197
|
+
}
|
|
198
|
+
const data = json?.data ?? json;
|
|
199
|
+
const aiContent = data?.content ?? data?.response ?? data?.message ?? "I'm sorry, I couldn't process that.";
|
|
200
|
+
const suggestions = data?.suggestions;
|
|
201
|
+
const newConversationId = data?.conversationId ?? conversationId;
|
|
202
|
+
return { content: aiContent, suggestions, conversationId: newConversationId };
|
|
203
|
+
},
|
|
204
|
+
[signalUrl, projectId, visitorId]
|
|
205
|
+
);
|
|
172
206
|
const handleSocketMessage = useCallback((data) => {
|
|
173
207
|
switch (data.type || data.event) {
|
|
174
208
|
case "message": {
|
|
@@ -357,13 +391,50 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
357
391
|
}, [fetchWidgetConfig, checkAvailability]);
|
|
358
392
|
useEffect(() => {
|
|
359
393
|
if (isOpen && !showWelcome && !checkingAvailability && !showOfflineForm && !sessionId) {
|
|
360
|
-
initSession().then(async (
|
|
361
|
-
if (!id) return;
|
|
362
|
-
|
|
363
|
-
await connectSocket(id);
|
|
394
|
+
initSession().then(async (result) => {
|
|
395
|
+
if (!result?.id) return;
|
|
396
|
+
const { id, signalEnabled } = result;
|
|
364
397
|
const pending = pendingInitialMessageRef.current;
|
|
365
398
|
if (pending) {
|
|
366
399
|
pendingInitialMessageRef.current = null;
|
|
400
|
+
}
|
|
401
|
+
if (signalEnabled && pending) {
|
|
402
|
+
setIsLoading(true);
|
|
403
|
+
setMessages((prev) => [
|
|
404
|
+
...prev,
|
|
405
|
+
{ id: `u-${Date.now()}`, role: "user", content: pending, timestamp: /* @__PURE__ */ new Date() }
|
|
406
|
+
]);
|
|
407
|
+
try {
|
|
408
|
+
const { content: aiContent, suggestions } = await sendToSignalApi(pending, id);
|
|
409
|
+
setMessages((prev) => [
|
|
410
|
+
...prev,
|
|
411
|
+
{
|
|
412
|
+
id: `ai-${Date.now()}`,
|
|
413
|
+
role: "assistant",
|
|
414
|
+
content: aiContent,
|
|
415
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
416
|
+
...suggestions?.length ? { suggestions } : {}
|
|
417
|
+
}
|
|
418
|
+
]);
|
|
419
|
+
} catch (err) {
|
|
420
|
+
console.error("[ChatWidget] Signal API error:", err);
|
|
421
|
+
setMessages((prev) => [
|
|
422
|
+
...prev,
|
|
423
|
+
{
|
|
424
|
+
id: `err-${Date.now()}`,
|
|
425
|
+
role: "assistant",
|
|
426
|
+
content: "I apologize, but I encountered an error. Would you like to speak with a team member?",
|
|
427
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
428
|
+
suggestions: ["Talk to a person", "Try again"]
|
|
429
|
+
}
|
|
430
|
+
]);
|
|
431
|
+
} finally {
|
|
432
|
+
setIsLoading(false);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
setConnectionStatus("connecting");
|
|
436
|
+
await connectSocket(id);
|
|
437
|
+
if (!signalEnabled && pending) {
|
|
367
438
|
const waitForSocket = () => new Promise((resolve) => {
|
|
368
439
|
const check = (attempts = 0) => {
|
|
369
440
|
if (socketRef.current?.connected || attempts > 20) {
|
|
@@ -384,7 +455,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
384
455
|
return () => {
|
|
385
456
|
if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current);
|
|
386
457
|
};
|
|
387
|
-
}, [isOpen, showWelcome, checkingAvailability, showOfflineForm, sessionId, initSession, connectSocket]);
|
|
458
|
+
}, [isOpen, showWelcome, checkingAvailability, showOfflineForm, sessionId, initSession, connectSocket, sendToSignalApi]);
|
|
388
459
|
const handleToggle = useCallback(() => {
|
|
389
460
|
setIsOpen((prev) => !prev);
|
|
390
461
|
}, []);
|
|
@@ -474,6 +545,36 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
474
545
|
setMessages((prev) => [...prev, userMessage]);
|
|
475
546
|
setInputValue("");
|
|
476
547
|
setIsLoading(true);
|
|
548
|
+
if (widgetConfig?.signal_enabled && hasText) {
|
|
549
|
+
try {
|
|
550
|
+
const { content: aiContent, suggestions } = await sendToSignalApi(content, sessionId);
|
|
551
|
+
setMessages((prev) => [
|
|
552
|
+
...prev,
|
|
553
|
+
{
|
|
554
|
+
id: `ai-${Date.now()}`,
|
|
555
|
+
role: "assistant",
|
|
556
|
+
content: aiContent,
|
|
557
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
558
|
+
...suggestions?.length ? { suggestions } : {}
|
|
559
|
+
}
|
|
560
|
+
]);
|
|
561
|
+
} catch (err) {
|
|
562
|
+
console.error("[ChatWidget] Signal API error:", err);
|
|
563
|
+
setMessages((prev) => [
|
|
564
|
+
...prev,
|
|
565
|
+
{
|
|
566
|
+
id: `err-${Date.now()}`,
|
|
567
|
+
role: "assistant",
|
|
568
|
+
content: "I apologize, but I encountered an error. Would you like to speak with a team member?",
|
|
569
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
570
|
+
suggestions: ["Talk to a person", "Try again"]
|
|
571
|
+
}
|
|
572
|
+
]);
|
|
573
|
+
} finally {
|
|
574
|
+
setIsLoading(false);
|
|
575
|
+
}
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
477
578
|
const socket = socketRef.current;
|
|
478
579
|
if (socket?.connected) {
|
|
479
580
|
socket.emit("visitor:message", { content: userMessage.content, attachments: attachments.length ? attachments : void 0 });
|
|
@@ -491,7 +592,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
491
592
|
}
|
|
492
593
|
}, 3e3);
|
|
493
594
|
},
|
|
494
|
-
[inputValue, isLoading, pendingFiles, uploadWidgetFile]
|
|
595
|
+
[inputValue, isLoading, pendingFiles, uploadWidgetFile, widgetConfig?.signal_enabled, sendToSignalApi, sessionId]
|
|
495
596
|
);
|
|
496
597
|
const retryFailedSend = useCallback(() => {
|
|
497
598
|
if (!lastFailedSend || !sessionId) return;
|
|
@@ -509,33 +610,48 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
509
610
|
if (!sessionId) return;
|
|
510
611
|
const apiKey = ensureApiKey();
|
|
511
612
|
if (!apiKey) return;
|
|
613
|
+
setCheckingHandoff(true);
|
|
512
614
|
try {
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
615
|
+
const firstAvail = await checkAvailability();
|
|
616
|
+
if (firstAvail?.agentsOnline && firstAvail.agentsOnline > 0) {
|
|
617
|
+
setCheckingHandoff(false);
|
|
618
|
+
await fetch(`${baseUrl}/engage/widget/handoff`, {
|
|
619
|
+
method: "POST",
|
|
620
|
+
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
621
|
+
body: JSON.stringify({ sessionId })
|
|
622
|
+
});
|
|
623
|
+
setMessages((prev) => [
|
|
624
|
+
...prev,
|
|
625
|
+
{ id: `handoff-${Date.now()}`, role: "system", content: "Connecting you with a team member. Please hold on!", timestamp: /* @__PURE__ */ new Date() }
|
|
626
|
+
]);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
630
|
+
const secondAvail = await checkAvailability();
|
|
631
|
+
setCheckingHandoff(false);
|
|
632
|
+
if (secondAvail?.agentsOnline && secondAvail.agentsOnline > 0) {
|
|
633
|
+
await fetch(`${baseUrl}/engage/widget/handoff`, {
|
|
634
|
+
method: "POST",
|
|
635
|
+
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
636
|
+
body: JSON.stringify({ sessionId })
|
|
637
|
+
});
|
|
638
|
+
setMessages((prev) => [
|
|
639
|
+
...prev,
|
|
640
|
+
{ id: `handoff-${Date.now()}`, role: "system", content: "Connecting you with a team member. Please hold on!", timestamp: /* @__PURE__ */ new Date() }
|
|
641
|
+
]);
|
|
642
|
+
} else {
|
|
518
643
|
setHandoffOfflinePrompt(widgetConfig?.offline_subheading ?? "Nobody is online right now. Leave your details and we'll get back to you.");
|
|
519
644
|
setShowOfflineForm(true);
|
|
520
645
|
setMessages((prev) => [
|
|
521
646
|
...prev,
|
|
522
647
|
{ id: `handoff-offline-${Date.now()}`, role: "system", content: offlineHeading, timestamp: /* @__PURE__ */ new Date() }
|
|
523
648
|
]);
|
|
524
|
-
return;
|
|
525
649
|
}
|
|
526
|
-
await fetch(`${baseUrl}/engage/widget/handoff`, {
|
|
527
|
-
method: "POST",
|
|
528
|
-
headers: { "Content-Type": "application/json", "x-api-key": apiKey },
|
|
529
|
-
body: JSON.stringify({ sessionId })
|
|
530
|
-
});
|
|
531
|
-
setMessages((prev) => [
|
|
532
|
-
...prev,
|
|
533
|
-
{ id: `handoff-${Date.now()}`, role: "system", content: "Connecting you with a team member. Please hold on!", timestamp: /* @__PURE__ */ new Date() }
|
|
534
|
-
]);
|
|
535
650
|
} catch (error) {
|
|
651
|
+
setCheckingHandoff(false);
|
|
536
652
|
console.error("[ChatWidget] Handoff request failed:", error);
|
|
537
653
|
}
|
|
538
|
-
}, [sessionId, baseUrl, projectId, widgetConfig, offlineHeading]);
|
|
654
|
+
}, [sessionId, baseUrl, projectId, widgetConfig, offlineHeading, checkAvailability]);
|
|
539
655
|
const [offlineError, setOfflineError] = useState(null);
|
|
540
656
|
const handleOfflineSubmit = useCallback(
|
|
541
657
|
async (e) => {
|
|
@@ -836,7 +952,7 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
836
952
|
)
|
|
837
953
|
] }),
|
|
838
954
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
839
|
-
/* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 600, color: "#111827", marginBottom: 6 }, children: "Checking for a team member" }),
|
|
955
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 600, color: "#111827", marginBottom: 6 }, children: checkingHandoff ? "Looking for online support" : "Checking for a team member" }),
|
|
840
956
|
/* @__PURE__ */ jsxs("div", { style: { fontSize: 14, color: "#6b7280", lineHeight: 1.5 }, children: [
|
|
841
957
|
"One moment please",
|
|
842
958
|
/* @__PURE__ */ jsx("span", { style: { display: "inline-flex", width: 20 }, children: /* @__PURE__ */ jsx("span", { style: { animation: "checkDots 1.5s infinite steps(4, end)" }, children: "..." }) })
|
|
@@ -1039,11 +1155,11 @@ function ChatWidget({ projectId: propProjectId, config, apiUrl: propApiUrl }) {
|
|
|
1039
1155
|
},
|
|
1040
1156
|
children: [
|
|
1041
1157
|
Header,
|
|
1042
|
-
checkingAvailability ? CheckingScreen : showOfflineForm ? OfflineFormView : showWelcome && welcomeEnabled && messages.length === 0 ? WelcomeScreen : MessagesView,
|
|
1158
|
+
checkingAvailability || checkingHandoff ? CheckingScreen : showOfflineForm ? OfflineFormView : showWelcome && welcomeEnabled && messages.length === 0 ? WelcomeScreen : MessagesView,
|
|
1043
1159
|
showPoweredBy && /* @__PURE__ */ jsxs("div", { style: { padding: "6px 0", textAlign: "center", fontSize: 11, color: "#9ca3af", backgroundColor: "#ffffff", borderTop: "1px solid #f3f4f6" }, children: [
|
|
1044
1160
|
"Powered by",
|
|
1045
1161
|
" ",
|
|
1046
|
-
/* @__PURE__ */ jsx("a", { href: "https://uptrademedia.com", target: "_blank", rel: "noopener noreferrer", style: { color: "#6b7280", textDecoration: "none", fontWeight: 500 }, children: "
|
|
1162
|
+
/* @__PURE__ */ jsx("a", { href: "https://uptrademedia.com", target: "_blank", rel: "noopener noreferrer", style: { color: "#6b7280", textDecoration: "none", fontWeight: 500 }, children: "Sonor" })
|
|
1047
1163
|
] }),
|
|
1048
1164
|
/* @__PURE__ */ jsx("style", { children: `
|
|
1049
1165
|
@keyframes chatSlideUp {
|
|
@@ -1338,7 +1454,9 @@ function DesignRenderer({
|
|
|
1338
1454
|
)) });
|
|
1339
1455
|
}
|
|
1340
1456
|
function getApiConfig2() {
|
|
1341
|
-
const
|
|
1457
|
+
const isDev = typeof process !== "undefined" && process.env?.NODE_ENV === "development";
|
|
1458
|
+
const defaultApiUrl = isDev && process.env?.NEXT_PUBLIC_UPTRADE_API_URL ? process.env.NEXT_PUBLIC_UPTRADE_API_URL : "https://api.uptrademedia.com";
|
|
1459
|
+
const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || defaultApiUrl : defaultApiUrl;
|
|
1342
1460
|
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
|
|
1343
1461
|
return { apiUrl, apiKey };
|
|
1344
1462
|
}
|
|
@@ -1697,5 +1815,5 @@ function getDeviceType() {
|
|
|
1697
1815
|
}
|
|
1698
1816
|
|
|
1699
1817
|
export { ChatWidget, DesignRenderer, EngageWidget };
|
|
1700
|
-
//# sourceMappingURL=chunk-
|
|
1701
|
-
//# sourceMappingURL=chunk-
|
|
1818
|
+
//# sourceMappingURL=chunk-TOUIKTXU.mjs.map
|
|
1819
|
+
//# sourceMappingURL=chunk-TOUIKTXU.mjs.map
|