@uptrademedia/site-kit 1.0.1 → 1.0.4
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/SetupWizard-Cki06kB0.d.mts +12 -0
- package/dist/SetupWizard-Cki06kB0.d.ts +12 -0
- package/dist/analytics/index.d.mts +87 -0
- package/dist/analytics/index.d.ts +87 -0
- package/dist/blog/index.d.mts +24 -0
- package/dist/blog/index.d.ts +24 -0
- package/dist/blog/index.js.map +1 -1
- package/dist/blog/index.mjs.map +1 -1
- package/dist/chunk-2IHTEKHU.mjs +332 -0
- package/dist/chunk-2IHTEKHU.mjs.map +1 -0
- package/dist/chunk-5R4R3WDP.js +1451 -0
- package/dist/chunk-5R4R3WDP.js.map +1 -0
- package/dist/{chunk-RV7H3I6J.js → chunk-ADHVEFWD.js} +68 -2
- package/dist/chunk-ADHVEFWD.js.map +1 -0
- package/dist/chunk-BGJLOJ7T.mjs +605 -0
- package/dist/chunk-BGJLOJ7T.mjs.map +1 -0
- package/dist/chunk-BZBJVG5Y.js +88 -0
- package/dist/chunk-BZBJVG5Y.js.map +1 -0
- package/dist/{chunk-COI6GOX2.mjs → chunk-DOHML47I.mjs} +3 -3
- package/dist/chunk-DOHML47I.mjs.map +1 -0
- package/dist/chunk-DRFTRTKV.js +809 -0
- package/dist/chunk-DRFTRTKV.js.map +1 -0
- package/dist/chunk-EL5QTAA3.mjs +805 -0
- package/dist/chunk-EL5QTAA3.mjs.map +1 -0
- package/dist/chunk-GAJLEDRD.js +334 -0
- package/dist/chunk-GAJLEDRD.js.map +1 -0
- package/dist/{chunk-3MUOUXHV.js → chunk-K2HWVOEO.js} +3 -3
- package/dist/chunk-K2HWVOEO.js.map +1 -0
- package/dist/chunk-O2OHHBUD.js +997 -0
- package/dist/chunk-O2OHHBUD.js.map +1 -0
- package/dist/chunk-QAYJV4KK.js +608 -0
- package/dist/chunk-QAYJV4KK.js.map +1 -0
- package/dist/{chunk-FEBYQGY4 2.mjs → chunk-SMUFNQLM.mjs} +67 -3
- package/dist/chunk-SMUFNQLM.mjs.map +1 -0
- package/dist/chunk-VDMZZL2O.mjs +83 -0
- package/dist/chunk-VDMZZL2O.mjs.map +1 -0
- package/dist/chunk-XFRPT5ZX.mjs +1449 -0
- package/dist/chunk-XFRPT5ZX.mjs.map +1 -0
- package/dist/chunk-XQJX252G.mjs +981 -0
- package/dist/chunk-XQJX252G.mjs.map +1 -0
- package/dist/commerce/index.d.mts +168 -0
- package/dist/commerce/index.d.ts +168 -0
- package/dist/commerce/index.js +38 -38
- package/dist/commerce/index.mjs +1 -1
- package/dist/commerce/server.d.mts +98 -0
- package/dist/commerce/server.d.ts +98 -0
- package/dist/engage/index.d.mts +27 -0
- package/dist/engage/index.d.ts +27 -0
- package/dist/engage/index.js +7 -40
- package/dist/engage/index.js.map +1 -1
- package/dist/engage/index.mjs +1 -41
- package/dist/engage/index.mjs.map +1 -1
- package/dist/forms/index.d.mts +437 -0
- package/dist/forms/index.d.ts +437 -0
- package/dist/forms/index.js +13 -5
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/index.mjs +2 -2
- package/dist/forms/index.mjs.map +1 -1
- package/dist/{generators-7Y5ABRYV 2.mjs → generators-TO2FKJR6.mjs} +134 -3
- package/dist/generators-TO2FKJR6.mjs.map +1 -0
- package/dist/{generators-GWIYCA5M.js → generators-YZWIGHCO.js} +135 -2
- package/dist/generators-YZWIGHCO.js.map +1 -0
- package/dist/images/index.d.mts +133 -0
- package/dist/images/index.d.ts +133 -0
- package/dist/images/index.js +41 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/index.mjs +8 -0
- package/dist/images/index.mjs.map +1 -0
- package/dist/index.d.mts +649 -0
- package/dist/index.d.ts +649 -0
- package/dist/index.js +1355 -104
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1242 -76
- package/dist/index.mjs.map +1 -1
- package/dist/redirects/index.d.mts +72 -0
- package/dist/redirects/index.d.ts +72 -0
- package/dist/redirects/index.js +25 -0
- package/dist/redirects/index.js.map +1 -0
- package/dist/redirects/index.mjs +4 -0
- package/dist/redirects/index.mjs.map +1 -0
- package/dist/routing-BWjUF7lp.d.ts +105 -0
- package/dist/routing-CgmRi9tD.d.mts +105 -0
- package/dist/{scanner-MF7P3CDE 2.mjs → scanner-AZV5I6US.mjs} +123 -4
- package/dist/scanner-AZV5I6US.mjs.map +1 -0
- package/dist/{scanner-NT6YG4TD 2.js → scanner-ETJAMIT7.js} +124 -3
- package/dist/scanner-ETJAMIT7.js.map +1 -0
- package/dist/seo/index.d.mts +273 -0
- package/dist/seo/index.d.ts +273 -0
- package/dist/seo/server.d.mts +89 -0
- package/dist/seo/server.d.ts +89 -0
- package/dist/setup/client.d.mts +60 -0
- package/dist/setup/client.d.ts +60 -0
- package/dist/setup/client.js +30 -0
- package/dist/setup/client.js.map +1 -0
- package/dist/setup/client.mjs +5 -0
- package/dist/setup/client.mjs.map +1 -0
- package/dist/setup/index.d.mts +5 -0
- package/dist/setup/index.d.ts +5 -0
- package/dist/setup/index.js +28 -1043
- package/dist/setup/index.js.map +1 -1
- package/dist/setup/index.mjs +3 -1043
- package/dist/setup/index.mjs.map +1 -1
- package/dist/setup/server.d.mts +14 -0
- package/dist/setup/server.d.ts +14 -0
- package/dist/setup/server.js +13 -0
- package/dist/setup/server.js.map +1 -0
- package/dist/setup/server.mjs +4 -0
- package/dist/setup/server.mjs.map +1 -0
- package/dist/sitemap/index.d.mts +78 -0
- package/dist/sitemap/index.d.ts +78 -0
- package/dist/types-BDojCvvL.d.mts +156 -0
- package/dist/types-BDojCvvL.d.ts +156 -0
- package/dist/types-BmzutFwy.d.mts +227 -0
- package/dist/types-BmzutFwy.d.ts +227 -0
- package/dist/types-C0pJGfbH.d.mts +155 -0
- package/dist/types-C0pJGfbH.d.ts +155 -0
- package/dist/types-DA_Kocle.d.mts +127 -0
- package/dist/types-DA_Kocle.d.ts +127 -0
- package/dist/types-lFLKKn0G.d.mts +163 -0
- package/dist/types-lFLKKn0G.d.ts +163 -0
- package/dist/types-nB206tPK.d.mts +309 -0
- package/dist/types-nB206tPK.d.ts +309 -0
- package/dist/useEventModal-6U1pF3_g.d.mts +209 -0
- package/dist/useEventModal-BA8g-1-P.d.ts +209 -0
- package/package.json +21 -1
- package/dist/chunk-3MUOUXHV.js.map +0 -1
- package/dist/chunk-4HVYXYQL 2.mjs +0 -255
- package/dist/chunk-4HVYXYQL.mjs +0 -255
- package/dist/chunk-4HVYXYQL.mjs.map +0 -1
- package/dist/chunk-COI6GOX2.mjs.map +0 -1
- package/dist/chunk-EQCVQC35.js 2.map +0 -1
- package/dist/chunk-FEBYQGY4.mjs +0 -251
- package/dist/chunk-FEBYQGY4.mjs.map +0 -1
- package/dist/chunk-NYKRE2FL 2.mjs +0 -31
- package/dist/chunk-NYKRE2FL.mjs 2.map +0 -1
- package/dist/chunk-RV7H3I6J.js 2.map +0 -1
- package/dist/chunk-RV7H3I6J.js.map +0 -1
- package/dist/chunk-TUKGA3UK.js +0 -257
- package/dist/chunk-TUKGA3UK.js 2.map +0 -1
- package/dist/chunk-TUKGA3UK.js.map +0 -1
- package/dist/generators-7Y5ABRYV.mjs +0 -161
- package/dist/generators-7Y5ABRYV.mjs 2.map +0 -1
- package/dist/generators-7Y5ABRYV.mjs.map +0 -1
- package/dist/generators-GWIYCA5M.js 2.map +0 -1
- package/dist/generators-GWIYCA5M.js.map +0 -1
- package/dist/index 2.mjs +0 -74
- package/dist/index.js 2.map +0 -1
- package/dist/migrator-V6KS75EA 2.mjs +0 -265
- package/dist/migrator-V6KS75EA.mjs 2.map +0 -1
- package/dist/migrator-XKM7YQCY.js 2.map +0 -1
- package/dist/scanner-MF7P3CDE.mjs +0 -14386
- package/dist/scanner-MF7P3CDE.mjs 2.map +0 -1
- package/dist/scanner-MF7P3CDE.mjs.map +0 -1
- package/dist/scanner-NT6YG4TD.js +0 -14397
- package/dist/scanner-NT6YG4TD.js 2.map +0 -1
- package/dist/scanner-NT6YG4TD.js.map +0 -1
- package/dist/web-vitals-BH55V7EJ.js 2.map +0 -1
- package/dist/web-vitals-RJYPWAR3 2.mjs +0 -241
- package/dist/web-vitals-RJYPWAR3.mjs 2.map +0 -1
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
import { SitemapSync } from './chunk-WPSRS352.mjs';
|
|
2
|
+
import { AnalyticsProvider } from './chunk-FKVJOT2F.mjs';
|
|
3
|
+
import { EngageWidget } from './chunk-BGJLOJ7T.mjs';
|
|
4
|
+
import { configureFormsApi } from './chunk-SMUFNQLM.mjs';
|
|
5
|
+
import { createContext, useContext, useState, useRef, useCallback, useEffect, useMemo, Suspense } from 'react';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var SignalContext = createContext(null);
|
|
9
|
+
function useSignal() {
|
|
10
|
+
const context = useContext(SignalContext);
|
|
11
|
+
if (!context) {
|
|
12
|
+
throw new Error("useSignal must be used within a SignalBridge");
|
|
13
|
+
}
|
|
14
|
+
return context;
|
|
15
|
+
}
|
|
16
|
+
function getApiConfig() {
|
|
17
|
+
const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
|
|
18
|
+
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
|
|
19
|
+
return { apiUrl, apiKey };
|
|
20
|
+
}
|
|
21
|
+
function getVisitorId() {
|
|
22
|
+
if (typeof window === "undefined") return "";
|
|
23
|
+
const key = "_uptrade_vid";
|
|
24
|
+
let visitorId = localStorage.getItem(key);
|
|
25
|
+
if (!visitorId) {
|
|
26
|
+
visitorId = crypto.randomUUID();
|
|
27
|
+
localStorage.setItem(key, visitorId);
|
|
28
|
+
}
|
|
29
|
+
return visitorId;
|
|
30
|
+
}
|
|
31
|
+
function getSessionId() {
|
|
32
|
+
if (typeof window === "undefined") return "";
|
|
33
|
+
const key = "_uptrade_sid";
|
|
34
|
+
let sessionId = sessionStorage.getItem(key);
|
|
35
|
+
if (!sessionId) {
|
|
36
|
+
sessionId = crypto.randomUUID();
|
|
37
|
+
sessionStorage.setItem(key, sessionId);
|
|
38
|
+
}
|
|
39
|
+
return sessionId;
|
|
40
|
+
}
|
|
41
|
+
function getDeviceType() {
|
|
42
|
+
if (typeof window === "undefined") return "desktop";
|
|
43
|
+
const ua = navigator.userAgent;
|
|
44
|
+
if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
|
|
45
|
+
if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
|
|
46
|
+
return "desktop";
|
|
47
|
+
}
|
|
48
|
+
function getBrowser() {
|
|
49
|
+
if (typeof window === "undefined") return "unknown";
|
|
50
|
+
const ua = navigator.userAgent;
|
|
51
|
+
if (ua.includes("Firefox")) return "Firefox";
|
|
52
|
+
if (ua.includes("Edg")) return "Edge";
|
|
53
|
+
if (ua.includes("Chrome")) return "Chrome";
|
|
54
|
+
if (ua.includes("Safari")) return "Safari";
|
|
55
|
+
return "Other";
|
|
56
|
+
}
|
|
57
|
+
function getOS() {
|
|
58
|
+
if (typeof window === "undefined") return "unknown";
|
|
59
|
+
const ua = navigator.userAgent;
|
|
60
|
+
if (ua.includes("Windows")) return "Windows";
|
|
61
|
+
if (ua.includes("Mac")) return "macOS";
|
|
62
|
+
if (ua.includes("iPhone") || ua.includes("iPad")) return "iOS";
|
|
63
|
+
if (ua.includes("Android")) return "Android";
|
|
64
|
+
if (ua.includes("Linux")) return "Linux";
|
|
65
|
+
return "Other";
|
|
66
|
+
}
|
|
67
|
+
function SignalBridge({
|
|
68
|
+
enabled = true,
|
|
69
|
+
realtime = true,
|
|
70
|
+
experiments = true,
|
|
71
|
+
behaviorTracking = true,
|
|
72
|
+
children
|
|
73
|
+
}) {
|
|
74
|
+
const [config, setConfig] = useState(null);
|
|
75
|
+
const [loading, setLoading] = useState(true);
|
|
76
|
+
const [error, setError] = useState(null);
|
|
77
|
+
const eventSourceRef = useRef(null);
|
|
78
|
+
const eventQueueRef = useRef([]);
|
|
79
|
+
const flushTimeoutRef = useRef(null);
|
|
80
|
+
const assignmentsRef = useRef(/* @__PURE__ */ new Map());
|
|
81
|
+
const pageLoadTimeRef = useRef(Date.now());
|
|
82
|
+
const scrollDepthRef = useRef(0);
|
|
83
|
+
const clickCountRef = useRef(0);
|
|
84
|
+
const { apiUrl, apiKey } = getApiConfig();
|
|
85
|
+
const fetchConfig = useCallback(async () => {
|
|
86
|
+
if (!apiKey || !enabled) {
|
|
87
|
+
setLoading(false);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const response = await fetch(`${apiUrl}/api/public/signal/config`, {
|
|
92
|
+
headers: {
|
|
93
|
+
"x-api-key": apiKey,
|
|
94
|
+
"x-visitor-id": getVisitorId()
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new Error(`Failed to fetch Signal config: ${response.statusText}`);
|
|
99
|
+
}
|
|
100
|
+
const data = await response.json();
|
|
101
|
+
setConfig(data.config);
|
|
102
|
+
setError(null);
|
|
103
|
+
if (experiments && data.config?.experiments) {
|
|
104
|
+
for (const exp of data.config.experiments) {
|
|
105
|
+
if (exp.status === "running") {
|
|
106
|
+
await loadExperimentAssignment(exp.id);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error("[Signal] Config fetch error:", err);
|
|
112
|
+
setError(err);
|
|
113
|
+
} finally {
|
|
114
|
+
setLoading(false);
|
|
115
|
+
}
|
|
116
|
+
}, [apiUrl, apiKey, enabled, experiments]);
|
|
117
|
+
const connectSSE = useCallback(() => {
|
|
118
|
+
if (!apiKey || !enabled || !realtime) return;
|
|
119
|
+
if (eventSourceRef.current) {
|
|
120
|
+
eventSourceRef.current.close();
|
|
121
|
+
}
|
|
122
|
+
const url = `${apiUrl}/api/public/signal/stream?key=${apiKey}`;
|
|
123
|
+
const eventSource = new EventSource(url);
|
|
124
|
+
eventSource.addEventListener("config_update", (e) => {
|
|
125
|
+
try {
|
|
126
|
+
const { config: newConfig, version } = JSON.parse(e.data);
|
|
127
|
+
setConfig((prev) => {
|
|
128
|
+
if (prev?.version !== version) {
|
|
129
|
+
console.log("[Signal] Config updated to version:", version);
|
|
130
|
+
return newConfig;
|
|
131
|
+
}
|
|
132
|
+
return prev;
|
|
133
|
+
});
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error("[Signal] SSE parse error:", err);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
eventSource.addEventListener("experiment_update", (e) => {
|
|
139
|
+
try {
|
|
140
|
+
const { experiment_id, action } = JSON.parse(e.data);
|
|
141
|
+
if (action === "started" || action === "updated") {
|
|
142
|
+
loadExperimentAssignment(experiment_id);
|
|
143
|
+
} else if (action === "stopped") {
|
|
144
|
+
assignmentsRef.current.delete(experiment_id);
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error("[Signal] Experiment update error:", err);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
eventSource.onerror = () => {
|
|
151
|
+
console.warn("[Signal] SSE connection error, reconnecting...");
|
|
152
|
+
eventSource.close();
|
|
153
|
+
setTimeout(connectSSE, 5e3);
|
|
154
|
+
};
|
|
155
|
+
eventSourceRef.current = eventSource;
|
|
156
|
+
}, [apiUrl, apiKey, enabled, realtime]);
|
|
157
|
+
const loadExperimentAssignment = useCallback(async (experimentId) => {
|
|
158
|
+
const storageKey = `_signal_exp_${experimentId}`;
|
|
159
|
+
const stored = localStorage.getItem(storageKey);
|
|
160
|
+
if (stored) {
|
|
161
|
+
try {
|
|
162
|
+
const assignment = JSON.parse(stored);
|
|
163
|
+
if (assignment.expires > Date.now()) {
|
|
164
|
+
assignmentsRef.current.set(experimentId, assignment);
|
|
165
|
+
return assignment;
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const response = await fetch(`${apiUrl}/api/public/signal/experiment/${experimentId}`, {
|
|
172
|
+
headers: {
|
|
173
|
+
"x-api-key": apiKey,
|
|
174
|
+
"x-visitor-id": getVisitorId()
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) return null;
|
|
178
|
+
const assignment = await response.json();
|
|
179
|
+
localStorage.setItem(storageKey, JSON.stringify(assignment));
|
|
180
|
+
assignmentsRef.current.set(experimentId, assignment);
|
|
181
|
+
return assignment;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error("[Signal] Experiment assignment error:", err);
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}, [apiUrl, apiKey]);
|
|
187
|
+
const getExperiment = useCallback((experimentId) => {
|
|
188
|
+
return assignmentsRef.current.get(experimentId) || null;
|
|
189
|
+
}, []);
|
|
190
|
+
const flushEvents = useCallback(async () => {
|
|
191
|
+
if (eventQueueRef.current.length === 0) return;
|
|
192
|
+
const events = [...eventQueueRef.current];
|
|
193
|
+
eventQueueRef.current = [];
|
|
194
|
+
try {
|
|
195
|
+
await fetch(`${apiUrl}/api/public/signal/events`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: {
|
|
198
|
+
"Content-Type": "application/json",
|
|
199
|
+
"x-api-key": apiKey
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
visitor_id: getVisitorId(),
|
|
203
|
+
session_id: getSessionId(),
|
|
204
|
+
events
|
|
205
|
+
})
|
|
206
|
+
});
|
|
207
|
+
} catch (err) {
|
|
208
|
+
eventQueueRef.current = [...events, ...eventQueueRef.current];
|
|
209
|
+
console.error("[Signal] Event flush error:", err);
|
|
210
|
+
}
|
|
211
|
+
}, [apiUrl, apiKey]);
|
|
212
|
+
const trackEvent = useCallback((event) => {
|
|
213
|
+
if (!enabled || !apiKey) return;
|
|
214
|
+
const enrichedEvent = {
|
|
215
|
+
...event,
|
|
216
|
+
page_url: window.location.href,
|
|
217
|
+
page_path: window.location.pathname,
|
|
218
|
+
referrer: document.referrer,
|
|
219
|
+
device_type: getDeviceType(),
|
|
220
|
+
browser: getBrowser(),
|
|
221
|
+
os: getOS(),
|
|
222
|
+
viewport_width: window.innerWidth,
|
|
223
|
+
viewport_height: window.innerHeight,
|
|
224
|
+
time_on_page: Date.now() - pageLoadTimeRef.current,
|
|
225
|
+
scroll_depth: scrollDepthRef.current,
|
|
226
|
+
click_count: clickCountRef.current,
|
|
227
|
+
experiments: Array.from(assignmentsRef.current.values()).map((a) => ({
|
|
228
|
+
experiment_id: a.experiment_id,
|
|
229
|
+
variant_key: a.variant_key
|
|
230
|
+
})),
|
|
231
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
232
|
+
};
|
|
233
|
+
eventQueueRef.current.push(enrichedEvent);
|
|
234
|
+
if (flushTimeoutRef.current) {
|
|
235
|
+
clearTimeout(flushTimeoutRef.current);
|
|
236
|
+
}
|
|
237
|
+
flushTimeoutRef.current = setTimeout(flushEvents, 1e3);
|
|
238
|
+
}, [enabled, apiKey, flushEvents]);
|
|
239
|
+
const trackOutcome = useCallback(async (outcome) => {
|
|
240
|
+
if (!enabled || !apiKey) return;
|
|
241
|
+
try {
|
|
242
|
+
await fetch(`${apiUrl}/api/public/signal/outcome`, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: {
|
|
245
|
+
"Content-Type": "application/json",
|
|
246
|
+
"x-api-key": apiKey
|
|
247
|
+
},
|
|
248
|
+
body: JSON.stringify({
|
|
249
|
+
...outcome,
|
|
250
|
+
visitor_id: getVisitorId(),
|
|
251
|
+
session_id: getSessionId(),
|
|
252
|
+
experiments: Array.from(assignmentsRef.current.keys()),
|
|
253
|
+
page_url: window.location.href,
|
|
254
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
255
|
+
})
|
|
256
|
+
});
|
|
257
|
+
} catch (err) {
|
|
258
|
+
console.error("[Signal] Outcome tracking error:", err);
|
|
259
|
+
}
|
|
260
|
+
}, [apiUrl, apiKey, enabled]);
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
if (!behaviorTracking || typeof window === "undefined") return;
|
|
263
|
+
const handleScroll = () => {
|
|
264
|
+
const scrollTop = window.scrollY;
|
|
265
|
+
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
266
|
+
const depth = docHeight > 0 ? Math.round(scrollTop / docHeight * 100) : 0;
|
|
267
|
+
scrollDepthRef.current = Math.max(scrollDepthRef.current, depth);
|
|
268
|
+
};
|
|
269
|
+
const handleClick = () => {
|
|
270
|
+
clickCountRef.current++;
|
|
271
|
+
};
|
|
272
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
273
|
+
window.addEventListener("click", handleClick);
|
|
274
|
+
const handleVisibilityChange = () => {
|
|
275
|
+
if (document.visibilityState === "hidden") {
|
|
276
|
+
flushEvents();
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
280
|
+
window.addEventListener("beforeunload", flushEvents);
|
|
281
|
+
return () => {
|
|
282
|
+
window.removeEventListener("scroll", handleScroll);
|
|
283
|
+
window.removeEventListener("click", handleClick);
|
|
284
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
285
|
+
window.removeEventListener("beforeunload", flushEvents);
|
|
286
|
+
};
|
|
287
|
+
}, [behaviorTracking, flushEvents]);
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
fetchConfig();
|
|
290
|
+
}, [fetchConfig]);
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
if (config && realtime) {
|
|
293
|
+
connectSSE();
|
|
294
|
+
}
|
|
295
|
+
return () => {
|
|
296
|
+
if (eventSourceRef.current) {
|
|
297
|
+
eventSourceRef.current.close();
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}, [config, realtime, connectSSE]);
|
|
301
|
+
const contextValue = useMemo(() => ({
|
|
302
|
+
config,
|
|
303
|
+
loading,
|
|
304
|
+
error,
|
|
305
|
+
trackEvent,
|
|
306
|
+
trackOutcome,
|
|
307
|
+
getExperiment,
|
|
308
|
+
refreshConfig: fetchConfig
|
|
309
|
+
}), [config, loading, error, trackEvent, trackOutcome, getExperiment, fetchConfig]);
|
|
310
|
+
return /* @__PURE__ */ jsx(SignalContext.Provider, { value: contextValue, children });
|
|
311
|
+
}
|
|
312
|
+
function useSignalConfig() {
|
|
313
|
+
const { config } = useSignal();
|
|
314
|
+
return config;
|
|
315
|
+
}
|
|
316
|
+
function useSignalEvent() {
|
|
317
|
+
const { trackEvent } = useSignal();
|
|
318
|
+
return trackEvent;
|
|
319
|
+
}
|
|
320
|
+
function useSignalOutcome() {
|
|
321
|
+
const { trackOutcome } = useSignal();
|
|
322
|
+
return { trackOutcome };
|
|
323
|
+
}
|
|
324
|
+
function useSignalExperiment(experimentId) {
|
|
325
|
+
const { getExperiment, config } = useSignal();
|
|
326
|
+
const assignment = getExperiment(experimentId);
|
|
327
|
+
const experiment = config?.experiments?.find((e) => e.id === experimentId);
|
|
328
|
+
const isRunning = experiment?.status === "running";
|
|
329
|
+
return {
|
|
330
|
+
assignment: isRunning ? assignment : null,
|
|
331
|
+
variant: isRunning && assignment ? assignment.variant_key : null,
|
|
332
|
+
isControl: !assignment || assignment.variant_key === "control"
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
var SiteKitContext = createContext(null);
|
|
336
|
+
function useSiteKit() {
|
|
337
|
+
const context = useContext(SiteKitContext);
|
|
338
|
+
if (!context) {
|
|
339
|
+
throw new Error("useSiteKit must be used within a SiteKitProvider");
|
|
340
|
+
}
|
|
341
|
+
return context;
|
|
342
|
+
}
|
|
343
|
+
function SiteKitProvider({
|
|
344
|
+
children,
|
|
345
|
+
apiUrl = "https://api.uptrademedia.com",
|
|
346
|
+
signalUrl = "https://signal.uptrademedia.com",
|
|
347
|
+
apiKey,
|
|
348
|
+
analytics,
|
|
349
|
+
engage,
|
|
350
|
+
forms,
|
|
351
|
+
signal,
|
|
352
|
+
debug = false
|
|
353
|
+
}) {
|
|
354
|
+
useEffect(() => {
|
|
355
|
+
if (typeof window !== "undefined") {
|
|
356
|
+
window.__SITE_KIT_API_URL__ = apiUrl;
|
|
357
|
+
window.__SITE_KIT_SIGNAL_URL__ = signalUrl;
|
|
358
|
+
window.__SITE_KIT_API_KEY__ = apiKey;
|
|
359
|
+
window.__SITE_KIT_DEBUG__ = debug;
|
|
360
|
+
}
|
|
361
|
+
configureFormsApi({
|
|
362
|
+
baseUrl: apiUrl,
|
|
363
|
+
apiKey
|
|
364
|
+
});
|
|
365
|
+
}, [apiUrl, signalUrl, apiKey, debug]);
|
|
366
|
+
const contextValue = useMemo(
|
|
367
|
+
() => ({
|
|
368
|
+
apiUrl,
|
|
369
|
+
signalUrl,
|
|
370
|
+
apiKey,
|
|
371
|
+
analytics,
|
|
372
|
+
engage,
|
|
373
|
+
forms,
|
|
374
|
+
signal,
|
|
375
|
+
debug,
|
|
376
|
+
isReady: true
|
|
377
|
+
}),
|
|
378
|
+
[apiUrl, signalUrl, apiKey, analytics, engage, forms, signal, debug]
|
|
379
|
+
);
|
|
380
|
+
let content = /* @__PURE__ */ jsx(Fragment, { children });
|
|
381
|
+
if (signal?.enabled) {
|
|
382
|
+
content = /* @__PURE__ */ jsx(
|
|
383
|
+
SignalBridge,
|
|
384
|
+
{
|
|
385
|
+
enabled: signal.enabled,
|
|
386
|
+
realtime: signal.realtime !== false,
|
|
387
|
+
experiments: signal.experiments !== false,
|
|
388
|
+
behaviorTracking: signal.behaviorTracking !== false,
|
|
389
|
+
children: content
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
if (analytics?.enabled) {
|
|
394
|
+
content = /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
395
|
+
AnalyticsProvider,
|
|
396
|
+
{
|
|
397
|
+
apiUrl,
|
|
398
|
+
apiKey,
|
|
399
|
+
trackPageViews: analytics.trackPageViews !== false,
|
|
400
|
+
trackWebVitals: analytics.trackWebVitals !== false,
|
|
401
|
+
trackScrollDepth: analytics.trackScrollDepth !== false,
|
|
402
|
+
trackClicks: analytics.trackClicks !== false,
|
|
403
|
+
debug,
|
|
404
|
+
children: content
|
|
405
|
+
}
|
|
406
|
+
) });
|
|
407
|
+
}
|
|
408
|
+
if (engage?.enabled) {
|
|
409
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
410
|
+
content,
|
|
411
|
+
/* @__PURE__ */ jsx(
|
|
412
|
+
EngageWidget,
|
|
413
|
+
{
|
|
414
|
+
apiUrl,
|
|
415
|
+
apiKey,
|
|
416
|
+
position: engage.position || "bottom-right",
|
|
417
|
+
chatEnabled: engage.chatEnabled !== false
|
|
418
|
+
}
|
|
419
|
+
)
|
|
420
|
+
] });
|
|
421
|
+
}
|
|
422
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
423
|
+
content,
|
|
424
|
+
/* @__PURE__ */ jsx(SitemapSync, { debug })
|
|
425
|
+
] });
|
|
426
|
+
return /* @__PURE__ */ jsx(SiteKitContext.Provider, { value: contextValue, children: content });
|
|
427
|
+
}
|
|
428
|
+
var isDevMode = () => {
|
|
429
|
+
if (typeof window === "undefined") return false;
|
|
430
|
+
return process.env.NODE_ENV === "development" || window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.search.includes("uptrade_dev=true");
|
|
431
|
+
};
|
|
432
|
+
function ManagedImage({
|
|
433
|
+
slotId,
|
|
434
|
+
pagePath,
|
|
435
|
+
alt,
|
|
436
|
+
className = "",
|
|
437
|
+
width,
|
|
438
|
+
height,
|
|
439
|
+
objectFit = "cover",
|
|
440
|
+
fallback,
|
|
441
|
+
placeholder,
|
|
442
|
+
onLoad,
|
|
443
|
+
onError,
|
|
444
|
+
priority,
|
|
445
|
+
style,
|
|
446
|
+
forceDevMode
|
|
447
|
+
}) {
|
|
448
|
+
const context = useSiteKit();
|
|
449
|
+
const [imageData, setImageData] = useState(null);
|
|
450
|
+
const [loading, setLoading] = useState(true);
|
|
451
|
+
const [error, setError] = useState(null);
|
|
452
|
+
const [showPicker, setShowPicker] = useState(false);
|
|
453
|
+
const [devMode] = useState(() => forceDevMode || isDevMode());
|
|
454
|
+
const currentPath = pagePath ?? (typeof window !== "undefined" ? window.location.pathname : "/");
|
|
455
|
+
const fetchImage = useCallback(async () => {
|
|
456
|
+
if (!context?.apiKey || !context?.apiUrl) {
|
|
457
|
+
setLoading(false);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
try {
|
|
461
|
+
const params = new URLSearchParams({ page_path: currentPath });
|
|
462
|
+
const res = await fetch(
|
|
463
|
+
`${context.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
|
|
464
|
+
{
|
|
465
|
+
headers: {
|
|
466
|
+
"Content-Type": "application/json",
|
|
467
|
+
"x-api-key": context.apiKey
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
if (!res.ok) {
|
|
472
|
+
throw new Error(`Failed to fetch image: ${res.status}`);
|
|
473
|
+
}
|
|
474
|
+
const data = await res.json();
|
|
475
|
+
setImageData(data.image);
|
|
476
|
+
setError(null);
|
|
477
|
+
} catch (err) {
|
|
478
|
+
console.error("[ManagedImage] Fetch error:", err);
|
|
479
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
480
|
+
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
481
|
+
} finally {
|
|
482
|
+
setLoading(false);
|
|
483
|
+
}
|
|
484
|
+
}, [context?.apiKey, context?.apiUrl, slotId, currentPath, onError]);
|
|
485
|
+
useEffect(() => {
|
|
486
|
+
fetchImage();
|
|
487
|
+
}, [fetchImage]);
|
|
488
|
+
const objectPosition = imageData ? `${imageData.focal_point_x}% ${imageData.focal_point_y}%` : "50% 50%";
|
|
489
|
+
const imageUrl = imageData?.public_url || imageData?.external_url || fallback;
|
|
490
|
+
const handleClick = (e) => {
|
|
491
|
+
if (devMode) {
|
|
492
|
+
e.preventDefault();
|
|
493
|
+
e.stopPropagation();
|
|
494
|
+
setShowPicker(true);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
if (loading) {
|
|
498
|
+
return /* @__PURE__ */ jsx(
|
|
499
|
+
"div",
|
|
500
|
+
{
|
|
501
|
+
className: `bg-gray-200 animate-pulse ${className}`,
|
|
502
|
+
style: {
|
|
503
|
+
width: width ?? "100%",
|
|
504
|
+
height: height ?? 200,
|
|
505
|
+
...style
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
if (!imageUrl) {
|
|
511
|
+
if (placeholder) {
|
|
512
|
+
return /* @__PURE__ */ jsx(Fragment, { children: placeholder });
|
|
513
|
+
}
|
|
514
|
+
return /* @__PURE__ */ jsxs(
|
|
515
|
+
"div",
|
|
516
|
+
{
|
|
517
|
+
onClick: handleClick,
|
|
518
|
+
className: `
|
|
519
|
+
bg-gray-100 border-2 border-dashed border-gray-300
|
|
520
|
+
flex items-center justify-center text-gray-400
|
|
521
|
+
${devMode ? "cursor-pointer hover:border-blue-400 hover:text-blue-500 hover:bg-blue-50" : ""}
|
|
522
|
+
${className}
|
|
523
|
+
`,
|
|
524
|
+
style: {
|
|
525
|
+
width: width ?? "100%",
|
|
526
|
+
height: height ?? 200,
|
|
527
|
+
...style
|
|
528
|
+
},
|
|
529
|
+
title: devMode ? `Click to add image for slot: ${slotId}` : void 0,
|
|
530
|
+
children: [
|
|
531
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center p-4", children: [
|
|
532
|
+
/* @__PURE__ */ jsx(
|
|
533
|
+
"svg",
|
|
534
|
+
{
|
|
535
|
+
className: "w-12 h-12 mx-auto mb-2",
|
|
536
|
+
fill: "none",
|
|
537
|
+
stroke: "currentColor",
|
|
538
|
+
viewBox: "0 0 24 24",
|
|
539
|
+
children: /* @__PURE__ */ jsx(
|
|
540
|
+
"path",
|
|
541
|
+
{
|
|
542
|
+
strokeLinecap: "round",
|
|
543
|
+
strokeLinejoin: "round",
|
|
544
|
+
strokeWidth: 1.5,
|
|
545
|
+
d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
546
|
+
}
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
),
|
|
550
|
+
devMode && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Click to add image" }),
|
|
551
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs mt-1", children: slotId })
|
|
552
|
+
] }),
|
|
553
|
+
showPicker && /* @__PURE__ */ jsx(
|
|
554
|
+
ImagePickerModal,
|
|
555
|
+
{
|
|
556
|
+
slotId,
|
|
557
|
+
pagePath: currentPath,
|
|
558
|
+
config: context,
|
|
559
|
+
onClose: () => setShowPicker(false),
|
|
560
|
+
onSelect: () => {
|
|
561
|
+
setShowPicker(false);
|
|
562
|
+
fetchImage();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
)
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", style: { width, height }, children: [
|
|
571
|
+
/* @__PURE__ */ jsx(
|
|
572
|
+
"img",
|
|
573
|
+
{
|
|
574
|
+
src: imageUrl,
|
|
575
|
+
alt: imageData?.alt_text || alt || "",
|
|
576
|
+
title: imageData?.title || void 0,
|
|
577
|
+
className,
|
|
578
|
+
style: {
|
|
579
|
+
objectFit,
|
|
580
|
+
objectPosition,
|
|
581
|
+
width: width ?? "100%",
|
|
582
|
+
height: height ?? "auto",
|
|
583
|
+
...style
|
|
584
|
+
},
|
|
585
|
+
onLoad,
|
|
586
|
+
onError: () => onError?.(new Error("Image failed to load")),
|
|
587
|
+
loading: priority ? "eager" : "lazy",
|
|
588
|
+
onClick: handleClick
|
|
589
|
+
}
|
|
590
|
+
),
|
|
591
|
+
devMode && /* @__PURE__ */ jsx(
|
|
592
|
+
"div",
|
|
593
|
+
{
|
|
594
|
+
className: "absolute inset-0 bg-black/0 hover:bg-black/30 transition-colors cursor-pointer flex items-center justify-center opacity-0 hover:opacity-100",
|
|
595
|
+
onClick: handleClick,
|
|
596
|
+
children: /* @__PURE__ */ jsx("div", { className: "bg-white/90 px-3 py-1.5 rounded-lg shadow-lg text-sm font-medium text-gray-700", children: "Edit Image" })
|
|
597
|
+
}
|
|
598
|
+
),
|
|
599
|
+
showPicker && /* @__PURE__ */ jsx(
|
|
600
|
+
ImagePickerModal,
|
|
601
|
+
{
|
|
602
|
+
slotId,
|
|
603
|
+
pagePath: currentPath,
|
|
604
|
+
config: context,
|
|
605
|
+
currentImage: imageData,
|
|
606
|
+
onClose: () => setShowPicker(false),
|
|
607
|
+
onSelect: () => {
|
|
608
|
+
setShowPicker(false);
|
|
609
|
+
fetchImage();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
)
|
|
613
|
+
] });
|
|
614
|
+
}
|
|
615
|
+
function ImagePickerModal({
|
|
616
|
+
slotId,
|
|
617
|
+
pagePath,
|
|
618
|
+
config,
|
|
619
|
+
currentImage,
|
|
620
|
+
onClose,
|
|
621
|
+
onSelect
|
|
622
|
+
}) {
|
|
623
|
+
const [files, setFiles] = useState([]);
|
|
624
|
+
const [folders, setFolders] = useState([]);
|
|
625
|
+
const [currentFolder, setCurrentFolder] = useState("");
|
|
626
|
+
const [search, setSearch] = useState("");
|
|
627
|
+
const [loading, setLoading] = useState(true);
|
|
628
|
+
const [uploading, setUploading] = useState(false);
|
|
629
|
+
const [altText, setAltText] = useState(currentImage?.alt_text || "");
|
|
630
|
+
const fetchFiles = useCallback(async () => {
|
|
631
|
+
if (!config?.apiKey || !config?.apiUrl) return;
|
|
632
|
+
setLoading(true);
|
|
633
|
+
try {
|
|
634
|
+
const params = new URLSearchParams();
|
|
635
|
+
if (currentFolder) params.set("folder", currentFolder);
|
|
636
|
+
if (search) params.set("search", search);
|
|
637
|
+
const res = await fetch(
|
|
638
|
+
`${config.apiUrl}/public/images/files?${params}`,
|
|
639
|
+
{
|
|
640
|
+
headers: {
|
|
641
|
+
"Content-Type": "application/json",
|
|
642
|
+
"x-api-key": config.apiKey
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
if (res.ok) {
|
|
647
|
+
const data = await res.json();
|
|
648
|
+
setFiles(data.files || []);
|
|
649
|
+
setFolders(data.folders || []);
|
|
650
|
+
}
|
|
651
|
+
} catch (err) {
|
|
652
|
+
console.error("[ImagePicker] Fetch error:", err);
|
|
653
|
+
} finally {
|
|
654
|
+
setLoading(false);
|
|
655
|
+
}
|
|
656
|
+
}, [config?.apiKey, config?.apiUrl, currentFolder, search]);
|
|
657
|
+
useEffect(() => {
|
|
658
|
+
fetchFiles();
|
|
659
|
+
}, [fetchFiles]);
|
|
660
|
+
const handleSelectFile = async (file) => {
|
|
661
|
+
if (!config?.apiKey || !config?.apiUrl) return;
|
|
662
|
+
try {
|
|
663
|
+
const res = await fetch(
|
|
664
|
+
`${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}`,
|
|
665
|
+
{
|
|
666
|
+
method: "POST",
|
|
667
|
+
headers: {
|
|
668
|
+
"Content-Type": "application/json",
|
|
669
|
+
"x-api-key": config.apiKey
|
|
670
|
+
},
|
|
671
|
+
body: JSON.stringify({
|
|
672
|
+
page_path: pagePath,
|
|
673
|
+
file_id: file.id,
|
|
674
|
+
alt_text: altText || file.filename
|
|
675
|
+
})
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
if (res.ok) {
|
|
679
|
+
onSelect();
|
|
680
|
+
}
|
|
681
|
+
} catch (err) {
|
|
682
|
+
console.error("[ImagePicker] Select error:", err);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
const handleUpload = async (e) => {
|
|
686
|
+
const file = e.target.files?.[0];
|
|
687
|
+
if (!file || !config?.apiKey || !config?.apiUrl) return;
|
|
688
|
+
setUploading(true);
|
|
689
|
+
try {
|
|
690
|
+
const formData = new FormData();
|
|
691
|
+
formData.append("file", file);
|
|
692
|
+
formData.append("slot_id", slotId);
|
|
693
|
+
formData.append("page_path", pagePath);
|
|
694
|
+
formData.append("folder", currentFolder || "Website/Images");
|
|
695
|
+
if (altText) formData.append("alt_text", altText);
|
|
696
|
+
const res = await fetch(`${config.apiUrl}/public/images/upload`, {
|
|
697
|
+
method: "POST",
|
|
698
|
+
headers: {
|
|
699
|
+
"x-api-key": config.apiKey
|
|
700
|
+
},
|
|
701
|
+
body: formData
|
|
702
|
+
});
|
|
703
|
+
if (res.ok) {
|
|
704
|
+
onSelect();
|
|
705
|
+
}
|
|
706
|
+
} catch (err) {
|
|
707
|
+
console.error("[ImagePicker] Upload error:", err);
|
|
708
|
+
} finally {
|
|
709
|
+
setUploading(false);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
const handleClear = async () => {
|
|
713
|
+
if (!config?.apiKey || !config?.apiUrl) return;
|
|
714
|
+
try {
|
|
715
|
+
const params = new URLSearchParams({ page_path: pagePath });
|
|
716
|
+
await fetch(
|
|
717
|
+
`${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
|
|
718
|
+
{
|
|
719
|
+
method: "DELETE",
|
|
720
|
+
headers: {
|
|
721
|
+
"x-api-key": config.apiKey
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
);
|
|
725
|
+
onSelect();
|
|
726
|
+
} catch (err) {
|
|
727
|
+
console.error("[ImagePicker] Clear error:", err);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
return /* @__PURE__ */ jsx(
|
|
731
|
+
"div",
|
|
732
|
+
{
|
|
733
|
+
className: "fixed inset-0 z-[99999] bg-black/50 flex items-center justify-center p-4",
|
|
734
|
+
onClick: onClose,
|
|
735
|
+
children: /* @__PURE__ */ jsxs(
|
|
736
|
+
"div",
|
|
737
|
+
{
|
|
738
|
+
className: "bg-white rounded-xl shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-hidden",
|
|
739
|
+
onClick: (e) => e.stopPropagation(),
|
|
740
|
+
children: [
|
|
741
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-4 border-b", children: [
|
|
742
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
743
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Select Image" }),
|
|
744
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500", children: [
|
|
745
|
+
"Slot: ",
|
|
746
|
+
slotId
|
|
747
|
+
] })
|
|
748
|
+
] }),
|
|
749
|
+
/* @__PURE__ */ jsx(
|
|
750
|
+
"button",
|
|
751
|
+
{
|
|
752
|
+
onClick: onClose,
|
|
753
|
+
className: "text-gray-400 hover:text-gray-600 p-2",
|
|
754
|
+
children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
755
|
+
}
|
|
756
|
+
)
|
|
757
|
+
] }),
|
|
758
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 overflow-y-auto", style: { maxHeight: "calc(90vh - 180px)" }, children: [
|
|
759
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4 mb-4", children: [
|
|
760
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
761
|
+
"input",
|
|
762
|
+
{
|
|
763
|
+
type: "text",
|
|
764
|
+
placeholder: "Search images...",
|
|
765
|
+
value: search,
|
|
766
|
+
onChange: (e) => setSearch(e.target.value),
|
|
767
|
+
className: "w-full px-3 py-2 border rounded-lg text-sm"
|
|
768
|
+
}
|
|
769
|
+
) }),
|
|
770
|
+
/* @__PURE__ */ jsxs("label", { className: "px-4 py-2 bg-blue-600 text-white rounded-lg cursor-pointer hover:bg-blue-700 text-sm font-medium flex items-center gap-2", children: [
|
|
771
|
+
uploading ? "Uploading..." : "Upload New",
|
|
772
|
+
/* @__PURE__ */ jsx(
|
|
773
|
+
"input",
|
|
774
|
+
{
|
|
775
|
+
type: "file",
|
|
776
|
+
accept: "image/*",
|
|
777
|
+
onChange: handleUpload,
|
|
778
|
+
disabled: uploading,
|
|
779
|
+
className: "hidden"
|
|
780
|
+
}
|
|
781
|
+
)
|
|
782
|
+
] }),
|
|
783
|
+
currentImage?.file_id && /* @__PURE__ */ jsx(
|
|
784
|
+
"button",
|
|
785
|
+
{
|
|
786
|
+
onClick: handleClear,
|
|
787
|
+
className: "px-4 py-2 border border-red-200 text-red-600 rounded-lg hover:bg-red-50 text-sm",
|
|
788
|
+
children: "Remove"
|
|
789
|
+
}
|
|
790
|
+
)
|
|
791
|
+
] }),
|
|
792
|
+
/* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx(
|
|
793
|
+
"input",
|
|
794
|
+
{
|
|
795
|
+
type: "text",
|
|
796
|
+
placeholder: "Alt text for image...",
|
|
797
|
+
value: altText,
|
|
798
|
+
onChange: (e) => setAltText(e.target.value),
|
|
799
|
+
className: "w-full px-3 py-2 border rounded-lg text-sm"
|
|
800
|
+
}
|
|
801
|
+
) }),
|
|
802
|
+
folders.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex gap-2 mb-4 flex-wrap", children: [
|
|
803
|
+
/* @__PURE__ */ jsx(
|
|
804
|
+
"button",
|
|
805
|
+
{
|
|
806
|
+
onClick: () => setCurrentFolder(""),
|
|
807
|
+
className: `px-3 py-1 text-sm rounded-full ${!currentFolder ? "bg-blue-100 text-blue-700" : "bg-gray-100 hover:bg-gray-200"}`,
|
|
808
|
+
children: "All"
|
|
809
|
+
}
|
|
810
|
+
),
|
|
811
|
+
folders.map((folder) => /* @__PURE__ */ jsx(
|
|
812
|
+
"button",
|
|
813
|
+
{
|
|
814
|
+
onClick: () => setCurrentFolder(folder),
|
|
815
|
+
className: `px-3 py-1 text-sm rounded-full ${currentFolder === folder ? "bg-blue-100 text-blue-700" : "bg-gray-100 hover:bg-gray-200"}`,
|
|
816
|
+
children: folder
|
|
817
|
+
},
|
|
818
|
+
folder
|
|
819
|
+
))
|
|
820
|
+
] }),
|
|
821
|
+
loading ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: [...Array(8)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "aspect-square bg-gray-200 rounded-lg animate-pulse" }, i)) }) : files.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-12 text-gray-500", children: [
|
|
822
|
+
/* @__PURE__ */ jsx("p", { children: "No images found" }),
|
|
823
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm mt-1", children: "Upload a new image to get started" })
|
|
824
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-4", children: files.map((file) => /* @__PURE__ */ jsx(
|
|
825
|
+
"button",
|
|
826
|
+
{
|
|
827
|
+
onClick: () => handleSelectFile(file),
|
|
828
|
+
className: `
|
|
829
|
+
aspect-square rounded-lg overflow-hidden border-2 transition-all
|
|
830
|
+
hover:border-blue-400 hover:shadow-lg
|
|
831
|
+
${currentImage?.file_id === file.id ? "border-blue-500 ring-2 ring-blue-200" : "border-gray-200"}
|
|
832
|
+
`,
|
|
833
|
+
children: /* @__PURE__ */ jsx(
|
|
834
|
+
"img",
|
|
835
|
+
{
|
|
836
|
+
src: file.public_url,
|
|
837
|
+
alt: file.filename,
|
|
838
|
+
className: "w-full h-full object-cover"
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
},
|
|
842
|
+
file.id
|
|
843
|
+
)) })
|
|
844
|
+
] }),
|
|
845
|
+
/* @__PURE__ */ jsx("div", { className: "p-4 border-t bg-gray-50 flex justify-end", children: /* @__PURE__ */ jsx(
|
|
846
|
+
"button",
|
|
847
|
+
{
|
|
848
|
+
onClick: onClose,
|
|
849
|
+
className: "px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg text-sm",
|
|
850
|
+
children: "Cancel"
|
|
851
|
+
}
|
|
852
|
+
) })
|
|
853
|
+
]
|
|
854
|
+
}
|
|
855
|
+
)
|
|
856
|
+
}
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// src/images/api.ts
|
|
861
|
+
async function fetchManagedImage(config, slotId, pagePath) {
|
|
862
|
+
const params = new URLSearchParams();
|
|
863
|
+
if (pagePath) params.set("page_path", pagePath);
|
|
864
|
+
const res = await fetch(
|
|
865
|
+
`${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
|
|
866
|
+
{
|
|
867
|
+
headers: {
|
|
868
|
+
"Content-Type": "application/json",
|
|
869
|
+
"x-api-key": config.apiKey
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
);
|
|
873
|
+
if (!res.ok) {
|
|
874
|
+
throw new Error(`Failed to fetch image: ${res.status}`);
|
|
875
|
+
}
|
|
876
|
+
return res.json();
|
|
877
|
+
}
|
|
878
|
+
async function fetchManagedImages(config, options) {
|
|
879
|
+
const params = new URLSearchParams();
|
|
880
|
+
if (options?.pagePath) params.set("page_path", options.pagePath);
|
|
881
|
+
if (options?.category) params.set("category", options.category);
|
|
882
|
+
if (options?.includePlaceholders) params.set("include_placeholders", "true");
|
|
883
|
+
const res = await fetch(
|
|
884
|
+
`${config.apiUrl}/public/images?${params}`,
|
|
885
|
+
{
|
|
886
|
+
headers: {
|
|
887
|
+
"Content-Type": "application/json",
|
|
888
|
+
"x-api-key": config.apiKey
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
if (!res.ok) {
|
|
893
|
+
throw new Error(`Failed to fetch images: ${res.status}`);
|
|
894
|
+
}
|
|
895
|
+
return res.json();
|
|
896
|
+
}
|
|
897
|
+
async function listImageFiles(config, options) {
|
|
898
|
+
const params = new URLSearchParams();
|
|
899
|
+
if (options?.folder) params.set("folder", options.folder);
|
|
900
|
+
if (options?.search) params.set("search", options.search);
|
|
901
|
+
const res = await fetch(
|
|
902
|
+
`${config.apiUrl}/public/images/files?${params}`,
|
|
903
|
+
{
|
|
904
|
+
headers: {
|
|
905
|
+
"Content-Type": "application/json",
|
|
906
|
+
"x-api-key": config.apiKey
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
);
|
|
910
|
+
if (!res.ok) {
|
|
911
|
+
throw new Error(`Failed to list files: ${res.status}`);
|
|
912
|
+
}
|
|
913
|
+
return res.json();
|
|
914
|
+
}
|
|
915
|
+
async function uploadImage(config, file, options) {
|
|
916
|
+
const formData = new FormData();
|
|
917
|
+
formData.append("file", file);
|
|
918
|
+
if (options?.slotId) formData.append("slot_id", options.slotId);
|
|
919
|
+
if (options?.pagePath) formData.append("page_path", options.pagePath);
|
|
920
|
+
if (options?.folder) formData.append("folder", options.folder);
|
|
921
|
+
if (options?.altText) formData.append("alt_text", options.altText);
|
|
922
|
+
const res = await fetch(`${config.apiUrl}/public/images/upload`, {
|
|
923
|
+
method: "POST",
|
|
924
|
+
headers: {
|
|
925
|
+
"x-api-key": config.apiKey
|
|
926
|
+
},
|
|
927
|
+
body: formData
|
|
928
|
+
});
|
|
929
|
+
if (!res.ok) {
|
|
930
|
+
throw new Error(`Failed to upload image: ${res.status}`);
|
|
931
|
+
}
|
|
932
|
+
return res.json();
|
|
933
|
+
}
|
|
934
|
+
async function assignImageToSlot(config, slotId, options) {
|
|
935
|
+
const res = await fetch(
|
|
936
|
+
`${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}`,
|
|
937
|
+
{
|
|
938
|
+
method: "POST",
|
|
939
|
+
headers: {
|
|
940
|
+
"Content-Type": "application/json",
|
|
941
|
+
"x-api-key": config.apiKey
|
|
942
|
+
},
|
|
943
|
+
body: JSON.stringify({
|
|
944
|
+
page_path: options.pagePath,
|
|
945
|
+
file_id: options.fileId,
|
|
946
|
+
external_url: options.externalUrl,
|
|
947
|
+
alt_text: options.altText,
|
|
948
|
+
title: options.title,
|
|
949
|
+
caption: options.caption,
|
|
950
|
+
focal_point_x: options.focalPointX,
|
|
951
|
+
focal_point_y: options.focalPointY,
|
|
952
|
+
aspect_ratio: options.aspectRatio
|
|
953
|
+
})
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
if (!res.ok) {
|
|
957
|
+
throw new Error(`Failed to assign image: ${res.status}`);
|
|
958
|
+
}
|
|
959
|
+
return res.json();
|
|
960
|
+
}
|
|
961
|
+
async function clearImageSlot(config, slotId, pagePath) {
|
|
962
|
+
const params = new URLSearchParams();
|
|
963
|
+
if (pagePath) params.set("page_path", pagePath);
|
|
964
|
+
const res = await fetch(
|
|
965
|
+
`${config.apiUrl}/public/images/slot/${encodeURIComponent(slotId)}?${params}`,
|
|
966
|
+
{
|
|
967
|
+
method: "DELETE",
|
|
968
|
+
headers: {
|
|
969
|
+
"x-api-key": config.apiKey
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
);
|
|
973
|
+
if (!res.ok) {
|
|
974
|
+
throw new Error(`Failed to clear slot: ${res.status}`);
|
|
975
|
+
}
|
|
976
|
+
return res.json();
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
export { ManagedImage, SignalBridge, SiteKitProvider, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage, useSignal, useSignalConfig, useSignalEvent, useSignalExperiment, useSignalOutcome, useSiteKit };
|
|
980
|
+
//# sourceMappingURL=chunk-XQJX252G.mjs.map
|
|
981
|
+
//# sourceMappingURL=chunk-XQJX252G.mjs.map
|