@uptrademedia/site-kit 1.0.5 → 1.0.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.
Files changed (169) hide show
  1. package/dist/analytics/index.js +7 -7
  2. package/dist/analytics/index.mjs +3 -3
  3. package/dist/api-QUIPJJCX.js +77 -0
  4. package/dist/api-QUIPJJCX.js.map +1 -0
  5. package/dist/api-V3BA5PMX.mjs +4 -0
  6. package/dist/api-V3BA5PMX.mjs.map +1 -0
  7. package/dist/blog/index.d.mts +2 -2
  8. package/dist/blog/index.d.ts +2 -2
  9. package/dist/blog/index.js +40 -6
  10. package/dist/blog/index.js.map +1 -1
  11. package/dist/blog/index.mjs +40 -6
  12. package/dist/blog/index.mjs.map +1 -1
  13. package/dist/blog/server.d.mts +3 -2
  14. package/dist/blog/server.d.ts +3 -2
  15. package/dist/blog/server.js +13 -6
  16. package/dist/blog/server.js.map +1 -1
  17. package/dist/blog/server.mjs +13 -6
  18. package/dist/blog/server.mjs.map +1 -1
  19. package/dist/{chunk-FKVJOT2F.mjs → chunk-42EXHJTC.mjs} +196 -7
  20. package/dist/chunk-42EXHJTC.mjs.map +1 -0
  21. package/dist/{scanner-AZV5I6US.mjs → chunk-44OMJFCG.mjs} +354 -14
  22. package/dist/chunk-44OMJFCG.mjs.map +1 -0
  23. package/dist/chunk-4TGJYNHV.js +981 -0
  24. package/dist/chunk-4TGJYNHV.js.map +1 -0
  25. package/dist/chunk-4XPGGLVP.mjs +53 -0
  26. package/dist/{chunk-NYKRE2FL.mjs.map → chunk-4XPGGLVP.mjs.map} +1 -1
  27. package/dist/{generators-TO2FKJR6.mjs → chunk-6ONUXZDO.mjs} +26 -9
  28. package/dist/chunk-6ONUXZDO.mjs.map +1 -0
  29. package/dist/chunk-CG53ASWX.mjs +729 -0
  30. package/dist/chunk-CG53ASWX.mjs.map +1 -0
  31. package/dist/{scanner-ETJAMIT7.js → chunk-DERI27QC.js} +448 -102
  32. package/dist/chunk-DERI27QC.js.map +1 -0
  33. package/dist/chunk-DYM5ML2V.mjs +1518 -0
  34. package/dist/chunk-DYM5ML2V.mjs.map +1 -0
  35. package/dist/chunk-FLZZOX44.js +1526 -0
  36. package/dist/chunk-FLZZOX44.js.map +1 -0
  37. package/dist/{chunk-7H6I3ECV.mjs → chunk-FQVGK746.mjs} +63 -3
  38. package/dist/chunk-FQVGK746.mjs.map +1 -0
  39. package/dist/{chunk-GQ6ZOU2N.mjs → chunk-JGQPAXTL.mjs} +4 -4
  40. package/dist/{chunk-GQ6ZOU2N.mjs.map → chunk-JGQPAXTL.mjs.map} +1 -1
  41. package/dist/{chunk-V3F5J6CV.js → chunk-JUEVN4Q4.js} +196 -7
  42. package/dist/chunk-JUEVN4Q4.js.map +1 -0
  43. package/dist/chunk-KKMGTT7F.mjs +968 -0
  44. package/dist/chunk-KKMGTT7F.mjs.map +1 -0
  45. package/dist/{chunk-XQJX252G.mjs → chunk-MB3WR5KJ.mjs} +28 -18
  46. package/dist/chunk-MB3WR5KJ.mjs.map +1 -0
  47. package/dist/{chunk-2IHTEKHU.mjs → chunk-QD5CN2OI.mjs} +16 -7
  48. package/dist/chunk-QD5CN2OI.mjs.map +1 -0
  49. package/dist/{chunk-SBVEYCSV.js → chunk-QQB4FO4Q.js} +7 -7
  50. package/dist/{chunk-SBVEYCSV.js.map → chunk-QQB4FO4Q.js.map} +1 -1
  51. package/dist/{generators-YZWIGHCO.js → chunk-S2GXR5HY.js} +26 -9
  52. package/dist/chunk-S2GXR5HY.js.map +1 -0
  53. package/dist/{chunk-O2OHHBUD.js → chunk-TDK7DLCH.js} +30 -20
  54. package/dist/chunk-TDK7DLCH.js.map +1 -0
  55. package/dist/{chunk-QP5NCO2E.js → chunk-VDI7KYME.js} +67 -2
  56. package/dist/chunk-VDI7KYME.js.map +1 -0
  57. package/dist/chunk-VOR53RUR.js +753 -0
  58. package/dist/chunk-VOR53RUR.js.map +1 -0
  59. package/dist/{chunk-GAJLEDRD.js → chunk-ZKJ7JKFV.js} +16 -7
  60. package/dist/chunk-ZKJ7JKFV.js.map +1 -0
  61. package/dist/chunk-ZSMWDLMK.js +63 -0
  62. package/dist/{chunk-EQCVQC35.js.map → chunk-ZSMWDLMK.js.map} +1 -1
  63. package/dist/cli/index.js +37269 -0
  64. package/dist/cli/index.js.map +1 -0
  65. package/dist/cli/index.mjs +37233 -0
  66. package/dist/cli/index.mjs.map +1 -0
  67. package/dist/commerce/index.js +1 -1
  68. package/dist/commerce/index.mjs +1 -1
  69. package/dist/commerce/server.d.mts +12 -3
  70. package/dist/commerce/server.d.ts +12 -3
  71. package/dist/commerce/server.js +71 -70
  72. package/dist/commerce/server.js.map +1 -1
  73. package/dist/commerce/server.mjs +71 -70
  74. package/dist/commerce/server.mjs.map +1 -1
  75. package/dist/engage/index.d.mts +6 -4
  76. package/dist/engage/index.d.ts +6 -4
  77. package/dist/engage/index.js +8 -4
  78. package/dist/engage/index.mjs +2 -2
  79. package/dist/forms/index.js +1 -1
  80. package/dist/forms/index.mjs +1 -1
  81. package/dist/generators-5EU4PTVF.js +33 -0
  82. package/dist/generators-5EU4PTVF.js.map +1 -0
  83. package/dist/generators-TYPILCWD.mjs +4 -0
  84. package/dist/generators-TYPILCWD.mjs.map +1 -0
  85. package/dist/images/index.js +11 -11
  86. package/dist/images/index.mjs +4 -4
  87. package/dist/index.d.mts +154 -5
  88. package/dist/index.d.ts +154 -5
  89. package/dist/index.js +940 -24
  90. package/dist/index.js.map +1 -1
  91. package/dist/index.mjs +819 -7
  92. package/dist/index.mjs.map +1 -1
  93. package/dist/llms/index.d.mts +657 -0
  94. package/dist/llms/index.d.ts +657 -0
  95. package/dist/llms/index.js +101 -0
  96. package/dist/llms/index.js.map +1 -0
  97. package/dist/llms/index.mjs +4 -0
  98. package/dist/llms/index.mjs.map +1 -0
  99. package/dist/migrator-ARLHUNB3.mjs +4 -0
  100. package/dist/migrator-ARLHUNB3.mjs.map +1 -0
  101. package/dist/migrator-VZLBH3VY.js +37 -0
  102. package/dist/migrator-VZLBH3VY.js.map +1 -0
  103. package/dist/redirects/index.js +1 -1
  104. package/dist/redirects/index.mjs +1 -1
  105. package/dist/reputation/index.js +1 -1
  106. package/dist/reputation/index.mjs +1 -1
  107. package/dist/{routing-BWjUF7lp.d.ts → routing-CF91y6NO.d.ts} +1 -1
  108. package/dist/{routing-CgmRi9tD.d.mts → routing-CIOFpFCB.d.mts} +1 -1
  109. package/dist/scanner-7ZMUM2P5.mjs +4 -0
  110. package/dist/scanner-7ZMUM2P5.mjs.map +1 -0
  111. package/dist/scanner-OY7UF3WA.js +53 -0
  112. package/dist/scanner-OY7UF3WA.js.map +1 -0
  113. package/dist/seo/index.d.mts +267 -7
  114. package/dist/seo/index.d.ts +267 -7
  115. package/dist/seo/index.js +432 -24
  116. package/dist/seo/index.js.map +1 -1
  117. package/dist/seo/index.mjs +400 -11
  118. package/dist/seo/index.mjs.map +1 -1
  119. package/dist/seo/server.d.mts +11 -4
  120. package/dist/seo/server.d.ts +11 -4
  121. package/dist/seo/server.js +17 -17
  122. package/dist/seo/server.mjs +3 -3
  123. package/dist/setup/client.js +1 -1
  124. package/dist/setup/client.mjs +1 -1
  125. package/dist/setup/index.js +3 -3
  126. package/dist/setup/index.mjs +2 -2
  127. package/dist/setup/server.js +3 -3
  128. package/dist/setup/server.mjs +2 -2
  129. package/dist/sitemap/index.js +2 -2
  130. package/dist/sitemap/index.js.map +1 -1
  131. package/dist/sitemap/index.mjs +2 -2
  132. package/dist/sitemap/index.mjs.map +1 -1
  133. package/dist/{types-C0pJGfbH.d.mts → types-D6FHAVWX.d.mts} +99 -3
  134. package/dist/{types-C0pJGfbH.d.ts → types-D6FHAVWX.d.ts} +99 -3
  135. package/dist/{types-BN4OwtCO.d.mts → types-DI0jnhjJ.d.mts} +2 -0
  136. package/dist/{types-BN4OwtCO.d.ts → types-DI0jnhjJ.d.ts} +2 -0
  137. package/dist/{types-BmzutFwy.d.ts → types-j8X4vUhB.d.mts} +19 -2
  138. package/dist/{types-BmzutFwy.d.mts → types-j8X4vUhB.d.ts} +19 -2
  139. package/dist/{web-vitals-BH55V7EJ.js → web-vitals-444RLW3B.js} +11 -11
  140. package/dist/{web-vitals-BH55V7EJ.js.map → web-vitals-444RLW3B.js.map} +1 -1
  141. package/dist/{web-vitals-RJYPWAR3.mjs → web-vitals-KPICZIEF.mjs} +3 -3
  142. package/dist/{web-vitals-RJYPWAR3.mjs.map → web-vitals-KPICZIEF.mjs.map} +1 -1
  143. package/package.json +9 -2
  144. package/dist/api-N35S3EES.js +0 -57
  145. package/dist/api-N35S3EES.js.map +0 -1
  146. package/dist/api-SYBTK7Z7.mjs +0 -4
  147. package/dist/api-SYBTK7Z7.mjs.map +0 -1
  148. package/dist/chunk-2IHTEKHU.mjs.map +0 -1
  149. package/dist/chunk-7H6I3ECV.mjs.map +0 -1
  150. package/dist/chunk-BGJLOJ7T.mjs +0 -605
  151. package/dist/chunk-BGJLOJ7T.mjs.map +0 -1
  152. package/dist/chunk-EQCVQC35.js +0 -35
  153. package/dist/chunk-FKVJOT2F.mjs.map +0 -1
  154. package/dist/chunk-GAJLEDRD.js.map +0 -1
  155. package/dist/chunk-NYKRE2FL.mjs +0 -31
  156. package/dist/chunk-O2OHHBUD.js.map +0 -1
  157. package/dist/chunk-QAYJV4KK.js +0 -608
  158. package/dist/chunk-QAYJV4KK.js.map +0 -1
  159. package/dist/chunk-QP5NCO2E.js.map +0 -1
  160. package/dist/chunk-V3F5J6CV.js.map +0 -1
  161. package/dist/chunk-XQJX252G.mjs.map +0 -1
  162. package/dist/generators-TO2FKJR6.mjs.map +0 -1
  163. package/dist/generators-YZWIGHCO.js.map +0 -1
  164. package/dist/migrator-V6KS75EA.mjs +0 -265
  165. package/dist/migrator-V6KS75EA.mjs.map +0 -1
  166. package/dist/migrator-XKM7YQCY.js +0 -272
  167. package/dist/migrator-XKM7YQCY.js.map +0 -1
  168. package/dist/scanner-AZV5I6US.mjs.map +0 -1
  169. package/dist/scanner-ETJAMIT7.js.map +0 -1
@@ -0,0 +1,1518 @@
1
+ import React2, { useState, useRef, useCallback, useEffect, createElement } from 'react';
2
+ import { io } from 'socket.io-client';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+ import { usePathname } from 'next/navigation';
5
+
6
+ // src/engage/ChatWidget.tsx
7
+ function getApiConfig() {
8
+ const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
9
+ const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
10
+ return { apiUrl, apiKey };
11
+ }
12
+ function generateVisitorId() {
13
+ const stored = typeof localStorage !== "undefined" ? localStorage.getItem("engage_visitor_id") : null;
14
+ if (stored) return stored;
15
+ const id = `visitor_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
16
+ if (typeof localStorage !== "undefined") {
17
+ localStorage.setItem("engage_visitor_id", id);
18
+ }
19
+ return id;
20
+ }
21
+ function ChatWidget({ projectId, config, apiUrl: propApiUrl }) {
22
+ const [isOpen, setIsOpen] = useState(false);
23
+ const [messages, setMessages] = useState([]);
24
+ const [inputValue, setInputValue] = useState("");
25
+ const [isLoading, setIsLoading] = useState(false);
26
+ const [availability, setAvailability] = useState(null);
27
+ const [sessionId, setSessionId] = useState(null);
28
+ const [visitorId] = useState(generateVisitorId);
29
+ const [agentTyping, setAgentTyping] = useState(false);
30
+ const [showOfflineForm, setShowOfflineForm] = useState(false);
31
+ const [offlineForm, setOfflineForm] = useState({ name: "", email: "", phone: "", message: "" });
32
+ const [offlineSubmitted, setOfflineSubmitted] = useState(false);
33
+ const [connectionStatus, setConnectionStatus] = useState("disconnected");
34
+ const [widgetConfig, setWidgetConfig] = useState(null);
35
+ const [handoffOfflinePrompt, setHandoffOfflinePrompt] = useState(null);
36
+ const [pendingFiles, setPendingFiles] = useState([]);
37
+ const [lastFailedSend, setLastFailedSend] = useState(null);
38
+ useRef(null);
39
+ const messagesEndRef = useRef(null);
40
+ const inputRef = useRef(null);
41
+ const socketRef = useRef(null);
42
+ const pollingIntervalRef = useRef(null);
43
+ const position = config?.position || "bottom-right";
44
+ const buttonColor = config?.buttonColor || "#00afab";
45
+ const welcomeMessage = widgetConfig?.initial_message ?? widgetConfig?.welcome_message ?? config?.welcomeMessage ?? "Hi! How can I help you today?";
46
+ const offlineFormPrompt = handoffOfflinePrompt ?? widgetConfig?.form_description ?? widgetConfig?.offline_message ?? config?.offlineMessage ?? "We're currently offline. Leave us a message and we'll get back to you!";
47
+ const baseUrl = propApiUrl || getApiConfig().apiUrl;
48
+ const fetchWidgetConfig = useCallback(async () => {
49
+ try {
50
+ const { apiKey } = getApiConfig();
51
+ const response = await fetch(`${baseUrl}/api/engage/widget/config?projectId=${projectId}`, {
52
+ headers: apiKey ? { "x-api-key": apiKey } : {}
53
+ });
54
+ if (response.ok) {
55
+ const { data } = await response.json();
56
+ setWidgetConfig(data ?? null);
57
+ }
58
+ } catch (error) {
59
+ console.error("[ChatWidget] Config fetch failed:", error);
60
+ }
61
+ }, [projectId, baseUrl]);
62
+ const checkAvailability = useCallback(async () => {
63
+ try {
64
+ const { apiKey } = getApiConfig();
65
+ const response = await fetch(`${baseUrl}/api/engage/widget/availability?projectId=${projectId}`, {
66
+ headers: apiKey ? { "x-api-key": apiKey } : {}
67
+ });
68
+ if (response.ok) {
69
+ const { data } = await response.json();
70
+ setAvailability(data);
71
+ if (data.mode === "offline" && !sessionId) {
72
+ setShowOfflineForm(true);
73
+ }
74
+ }
75
+ } catch (error) {
76
+ console.error("[ChatWidget] Availability check failed:", error);
77
+ }
78
+ }, [projectId, baseUrl, sessionId]);
79
+ const initSession = useCallback(async () => {
80
+ try {
81
+ const { apiKey } = getApiConfig();
82
+ const response = await fetch(`${baseUrl}/api/engage/widget/session`, {
83
+ method: "POST",
84
+ headers: {
85
+ "Content-Type": "application/json",
86
+ ...apiKey && { "x-api-key": apiKey }
87
+ },
88
+ body: JSON.stringify({
89
+ projectId,
90
+ visitorId,
91
+ sourceUrl: typeof window !== "undefined" ? window.location.href : "",
92
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : ""
93
+ })
94
+ });
95
+ if (response.ok) {
96
+ const { data } = await response.json();
97
+ const sid = data.id || data.session_id;
98
+ setSessionId(sid);
99
+ if (data.messages?.length > 0) {
100
+ setMessages(data.messages.map((m) => ({
101
+ id: m.id,
102
+ role: m.role === "visitor" ? "user" : m.role,
103
+ content: m.content,
104
+ timestamp: new Date(m.created_at),
105
+ agentName: m.sender_name
106
+ })));
107
+ }
108
+ return sid;
109
+ }
110
+ } catch (error) {
111
+ console.error("[ChatWidget] Session init failed:", error);
112
+ }
113
+ return null;
114
+ }, [projectId, visitorId, baseUrl]);
115
+ const handleSocketMessage = useCallback((data) => {
116
+ switch (data.type || data.event) {
117
+ case "message": {
118
+ const role = data.role === "visitor" ? "user" : data.role === "ai" ? "assistant" : data.role;
119
+ const newMessage = {
120
+ id: data.id || `msg-${Date.now()}`,
121
+ role,
122
+ content: data.content ?? "",
123
+ timestamp: /* @__PURE__ */ new Date(),
124
+ agentName: data.agentName
125
+ };
126
+ const withAttachments = data.attachments?.length ? { ...newMessage, attachments: data.attachments } : newMessage;
127
+ const withSuggestions = data.suggestions?.length ? { ...withAttachments, suggestions: data.suggestions } : withAttachments;
128
+ setMessages((prev) => [...prev, withSuggestions]);
129
+ if (role === "assistant" || role === "agent") {
130
+ setAgentTyping(false);
131
+ setIsLoading(false);
132
+ }
133
+ break;
134
+ }
135
+ case "agent:joined":
136
+ setMessages((prev) => [...prev, {
137
+ id: `system-${Date.now()}`,
138
+ role: "system",
139
+ content: data.agentName ? `${data.agentName} has joined the chat.` : "An agent has joined the chat.",
140
+ timestamp: /* @__PURE__ */ new Date()
141
+ }]);
142
+ break;
143
+ case "typing":
144
+ setAgentTyping(data.isTyping);
145
+ break;
146
+ case "handoff:initiated":
147
+ setMessages((prev) => [...prev, {
148
+ id: `system-${Date.now()}`,
149
+ role: "system",
150
+ content: data.message || "Connecting you with a team member...",
151
+ timestamp: /* @__PURE__ */ new Date()
152
+ }]);
153
+ break;
154
+ case "chat:closed":
155
+ setMessages((prev) => [...prev, {
156
+ id: `system-${Date.now()}`,
157
+ role: "system",
158
+ content: data.message || "This chat has been closed.",
159
+ timestamp: /* @__PURE__ */ new Date()
160
+ }]);
161
+ break;
162
+ }
163
+ }, []);
164
+ const connectSocket = useCallback(
165
+ (currentSessionId) => {
166
+ if (socketRef.current?.connected) return;
167
+ const namespaceUrl = `${baseUrl.replace(/\/$/, "")}/engage/chat`;
168
+ const socket = io(namespaceUrl, {
169
+ query: { projectId, visitorId, sessionId: currentSessionId },
170
+ transports: ["websocket", "polling"]
171
+ });
172
+ socket.on("connect", () => {
173
+ setConnectionStatus("connected");
174
+ console.log("[ChatWidget] Socket.io connected");
175
+ if (currentSessionId) {
176
+ const { apiKey } = getApiConfig();
177
+ fetch(`${baseUrl}/api/engage/widget/messages?sessionId=${currentSessionId}`, {
178
+ headers: apiKey ? { "x-api-key": apiKey } : {}
179
+ }).then((res) => res.ok ? res.json() : null).then((json) => {
180
+ const data = json?.data ?? json;
181
+ if (Array.isArray(data) && data.length) {
182
+ setMessages((prev) => {
183
+ const byId = new Map(prev.map((m) => [m.id, m]));
184
+ data.forEach((m) => byId.set(m.id, { id: m.id, role: m.role === "visitor" ? "user" : m.role, content: m.content, timestamp: new Date(m.created_at), agentName: m.sender_name, attachments: m.attachments }));
185
+ return Array.from(byId.values()).sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
186
+ });
187
+ }
188
+ }).catch((err) => console.warn("[ChatWidget] Refetch messages on reconnect failed", err));
189
+ }
190
+ });
191
+ socket.on("message", (data) => {
192
+ handleSocketMessage({ type: "message", ...data });
193
+ });
194
+ socket.on("agent:joined", (data) => {
195
+ handleSocketMessage({ type: "agent:joined", ...data });
196
+ });
197
+ socket.on("typing", (data) => {
198
+ handleSocketMessage({ type: "typing", ...data });
199
+ });
200
+ socket.on("handoff:initiated", (data) => {
201
+ handleSocketMessage({ type: "handoff:initiated", ...data });
202
+ });
203
+ socket.on("chat:closed", (data) => {
204
+ handleSocketMessage({ type: "chat:closed", ...data });
205
+ });
206
+ socket.on("disconnect", (reason) => {
207
+ setConnectionStatus("disconnected");
208
+ console.log("[ChatWidget] Socket disconnected:", reason);
209
+ });
210
+ socket.on("connect_error", (err) => {
211
+ console.error("[ChatWidget] Socket connect error:", err);
212
+ setConnectionStatus("disconnected");
213
+ if (isOpen && currentSessionId) startPolling(currentSessionId);
214
+ });
215
+ socketRef.current = socket;
216
+ },
217
+ [projectId, visitorId, baseUrl, isOpen, handleSocketMessage, getApiConfig]
218
+ );
219
+ const startPolling = useCallback((currentSessionId) => {
220
+ if (pollingIntervalRef.current) return;
221
+ pollingIntervalRef.current = setInterval(async () => {
222
+ try {
223
+ const { apiKey } = getApiConfig();
224
+ const response = await fetch(
225
+ `${baseUrl}/api/engage/widget/messages?sessionId=${currentSessionId}`,
226
+ { headers: apiKey ? { "x-api-key": apiKey } : {} }
227
+ );
228
+ if (response.ok) {
229
+ const { data } = await response.json();
230
+ setMessages((prev) => {
231
+ const existingIds = new Set(prev.map((m) => m.id));
232
+ const newMessages = data.filter((m) => !existingIds.has(m.id));
233
+ if (newMessages.length > 0) {
234
+ return [...prev, ...newMessages.map((m) => ({
235
+ id: m.id,
236
+ role: m.role === "visitor" ? "user" : m.role,
237
+ content: m.content,
238
+ timestamp: new Date(m.created_at),
239
+ agentName: m.sender_name
240
+ }))];
241
+ }
242
+ return prev;
243
+ });
244
+ }
245
+ } catch (error) {
246
+ console.error("[ChatWidget] Polling failed:", error);
247
+ }
248
+ }, 3e3);
249
+ }, [baseUrl]);
250
+ useEffect(() => {
251
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
252
+ }, [messages, agentTyping]);
253
+ useEffect(() => {
254
+ if (isOpen && inputRef.current) {
255
+ inputRef.current.focus();
256
+ }
257
+ }, [isOpen]);
258
+ useEffect(() => {
259
+ checkAvailability();
260
+ const interval = setInterval(checkAvailability, 6e4);
261
+ return () => {
262
+ clearInterval(interval);
263
+ if (socketRef.current) {
264
+ socketRef.current.disconnect();
265
+ socketRef.current = null;
266
+ }
267
+ };
268
+ }, [checkAvailability]);
269
+ useEffect(() => {
270
+ if (isOpen) fetchWidgetConfig();
271
+ }, [isOpen, fetchWidgetConfig]);
272
+ useEffect(() => {
273
+ if (isOpen && !sessionId && availability?.mode !== "offline") {
274
+ initSession().then((id) => {
275
+ if (id && (availability?.mode === "live" || availability?.mode === "ai")) {
276
+ setConnectionStatus("connecting");
277
+ connectSocket(id);
278
+ }
279
+ });
280
+ }
281
+ return () => {
282
+ if (pollingIntervalRef.current) clearInterval(pollingIntervalRef.current);
283
+ };
284
+ }, [isOpen, sessionId, availability?.mode, initSession, connectSocket]);
285
+ const handleToggle = useCallback(() => {
286
+ setIsOpen((prev) => !prev);
287
+ if (!isOpen && messages.length === 0 && availability?.mode !== "offline") {
288
+ setMessages([{
289
+ id: "welcome",
290
+ role: "assistant",
291
+ content: welcomeMessage,
292
+ timestamp: /* @__PURE__ */ new Date()
293
+ }]);
294
+ }
295
+ }, [isOpen, messages.length, welcomeMessage, availability?.mode]);
296
+ const uploadWidgetFile = useCallback(
297
+ async (file) => {
298
+ if (!sessionId || !visitorId) return null;
299
+ const { apiKey } = getApiConfig();
300
+ const form = new FormData();
301
+ form.append("file", file);
302
+ form.append("sessionId", sessionId);
303
+ form.append("visitorId", visitorId);
304
+ const res = await fetch(`${baseUrl}/api/engage/widget/upload`, {
305
+ method: "POST",
306
+ headers: apiKey ? { "x-api-key": apiKey } : {},
307
+ body: form
308
+ });
309
+ if (!res.ok) throw new Error("Upload failed");
310
+ const json = await res.json();
311
+ const d = json?.data ?? json;
312
+ return d?.url ? { name: d.name ?? file.name, url: d.url, size: d.size, mimeType: d.mimeType } : null;
313
+ },
314
+ [sessionId, visitorId, baseUrl]
315
+ );
316
+ const handleSubmit = useCallback(
317
+ async (e) => {
318
+ e.preventDefault();
319
+ const hasText = !!inputValue.trim();
320
+ const hasFiles = pendingFiles.length > 0;
321
+ if (!hasText && !hasFiles || isLoading) return;
322
+ const content = inputValue.trim() || "";
323
+ let attachments = [];
324
+ if (pendingFiles.length) {
325
+ try {
326
+ for (const file of pendingFiles) {
327
+ const att = await uploadWidgetFile(file);
328
+ if (att) attachments.push(att);
329
+ }
330
+ setPendingFiles([]);
331
+ } catch (err) {
332
+ console.error("[ChatWidget] File upload failed", err);
333
+ setMessages((prev) => [...prev, { id: `err-${Date.now()}`, role: "assistant", content: "Failed to upload file. Please try again.", timestamp: /* @__PURE__ */ new Date() }]);
334
+ return;
335
+ }
336
+ }
337
+ const userMessage = {
338
+ id: `user-${Date.now()}`,
339
+ role: "user",
340
+ content,
341
+ timestamp: /* @__PURE__ */ new Date(),
342
+ ...attachments.length ? { attachments } : {}
343
+ };
344
+ setMessages((prev) => [...prev, userMessage]);
345
+ setInputValue("");
346
+ setIsLoading(true);
347
+ const socket = socketRef.current;
348
+ if (socket?.connected) {
349
+ socket.emit("visitor:message", { content: userMessage.content, attachments: attachments.length ? attachments : void 0 });
350
+ setLastFailedSend(null);
351
+ return;
352
+ }
353
+ setLastFailedSend({ content: userMessage.content, attachments });
354
+ setMessages((prev) => [
355
+ ...prev,
356
+ { id: `error-${Date.now()}`, role: "assistant", content: "Connection lost.", timestamp: /* @__PURE__ */ new Date(), sendFailed: true }
357
+ ]);
358
+ setIsLoading(false);
359
+ },
360
+ [inputValue, isLoading, pendingFiles, uploadWidgetFile, sessionId]
361
+ );
362
+ const retryFailedSend = useCallback(() => {
363
+ if (!lastFailedSend || !sessionId) return;
364
+ const socket = socketRef.current;
365
+ if (socket?.connected) {
366
+ socket.emit("visitor:message", { content: lastFailedSend.content, attachments: lastFailedSend.attachments?.length ? lastFailedSend.attachments : void 0 });
367
+ setLastFailedSend(null);
368
+ setMessages((prev) => prev.filter((m) => !m.sendFailed));
369
+ setIsLoading(true);
370
+ } else {
371
+ connectSocket(sessionId);
372
+ setLastFailedSend(lastFailedSend);
373
+ }
374
+ }, [lastFailedSend, sessionId, connectSocket]);
375
+ const requestHandoff = useCallback(async () => {
376
+ if (!sessionId) return;
377
+ try {
378
+ const { apiKey } = getApiConfig();
379
+ const availRes = await fetch(`${baseUrl}/api/engage/widget/availability?projectId=${projectId}`, {
380
+ headers: apiKey ? { "x-api-key": apiKey } : {}
381
+ });
382
+ const avail = availRes.ok ? (await availRes.json()).data : null;
383
+ if (avail?.agentsOnline === 0) {
384
+ setHandoffOfflinePrompt(
385
+ widgetConfig?.form_description ?? widgetConfig?.offline_message ?? "Nobody is online right now. Leave your details and we'll get back to you."
386
+ );
387
+ setShowOfflineForm(true);
388
+ setMessages((prev) => [
389
+ ...prev,
390
+ {
391
+ id: `handoff-offline-${Date.now()}`,
392
+ role: "system",
393
+ content: "We're not available at the moment. Please leave your info below and we'll follow up soon.",
394
+ timestamp: /* @__PURE__ */ new Date()
395
+ }
396
+ ]);
397
+ return;
398
+ }
399
+ await fetch(`${baseUrl}/api/engage/widget/handoff`, {
400
+ method: "POST",
401
+ headers: {
402
+ "Content-Type": "application/json",
403
+ ...apiKey && { "x-api-key": apiKey }
404
+ },
405
+ body: JSON.stringify({ sessionId })
406
+ });
407
+ setMessages((prev) => [
408
+ ...prev,
409
+ {
410
+ id: `handoff-${Date.now()}`,
411
+ role: "system",
412
+ content: "Connecting you with a team member. Please hold on!",
413
+ timestamp: /* @__PURE__ */ new Date()
414
+ }
415
+ ]);
416
+ } catch (error) {
417
+ console.error("[ChatWidget] Handoff request failed:", error);
418
+ }
419
+ }, [sessionId, baseUrl, projectId, widgetConfig]);
420
+ const handleOfflineSubmit = useCallback(async (e) => {
421
+ e.preventDefault();
422
+ if (!offlineForm.name || !offlineForm.email || !offlineForm.message) return;
423
+ setIsLoading(true);
424
+ try {
425
+ const { apiKey } = getApiConfig();
426
+ const response = await fetch(`${baseUrl}/api/engage/widget/offline-form`, {
427
+ method: "POST",
428
+ headers: {
429
+ "Content-Type": "application/json",
430
+ ...apiKey && { "x-api-key": apiKey }
431
+ },
432
+ body: JSON.stringify({
433
+ projectId,
434
+ visitorId,
435
+ ...offlineForm,
436
+ pageUrl: typeof window !== "undefined" ? window.location.href : "",
437
+ ...widgetConfig?.offlineFormSlug && { formSlug: widgetConfig.offlineFormSlug }
438
+ })
439
+ });
440
+ if (response.ok) {
441
+ setOfflineSubmitted(true);
442
+ }
443
+ } catch (error) {
444
+ console.error("[ChatWidget] Offline form submission failed:", error);
445
+ } finally {
446
+ setIsLoading(false);
447
+ }
448
+ }, [offlineForm, projectId, visitorId, baseUrl]);
449
+ const handleKeyDown = useCallback((e) => {
450
+ if (e.key === "Enter" && !e.shiftKey) {
451
+ e.preventDefault();
452
+ handleSubmit(e);
453
+ }
454
+ }, [handleSubmit]);
455
+ const handleTyping = useCallback(() => {
456
+ const socket = socketRef.current;
457
+ if (socket?.connected) {
458
+ socket.emit("visitor:typing", { isTyping: true });
459
+ setTimeout(() => {
460
+ if (socketRef.current?.connected) {
461
+ socketRef.current.emit("visitor:typing", { isTyping: false });
462
+ }
463
+ }, 2e3);
464
+ }
465
+ }, []);
466
+ const ChatButton = /* @__PURE__ */ jsx(
467
+ "button",
468
+ {
469
+ onClick: handleToggle,
470
+ "aria-label": isOpen ? "Close chat" : "Open chat",
471
+ style: {
472
+ position: "fixed",
473
+ [position === "bottom-left" ? "left" : "right"]: 20,
474
+ bottom: 20,
475
+ width: 60,
476
+ height: 60,
477
+ borderRadius: "50%",
478
+ backgroundColor: buttonColor,
479
+ border: "none",
480
+ display: "flex",
481
+ alignItems: "center",
482
+ justifyContent: "center",
483
+ cursor: "pointer",
484
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.25)",
485
+ transition: "transform 0.2s, box-shadow 0.2s",
486
+ zIndex: 9999
487
+ },
488
+ onMouseEnter: (e) => {
489
+ e.currentTarget.style.transform = "scale(1.05)";
490
+ e.currentTarget.style.boxShadow = "0 6px 16px rgba(0, 0, 0, 0.3)";
491
+ },
492
+ onMouseLeave: (e) => {
493
+ e.currentTarget.style.transform = "scale(1)";
494
+ e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.25)";
495
+ },
496
+ children: isOpen ? (
497
+ // Close icon (X)
498
+ /* @__PURE__ */ jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
499
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
500
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
501
+ ] })
502
+ ) : (
503
+ // Chat icon
504
+ /* @__PURE__ */ jsx("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })
505
+ )
506
+ }
507
+ );
508
+ const ChatPopup = isOpen && /* @__PURE__ */ jsxs(
509
+ "div",
510
+ {
511
+ style: {
512
+ position: "fixed",
513
+ [position === "bottom-left" ? "left" : "right"]: 20,
514
+ bottom: 90,
515
+ width: 380,
516
+ maxWidth: "calc(100vw - 40px)",
517
+ height: 500,
518
+ maxHeight: "calc(100vh - 120px)",
519
+ backgroundColor: "#ffffff",
520
+ borderRadius: 16,
521
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
522
+ display: "flex",
523
+ flexDirection: "column",
524
+ overflow: "hidden",
525
+ zIndex: 9998,
526
+ animation: "chatSlideUp 0.3s ease-out"
527
+ },
528
+ children: [
529
+ /* @__PURE__ */ jsxs(
530
+ "div",
531
+ {
532
+ style: {
533
+ padding: "16px 20px",
534
+ background: `linear-gradient(135deg, ${buttonColor}, ${adjustColor(buttonColor, -20)})`,
535
+ color: "white",
536
+ display: "flex",
537
+ alignItems: "center",
538
+ gap: 12
539
+ },
540
+ children: [
541
+ /* @__PURE__ */ jsx(
542
+ "div",
543
+ {
544
+ style: {
545
+ width: 40,
546
+ height: 40,
547
+ borderRadius: "50%",
548
+ backgroundColor: "rgba(255,255,255,0.2)",
549
+ display: "flex",
550
+ alignItems: "center",
551
+ justifyContent: "center"
552
+ },
553
+ children: /* @__PURE__ */ jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })
554
+ }
555
+ ),
556
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
557
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 600, fontSize: 16 }, children: showOfflineForm ? "Leave a Message" : "Chat with us" }),
558
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, opacity: 0.9, display: "flex", alignItems: "center", gap: 6 }, children: availability?.mode === "live" && availability.agentsOnline > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
559
+ /* @__PURE__ */ jsx("span", { style: { width: 8, height: 8, borderRadius: "50%", backgroundColor: "#22c55e" } }),
560
+ availability.agentsOnline,
561
+ " online"
562
+ ] }) : availability?.mode === "ai" ? "AI-powered support" : "We'll respond soon" })
563
+ ] }),
564
+ availability?.mode === "offline" && !offlineSubmitted && /* @__PURE__ */ jsx(
565
+ "button",
566
+ {
567
+ onClick: () => setShowOfflineForm((prev) => !prev),
568
+ style: {
569
+ background: "rgba(255,255,255,0.2)",
570
+ border: "none",
571
+ borderRadius: 6,
572
+ padding: "4px 8px",
573
+ color: "white",
574
+ fontSize: 12,
575
+ cursor: "pointer"
576
+ },
577
+ children: showOfflineForm ? "Try Chat" : "Leave Message"
578
+ }
579
+ )
580
+ ]
581
+ }
582
+ ),
583
+ showOfflineForm ? /* @__PURE__ */ jsx("div", { style: { padding: 20, flex: 1, overflowY: "auto" }, children: offlineSubmitted ? /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: 20 }, children: [
584
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 48, marginBottom: 16 }, children: "\u2713" }),
585
+ /* @__PURE__ */ jsx("h3", { style: { margin: "0 0 8px", fontSize: 18, fontWeight: 600 }, children: "Message Sent!" }),
586
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, color: "#666", fontSize: 14 }, children: "We'll get back to you as soon as possible." })
587
+ ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleOfflineSubmit, children: [
588
+ /* @__PURE__ */ jsx("p", { style: { margin: "0 0 16px", color: "#666", fontSize: 14 }, children: offlineFormPrompt }),
589
+ /* @__PURE__ */ jsx("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx(
590
+ "input",
591
+ {
592
+ type: "text",
593
+ placeholder: "Your name *",
594
+ value: offlineForm.name,
595
+ onChange: (e) => setOfflineForm((prev) => ({ ...prev, name: e.target.value })),
596
+ required: true,
597
+ style: {
598
+ width: "100%",
599
+ padding: "10px 12px",
600
+ borderRadius: 8,
601
+ border: "1px solid #e5e7eb",
602
+ fontSize: 14,
603
+ outline: "none",
604
+ boxSizing: "border-box"
605
+ }
606
+ }
607
+ ) }),
608
+ /* @__PURE__ */ jsx("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx(
609
+ "input",
610
+ {
611
+ type: "email",
612
+ placeholder: "Your email *",
613
+ value: offlineForm.email,
614
+ onChange: (e) => setOfflineForm((prev) => ({ ...prev, email: e.target.value })),
615
+ required: true,
616
+ style: {
617
+ width: "100%",
618
+ padding: "10px 12px",
619
+ borderRadius: 8,
620
+ border: "1px solid #e5e7eb",
621
+ fontSize: 14,
622
+ outline: "none",
623
+ boxSizing: "border-box"
624
+ }
625
+ }
626
+ ) }),
627
+ /* @__PURE__ */ jsx("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx(
628
+ "input",
629
+ {
630
+ type: "tel",
631
+ placeholder: "Phone (optional)",
632
+ value: offlineForm.phone,
633
+ onChange: (e) => setOfflineForm((prev) => ({ ...prev, phone: e.target.value })),
634
+ style: {
635
+ width: "100%",
636
+ padding: "10px 12px",
637
+ borderRadius: 8,
638
+ border: "1px solid #e5e7eb",
639
+ fontSize: 14,
640
+ outline: "none",
641
+ boxSizing: "border-box"
642
+ }
643
+ }
644
+ ) }),
645
+ /* @__PURE__ */ jsx("div", { style: { marginBottom: 16 }, children: /* @__PURE__ */ jsx(
646
+ "textarea",
647
+ {
648
+ placeholder: "How can we help? *",
649
+ value: offlineForm.message,
650
+ onChange: (e) => setOfflineForm((prev) => ({ ...prev, message: e.target.value })),
651
+ required: true,
652
+ rows: 4,
653
+ style: {
654
+ width: "100%",
655
+ padding: "10px 12px",
656
+ borderRadius: 8,
657
+ border: "1px solid #e5e7eb",
658
+ fontSize: 14,
659
+ outline: "none",
660
+ resize: "vertical",
661
+ boxSizing: "border-box"
662
+ }
663
+ }
664
+ ) }),
665
+ /* @__PURE__ */ jsx(
666
+ "button",
667
+ {
668
+ type: "submit",
669
+ disabled: isLoading,
670
+ style: {
671
+ width: "100%",
672
+ padding: "12px",
673
+ borderRadius: 8,
674
+ border: "none",
675
+ backgroundColor: buttonColor,
676
+ color: "white",
677
+ fontSize: 14,
678
+ fontWeight: 600,
679
+ cursor: isLoading ? "wait" : "pointer",
680
+ opacity: isLoading ? 0.7 : 1
681
+ },
682
+ children: isLoading ? "Sending..." : "Send Message"
683
+ }
684
+ )
685
+ ] }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
686
+ /* @__PURE__ */ jsxs(
687
+ "div",
688
+ {
689
+ style: {
690
+ flex: 1,
691
+ overflowY: "auto",
692
+ padding: 16,
693
+ display: "flex",
694
+ flexDirection: "column",
695
+ gap: 12,
696
+ backgroundColor: "#f8f9fa"
697
+ },
698
+ children: [
699
+ messages.map((message) => /* @__PURE__ */ jsx(
700
+ "div",
701
+ {
702
+ style: {
703
+ display: "flex",
704
+ justifyContent: message.role === "user" ? "flex-end" : "flex-start"
705
+ },
706
+ children: /* @__PURE__ */ jsxs(
707
+ "div",
708
+ {
709
+ style: {
710
+ maxWidth: "80%",
711
+ padding: message.role === "system" ? "8px 12px" : "10px 14px",
712
+ borderRadius: message.role === "user" ? "16px 16px 4px 16px" : message.role === "system" ? "8px" : "16px 16px 16px 4px",
713
+ backgroundColor: message.role === "user" ? buttonColor : message.role === "system" ? "#e5e7eb" : "#ffffff",
714
+ color: message.role === "user" ? "white" : message.role === "system" ? "#666" : "#1a1a1a",
715
+ boxShadow: message.role === "system" ? "none" : "0 1px 2px rgba(0,0,0,0.1)",
716
+ fontSize: message.role === "system" ? 13 : 14,
717
+ fontStyle: message.role === "system" ? "italic" : "normal",
718
+ lineHeight: 1.5,
719
+ whiteSpace: "pre-wrap",
720
+ wordBreak: "break-word"
721
+ },
722
+ children: [
723
+ message.agentName && message.role === "agent" && /* @__PURE__ */ jsx("div", { style: { fontSize: 12, opacity: 0.7, marginBottom: 4 }, children: message.agentName }),
724
+ message.content,
725
+ message.attachments?.length ? /* @__PURE__ */ jsx("div", { style: { marginTop: 8, display: "flex", flexDirection: "column", gap: 6 }, children: message.attachments.map((att, i) => att.mimeType?.startsWith("image/") ? /* @__PURE__ */ jsx("a", { href: att.url, target: "_blank", rel: "noopener noreferrer", style: { display: "block" }, children: /* @__PURE__ */ jsx("img", { src: att.url, alt: att.name, style: { maxWidth: "100%", maxHeight: 200, borderRadius: 8, objectFit: "contain" } }) }, i) : /* @__PURE__ */ jsxs("a", { href: att.url, target: "_blank", rel: "noopener noreferrer", style: { fontSize: 13, wordBreak: "break-all" }, children: [
726
+ "\u{1F4CE} ",
727
+ att.name
728
+ ] }, i)) }) : null,
729
+ message.suggestions?.length ? /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, marginTop: 8 }, children: message.suggestions.map((s, i) => /* @__PURE__ */ jsx(
730
+ "button",
731
+ {
732
+ type: "button",
733
+ onClick: () => {
734
+ setInputValue(s);
735
+ inputRef.current?.focus();
736
+ },
737
+ style: {
738
+ padding: "6px 12px",
739
+ borderRadius: 16,
740
+ border: `1px solid ${buttonColor}`,
741
+ backgroundColor: "rgba(255,255,255,0.9)",
742
+ color: buttonColor,
743
+ fontSize: 13,
744
+ cursor: "pointer"
745
+ },
746
+ children: s
747
+ },
748
+ i
749
+ )) }) : null,
750
+ message.sendFailed && lastFailedSend && /* @__PURE__ */ jsx("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsx(
751
+ "button",
752
+ {
753
+ type: "button",
754
+ onClick: retryFailedSend,
755
+ style: {
756
+ padding: "6px 12px",
757
+ borderRadius: 6,
758
+ border: "1px solid #ef4444",
759
+ backgroundColor: "#fef2f2",
760
+ color: "#dc2626",
761
+ fontSize: 13,
762
+ cursor: "pointer"
763
+ },
764
+ children: "Retry send"
765
+ }
766
+ ) }),
767
+ message.content.includes("speak with") && /* @__PURE__ */ jsx(
768
+ "button",
769
+ {
770
+ onClick: requestHandoff,
771
+ style: {
772
+ display: "block",
773
+ marginTop: 8,
774
+ padding: "6px 12px",
775
+ borderRadius: 6,
776
+ border: `1px solid ${buttonColor}`,
777
+ backgroundColor: "transparent",
778
+ color: buttonColor,
779
+ fontSize: 13,
780
+ cursor: "pointer"
781
+ },
782
+ children: "Talk to a person"
783
+ }
784
+ )
785
+ ]
786
+ }
787
+ )
788
+ },
789
+ message.id
790
+ )),
791
+ (isLoading || agentTyping) && /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ jsxs(
792
+ "div",
793
+ {
794
+ style: {
795
+ padding: "10px 14px",
796
+ borderRadius: "16px 16px 16px 4px",
797
+ backgroundColor: "#ffffff",
798
+ boxShadow: "0 1px 2px rgba(0,0,0,0.1)",
799
+ display: "flex",
800
+ gap: 4
801
+ },
802
+ children: [
803
+ /* @__PURE__ */ jsx("span", { style: { animation: "chatDot 1.4s infinite ease-in-out", animationDelay: "0s" }, children: "\u25CF" }),
804
+ /* @__PURE__ */ jsx("span", { style: { animation: "chatDot 1.4s infinite ease-in-out", animationDelay: "0.2s" }, children: "\u25CF" }),
805
+ /* @__PURE__ */ jsx("span", { style: { animation: "chatDot 1.4s infinite ease-in-out", animationDelay: "0.4s" }, children: "\u25CF" })
806
+ ]
807
+ }
808
+ ) }),
809
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
810
+ ]
811
+ }
812
+ ),
813
+ lastFailedSend && /* @__PURE__ */ jsxs("div", { style: { padding: "8px 12px", backgroundColor: "#fef2f2", borderTop: "1px solid #fecaca", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }, children: [
814
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 13, color: "#dc2626" }, children: "Failed to send" }),
815
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: retryFailedSend, style: { padding: "4px 10px", borderRadius: 6, border: "1px solid #dc2626", background: "#fff", color: "#dc2626", fontSize: 12, cursor: "pointer" }, children: "Retry" })
816
+ ] }),
817
+ /* @__PURE__ */ jsx("form", { onSubmit: handleSubmit, style: { padding: 12, borderTop: "1px solid #e5e7eb", backgroundColor: "#ffffff" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
818
+ /* @__PURE__ */ jsx(
819
+ "input",
820
+ {
821
+ ref: inputRef,
822
+ type: "text",
823
+ value: inputValue,
824
+ onChange: (e) => {
825
+ setInputValue(e.target.value);
826
+ handleTyping();
827
+ },
828
+ onKeyDown: handleKeyDown,
829
+ placeholder: "Type a message...",
830
+ disabled: isLoading,
831
+ style: {
832
+ flex: 1,
833
+ padding: "10px 14px",
834
+ borderRadius: 24,
835
+ border: "1px solid #e5e7eb",
836
+ fontSize: 14,
837
+ outline: "none",
838
+ transition: "border-color 0.2s"
839
+ },
840
+ onFocus: (e) => e.currentTarget.style.borderColor = buttonColor,
841
+ onBlur: (e) => e.currentTarget.style.borderColor = "#e5e7eb"
842
+ }
843
+ ),
844
+ /* @__PURE__ */ jsx(
845
+ "button",
846
+ {
847
+ type: "submit",
848
+ disabled: !inputValue.trim() || isLoading,
849
+ style: {
850
+ width: 40,
851
+ height: 40,
852
+ borderRadius: "50%",
853
+ border: "none",
854
+ backgroundColor: (inputValue.trim() || pendingFiles.length) && !isLoading ? buttonColor : "#e5e7eb",
855
+ color: "white",
856
+ cursor: inputValue.trim() && !isLoading ? "pointer" : "not-allowed",
857
+ display: "flex",
858
+ alignItems: "center",
859
+ justifyContent: "center",
860
+ transition: "background-color 0.2s"
861
+ },
862
+ children: /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
863
+ }
864
+ )
865
+ ] }) })
866
+ ] }),
867
+ /* @__PURE__ */ jsx("style", { children: `
868
+ @keyframes chatSlideUp {
869
+ from {
870
+ opacity: 0;
871
+ transform: translateY(20px);
872
+ }
873
+ to {
874
+ opacity: 1;
875
+ transform: translateY(0);
876
+ }
877
+ }
878
+ @keyframes chatDot {
879
+ 0%, 80%, 100% {
880
+ opacity: 0.3;
881
+ }
882
+ 40% {
883
+ opacity: 1;
884
+ }
885
+ }
886
+ ` })
887
+ ]
888
+ }
889
+ );
890
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
891
+ ChatPopup,
892
+ ChatButton
893
+ ] });
894
+ }
895
+ function adjustColor(hex, amount) {
896
+ const num = parseInt(hex.replace("#", ""), 16);
897
+ const r = Math.min(255, Math.max(0, (num >> 16) + amount));
898
+ const g = Math.min(255, Math.max(0, (num >> 8 & 255) + amount));
899
+ const b = Math.min(255, Math.max(0, (num & 255) + amount));
900
+ return `#${(1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1)}`;
901
+ }
902
+ function handleAction(action, node, onClose, onAction) {
903
+ if (!action?.action || action.action === "none") return;
904
+ if (onAction) {
905
+ onAction(action, node);
906
+ }
907
+ switch (action.action) {
908
+ case "link":
909
+ if (action.url) {
910
+ if (action.newTab) {
911
+ window.open(action.url, "_blank", "noopener,noreferrer");
912
+ } else {
913
+ window.location.href = action.url;
914
+ }
915
+ }
916
+ break;
917
+ case "scroll":
918
+ if (action.target) {
919
+ const element = document.querySelector(action.target);
920
+ element?.scrollIntoView({ behavior: "smooth" });
921
+ }
922
+ break;
923
+ case "close":
924
+ onClose?.();
925
+ break;
926
+ case "copy":
927
+ if (action.text) {
928
+ navigator.clipboard.writeText(action.text).catch(console.error);
929
+ }
930
+ break;
931
+ case "share":
932
+ if (navigator.share) {
933
+ navigator.share({
934
+ title: document.title,
935
+ url: window.location.href
936
+ }).catch(console.error);
937
+ }
938
+ break;
939
+ case "download":
940
+ if (action.url) {
941
+ const a = document.createElement("a");
942
+ a.href = action.url;
943
+ a.download = "";
944
+ a.click();
945
+ }
946
+ break;
947
+ }
948
+ }
949
+ var animationKeyframes = `
950
+ @keyframes engageFadeIn { from { opacity: 0; } to { opacity: 1; } }
951
+ @keyframes engageFadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
952
+ @keyframes engageFadeInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
953
+ @keyframes engageSlideInLeft { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } }
954
+ @keyframes engageSlideInRight { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
955
+ @keyframes engageSlideInUp { from { opacity: 0; transform: translateY(100%); } to { opacity: 1; transform: translateY(0); } }
956
+ @keyframes engageScaleIn { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }
957
+ @keyframes engageBounceIn { 0% { opacity: 0; transform: scale(0.3); } 50% { transform: scale(1.05); } 70% { transform: scale(0.9); } 100% { opacity: 1; transform: scale(1); } }
958
+ `;
959
+ function getAnimationStyle(animation, delay, duration) {
960
+ if (!animation || animation === "none") return {};
961
+ const animationMap = {
962
+ fadeIn: "engageFadeIn",
963
+ fadeInUp: "engageFadeInUp",
964
+ fadeInDown: "engageFadeInDown",
965
+ slideInLeft: "engageSlideInLeft",
966
+ slideInRight: "engageSlideInRight",
967
+ slideInUp: "engageSlideInUp",
968
+ scaleIn: "engageScaleIn",
969
+ bounceIn: "engageBounceIn"
970
+ };
971
+ const animationName = animationMap[animation];
972
+ if (!animationName) return {};
973
+ return {
974
+ animation: `${animationName} ${duration || 300}ms ease-out ${delay || 0}ms forwards`,
975
+ opacity: 0
976
+ // Start invisible, animation will show it
977
+ };
978
+ }
979
+ function NodeRenderer({ node, onClose, onAction, context }) {
980
+ const [isHovered, setIsHovered] = useState(false);
981
+ const { type, props = {}, style = {}, children } = node;
982
+ const computedStyle = { ...style };
983
+ if (props.animation) {
984
+ Object.assign(computedStyle, getAnimationStyle(
985
+ props.animation,
986
+ props.animationDelay,
987
+ props.animationDuration
988
+ ));
989
+ }
990
+ if (isHovered) {
991
+ if (props.hoverScale && props.hoverScale !== "none") {
992
+ computedStyle.transform = `scale(${props.hoverScale})`;
993
+ computedStyle.transition = "transform 0.2s ease";
994
+ }
995
+ if (props.hoverShadow && props.hoverShadow !== "none") {
996
+ const shadowMap = {
997
+ sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
998
+ md: "0 4px 6px -1px rgb(0 0 0 / 0.1)",
999
+ lg: "0 10px 15px -3px rgb(0 0 0 / 0.1)"
1000
+ };
1001
+ computedStyle.boxShadow = shadowMap[props.hoverShadow];
1002
+ }
1003
+ }
1004
+ const eventHandlers = {
1005
+ onMouseEnter: () => setIsHovered(true),
1006
+ onMouseLeave: () => setIsHovered(false)
1007
+ };
1008
+ if (props.onClick) {
1009
+ eventHandlers.onClick = (e) => {
1010
+ e.preventDefault();
1011
+ handleAction(props.onClick, node, onClose, onAction);
1012
+ };
1013
+ computedStyle.cursor = "pointer";
1014
+ }
1015
+ const renderedChildren = children?.map((child) => /* @__PURE__ */ jsx(
1016
+ NodeRenderer,
1017
+ {
1018
+ node: child,
1019
+ onClose,
1020
+ onAction,
1021
+ context
1022
+ },
1023
+ child.id
1024
+ ));
1025
+ switch (type) {
1026
+ case "Container":
1027
+ case "Box":
1028
+ case "Section":
1029
+ return /* @__PURE__ */ jsx("div", { style: computedStyle, ...eventHandlers, children: renderedChildren });
1030
+ case "Text":
1031
+ return /* @__PURE__ */ jsx("p", { style: computedStyle, ...eventHandlers, children: resolveText(props.text, context) });
1032
+ case "Heading":
1033
+ const HeadingTag = `h${props.level || 2}`;
1034
+ return createElement(
1035
+ HeadingTag,
1036
+ { style: computedStyle, ...eventHandlers },
1037
+ resolveText(props.text, context)
1038
+ );
1039
+ case "Button":
1040
+ case "BookingButton":
1041
+ case "BuyNow":
1042
+ case "AddToCart":
1043
+ case "EventRSVP":
1044
+ return /* @__PURE__ */ jsx(
1045
+ "button",
1046
+ {
1047
+ style: computedStyle,
1048
+ ...eventHandlers,
1049
+ type: "button",
1050
+ children: resolveText(props.text || props.label, context) || "Button"
1051
+ }
1052
+ );
1053
+ case "Image":
1054
+ return /* @__PURE__ */ jsx(
1055
+ "img",
1056
+ {
1057
+ src: props.src,
1058
+ alt: props.alt || "",
1059
+ style: computedStyle,
1060
+ ...eventHandlers
1061
+ }
1062
+ );
1063
+ case "Link":
1064
+ return /* @__PURE__ */ jsx(
1065
+ "a",
1066
+ {
1067
+ href: props.href || "#",
1068
+ target: props.newTab ? "_blank" : void 0,
1069
+ rel: props.newTab ? "noopener noreferrer" : void 0,
1070
+ style: computedStyle,
1071
+ ...eventHandlers,
1072
+ children: resolveText(props.text, context) || renderedChildren
1073
+ }
1074
+ );
1075
+ case "Input":
1076
+ return /* @__PURE__ */ jsx(
1077
+ "input",
1078
+ {
1079
+ type: props.inputType || "text",
1080
+ placeholder: props.placeholder,
1081
+ style: computedStyle,
1082
+ ...eventHandlers
1083
+ }
1084
+ );
1085
+ case "Divider":
1086
+ return /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: "1px solid #e5e7eb", ...computedStyle } });
1087
+ case "Spacer":
1088
+ return /* @__PURE__ */ jsx("div", { style: { ...computedStyle, width: props.width, height: props.height } });
1089
+ case "Icon":
1090
+ return /* @__PURE__ */ jsx("span", { style: computedStyle, ...eventHandlers, children: props.icon || "\u2605" });
1091
+ case "FormEmbed":
1092
+ return /* @__PURE__ */ jsx(
1093
+ "div",
1094
+ {
1095
+ style: computedStyle,
1096
+ "data-engage-form": props.form_id,
1097
+ ...eventHandlers,
1098
+ children: /* @__PURE__ */ jsxs("div", { style: { padding: "16px", background: "#f5f5f5", borderRadius: "8px", textAlign: "center" }, children: [
1099
+ "Form: ",
1100
+ props.form_id || "Not configured"
1101
+ ] })
1102
+ }
1103
+ );
1104
+ case "ProductCard":
1105
+ case "EventCard":
1106
+ return /* @__PURE__ */ jsx(
1107
+ "div",
1108
+ {
1109
+ style: computedStyle,
1110
+ "data-engage-offering": props.offering_id,
1111
+ ...eventHandlers,
1112
+ children: renderedChildren
1113
+ }
1114
+ );
1115
+ default:
1116
+ console.warn(`[DesignRenderer] Unknown node type: ${type}`);
1117
+ return /* @__PURE__ */ jsx("div", { style: computedStyle, ...eventHandlers, children: renderedChildren });
1118
+ }
1119
+ }
1120
+ function resolveText(text, context) {
1121
+ if (!text) return "";
1122
+ if (!context) return text;
1123
+ return text.replace(/\{\{(\w+(?:\.\w+)*)\}\}/g, (match, path) => {
1124
+ const value = getNestedValue(context, path);
1125
+ return value !== void 0 ? String(value) : match;
1126
+ });
1127
+ }
1128
+ function getNestedValue(obj, path) {
1129
+ return path.split(".").reduce((acc, key) => acc?.[key], obj);
1130
+ }
1131
+ function DesignRenderer({
1132
+ design,
1133
+ onClose,
1134
+ onAction,
1135
+ context
1136
+ }) {
1137
+ React2.useEffect(() => {
1138
+ if (typeof document === "undefined") return;
1139
+ const styleId = "engage-design-renderer-animations";
1140
+ if (!document.getElementById(styleId)) {
1141
+ const styleSheet = document.createElement("style");
1142
+ styleSheet.id = styleId;
1143
+ styleSheet.textContent = animationKeyframes;
1144
+ document.head.appendChild(styleSheet);
1145
+ }
1146
+ }, []);
1147
+ const rootStyle = {
1148
+ ...design.style
1149
+ };
1150
+ return /* @__PURE__ */ jsx("div", { style: rootStyle, "data-engage-design": design.id, children: design.children?.map((node) => /* @__PURE__ */ jsx(
1151
+ NodeRenderer,
1152
+ {
1153
+ node,
1154
+ onClose,
1155
+ onAction,
1156
+ context
1157
+ },
1158
+ node.id
1159
+ )) });
1160
+ }
1161
+ function getApiConfig2() {
1162
+ const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
1163
+ const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
1164
+ return { apiUrl, apiKey };
1165
+ }
1166
+ function EngageWidget({
1167
+ apiUrl: propApiUrl,
1168
+ apiKey: propApiKey,
1169
+ position = "bottom-right",
1170
+ zIndex = 9999,
1171
+ chatEnabled = true,
1172
+ debug = false
1173
+ }) {
1174
+ const pathname = usePathname();
1175
+ const [elements, setElements] = useState([]);
1176
+ const [activeElements, setActiveElements] = useState([]);
1177
+ const [dismissedElements, setDismissedElements] = useState(/* @__PURE__ */ new Set());
1178
+ useEffect(() => {
1179
+ async function loadElements() {
1180
+ const { apiUrl: globalApiUrl, apiKey: globalApiKey } = getApiConfig2();
1181
+ const apiUrl = propApiUrl || globalApiUrl;
1182
+ const apiKey = propApiKey || globalApiKey;
1183
+ if (!apiKey) {
1184
+ if (debug) console.warn("[Engage] No API key configured");
1185
+ return;
1186
+ }
1187
+ try {
1188
+ const response = await fetch(`${apiUrl}/api/public/engage/elements`, {
1189
+ method: "POST",
1190
+ headers: {
1191
+ "Content-Type": "application/json",
1192
+ "x-api-key": apiKey
1193
+ },
1194
+ body: JSON.stringify({})
1195
+ });
1196
+ if (!response.ok) {
1197
+ if (debug) console.error("[Engage] Error loading elements:", response.statusText);
1198
+ return;
1199
+ }
1200
+ const data = await response.json();
1201
+ if (debug) console.log("[Engage] Loaded elements:", data.elements);
1202
+ setElements(data.elements || []);
1203
+ } catch (error) {
1204
+ if (debug) console.error("[Engage] Error loading elements:", error);
1205
+ }
1206
+ }
1207
+ loadElements();
1208
+ }, [propApiUrl, propApiKey, debug]);
1209
+ useEffect(() => {
1210
+ if (!elements.length) return;
1211
+ const checkElement = (element) => {
1212
+ if (dismissedElements.has(element.id)) return false;
1213
+ if (element.targeting?.pages) {
1214
+ const { include, exclude } = element.targeting.pages;
1215
+ if (exclude?.some((p) => matchPath(pathname, p))) return false;
1216
+ if (include && !include.some((p) => matchPath(pathname, p))) return false;
1217
+ }
1218
+ if (element.targeting?.devices) {
1219
+ const device = getDeviceType();
1220
+ if (!element.targeting.devices.includes(device)) return false;
1221
+ }
1222
+ if (element.trigger?.frequency) {
1223
+ const { type, days } = element.trigger.frequency;
1224
+ const key = `_engage_${element.id}`;
1225
+ if (type === "once") {
1226
+ if (localStorage.getItem(key)) return false;
1227
+ } else if (type === "once-per-session") {
1228
+ if (sessionStorage.getItem(key)) return false;
1229
+ } else if (type === "every-n-days" && days) {
1230
+ const lastShown = localStorage.getItem(key);
1231
+ if (lastShown) {
1232
+ const elapsed = Date.now() - parseInt(lastShown, 10);
1233
+ if (elapsed < days * 24 * 60 * 60 * 1e3) return false;
1234
+ }
1235
+ }
1236
+ }
1237
+ return true;
1238
+ };
1239
+ const eligible = elements.filter(checkElement);
1240
+ if (debug) console.log("[Engage] Eligible elements:", eligible);
1241
+ eligible.forEach((element) => {
1242
+ const trigger = element.trigger;
1243
+ if (trigger?.type === "immediate" || !trigger?.type) {
1244
+ setActiveElements((prev) => [...prev, element.id]);
1245
+ } else if (trigger?.type === "delay" && trigger.delay) {
1246
+ setTimeout(() => {
1247
+ setActiveElements((prev) => [...prev, element.id]);
1248
+ }, trigger.delay * 1e3);
1249
+ } else if (trigger?.type === "exit-intent") {
1250
+ const handleMouseLeave = (e) => {
1251
+ if (e.clientY < 10) {
1252
+ setActiveElements((prev) => [...prev, element.id]);
1253
+ document.removeEventListener("mouseleave", handleMouseLeave);
1254
+ }
1255
+ };
1256
+ document.addEventListener("mouseleave", handleMouseLeave);
1257
+ } else if (trigger?.type === "scroll" && trigger.scrollPercentage) {
1258
+ const handleScroll = () => {
1259
+ const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight) * 100;
1260
+ if (scrollPercent >= (trigger.scrollPercentage || 50)) {
1261
+ setActiveElements((prev) => [...prev, element.id]);
1262
+ window.removeEventListener("scroll", handleScroll);
1263
+ }
1264
+ };
1265
+ window.addEventListener("scroll", handleScroll);
1266
+ }
1267
+ });
1268
+ }, [elements, pathname, dismissedElements, debug]);
1269
+ const handleDismiss = useCallback((elementId) => {
1270
+ setDismissedElements((prev) => /* @__PURE__ */ new Set([...prev, elementId]));
1271
+ setActiveElements((prev) => prev.filter((id) => id !== elementId));
1272
+ const element = elements.find((e) => e.id === elementId);
1273
+ if (element?.trigger?.frequency) {
1274
+ const key = `_engage_${elementId}`;
1275
+ if (element.trigger.frequency.type === "once-per-session") {
1276
+ sessionStorage.setItem(key, "true");
1277
+ } else {
1278
+ localStorage.setItem(key, Date.now().toString());
1279
+ }
1280
+ }
1281
+ }, [elements]);
1282
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1283
+ activeElements.map((elementId) => {
1284
+ const element = elements.find((e) => e.id === elementId);
1285
+ if (!element) return null;
1286
+ return /* @__PURE__ */ jsx(
1287
+ EngageElementRenderer,
1288
+ {
1289
+ element,
1290
+ onDismiss: () => handleDismiss(element.id),
1291
+ zIndex
1292
+ },
1293
+ element.id
1294
+ );
1295
+ }),
1296
+ chatEnabled && /* @__PURE__ */ jsx(
1297
+ ChatWidget,
1298
+ {
1299
+ projectId: propApiKey || "",
1300
+ config: {
1301
+ position,
1302
+ buttonColor: "#00afab"
1303
+ // Default teal, can be customized later
1304
+ }
1305
+ }
1306
+ )
1307
+ ] });
1308
+ }
1309
+ function EngageElementRenderer({
1310
+ element,
1311
+ onDismiss,
1312
+ zIndex
1313
+ }) {
1314
+ if (element.design_json) {
1315
+ if (element.type === "popup") {
1316
+ return /* @__PURE__ */ jsx(
1317
+ "div",
1318
+ {
1319
+ style: {
1320
+ position: "fixed",
1321
+ top: 0,
1322
+ left: 0,
1323
+ right: 0,
1324
+ bottom: 0,
1325
+ backgroundColor: "rgba(0,0,0,0.5)",
1326
+ display: "flex",
1327
+ alignItems: "center",
1328
+ justifyContent: "center",
1329
+ zIndex
1330
+ },
1331
+ onClick: onDismiss,
1332
+ children: /* @__PURE__ */ jsx("div", { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
1333
+ DesignRenderer,
1334
+ {
1335
+ design: element.design_json,
1336
+ onClose: onDismiss,
1337
+ onAction: (action, node) => {
1338
+ console.log("[Engage] Action:", action, node);
1339
+ }
1340
+ }
1341
+ ) })
1342
+ }
1343
+ );
1344
+ }
1345
+ if (element.type === "bar") {
1346
+ return /* @__PURE__ */ jsx(
1347
+ "div",
1348
+ {
1349
+ style: {
1350
+ position: "fixed",
1351
+ top: 0,
1352
+ left: 0,
1353
+ right: 0,
1354
+ zIndex
1355
+ },
1356
+ children: /* @__PURE__ */ jsx(
1357
+ DesignRenderer,
1358
+ {
1359
+ design: element.design_json,
1360
+ onClose: onDismiss,
1361
+ onAction: (action, node) => {
1362
+ console.log("[Engage] Action:", action, node);
1363
+ }
1364
+ }
1365
+ )
1366
+ }
1367
+ );
1368
+ }
1369
+ if (element.type === "nudge" || element.type === "slide-in") {
1370
+ const position = element.config?.position || "bottom-right";
1371
+ const positionStyles = {
1372
+ "bottom-right": { bottom: 20, right: 20 },
1373
+ "bottom-left": { bottom: 20, left: 20 },
1374
+ "top-right": { top: 20, right: 20 },
1375
+ "top-left": { top: 20, left: 20 }
1376
+ };
1377
+ return /* @__PURE__ */ jsx(
1378
+ "div",
1379
+ {
1380
+ style: {
1381
+ position: "fixed",
1382
+ zIndex,
1383
+ ...positionStyles[position]
1384
+ },
1385
+ children: /* @__PURE__ */ jsx(
1386
+ DesignRenderer,
1387
+ {
1388
+ design: element.design_json,
1389
+ onClose: onDismiss,
1390
+ onAction: (action, node) => {
1391
+ console.log("[Engage] Action:", action, node);
1392
+ }
1393
+ }
1394
+ )
1395
+ }
1396
+ );
1397
+ }
1398
+ return /* @__PURE__ */ jsx(
1399
+ DesignRenderer,
1400
+ {
1401
+ design: element.design_json,
1402
+ onClose: onDismiss,
1403
+ onAction: (action, node) => {
1404
+ console.log("[Engage] Action:", action, node);
1405
+ }
1406
+ }
1407
+ );
1408
+ }
1409
+ if (element.type === "popup") {
1410
+ return /* @__PURE__ */ jsx(
1411
+ "div",
1412
+ {
1413
+ style: {
1414
+ position: "fixed",
1415
+ top: 0,
1416
+ left: 0,
1417
+ right: 0,
1418
+ bottom: 0,
1419
+ backgroundColor: "rgba(0,0,0,0.5)",
1420
+ display: "flex",
1421
+ alignItems: "center",
1422
+ justifyContent: "center",
1423
+ zIndex
1424
+ },
1425
+ onClick: onDismiss,
1426
+ children: /* @__PURE__ */ jsxs(
1427
+ "div",
1428
+ {
1429
+ style: {
1430
+ backgroundColor: "white",
1431
+ padding: 24,
1432
+ borderRadius: 8,
1433
+ maxWidth: 500,
1434
+ width: "90%"
1435
+ },
1436
+ onClick: (e) => e.stopPropagation(),
1437
+ children: [
1438
+ /* @__PURE__ */ jsx(
1439
+ "button",
1440
+ {
1441
+ onClick: onDismiss,
1442
+ style: {
1443
+ position: "absolute",
1444
+ top: 8,
1445
+ right: 8,
1446
+ background: "none",
1447
+ border: "none",
1448
+ fontSize: 24,
1449
+ cursor: "pointer"
1450
+ },
1451
+ children: "\xD7"
1452
+ }
1453
+ ),
1454
+ element.config?.title && /* @__PURE__ */ jsx("h2", { children: element.config.title }),
1455
+ element.config?.message && /* @__PURE__ */ jsx("p", { children: element.config.message })
1456
+ ]
1457
+ }
1458
+ )
1459
+ }
1460
+ );
1461
+ }
1462
+ if (element.type === "bar") {
1463
+ return /* @__PURE__ */ jsxs(
1464
+ "div",
1465
+ {
1466
+ style: {
1467
+ position: "fixed",
1468
+ top: 0,
1469
+ left: 0,
1470
+ right: 0,
1471
+ backgroundColor: element.config?.backgroundColor || "#3b82f6",
1472
+ color: element.config?.textColor || "white",
1473
+ padding: "12px 24px",
1474
+ display: "flex",
1475
+ alignItems: "center",
1476
+ justifyContent: "center",
1477
+ gap: 16,
1478
+ zIndex
1479
+ },
1480
+ children: [
1481
+ /* @__PURE__ */ jsx("span", { children: element.config?.message }),
1482
+ /* @__PURE__ */ jsx(
1483
+ "button",
1484
+ {
1485
+ onClick: onDismiss,
1486
+ style: {
1487
+ background: "none",
1488
+ border: "none",
1489
+ color: "inherit",
1490
+ fontSize: 20,
1491
+ cursor: "pointer"
1492
+ },
1493
+ children: "\xD7"
1494
+ }
1495
+ )
1496
+ ]
1497
+ }
1498
+ );
1499
+ }
1500
+ return null;
1501
+ }
1502
+ function matchPath(pathname, pattern) {
1503
+ if (pattern.endsWith("*")) {
1504
+ return pathname.startsWith(pattern.slice(0, -1));
1505
+ }
1506
+ return pathname === pattern;
1507
+ }
1508
+ function getDeviceType() {
1509
+ if (typeof window === "undefined") return "desktop";
1510
+ const ua = navigator.userAgent;
1511
+ if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
1512
+ if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
1513
+ return "desktop";
1514
+ }
1515
+
1516
+ export { ChatWidget, DesignRenderer, EngageWidget };
1517
+ //# sourceMappingURL=chunk-DYM5ML2V.mjs.map
1518
+ //# sourceMappingURL=chunk-DYM5ML2V.mjs.map