@neowhale/storefront 0.2.34 → 0.2.36
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/landing.global.js +448 -525
- package/dist/react/index.cjs +343 -470
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +9 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.js +343 -470
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -2135,7 +2135,18 @@ function useReferral() {
|
|
|
2135
2135
|
referredBy: status?.referred_by ?? null
|
|
2136
2136
|
};
|
|
2137
2137
|
}
|
|
2138
|
-
|
|
2138
|
+
function trackClick(tracking, label, url, position) {
|
|
2139
|
+
if (!tracking?.gatewayUrl || !tracking?.code) return;
|
|
2140
|
+
const body = JSON.stringify({ label, url, position });
|
|
2141
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
2142
|
+
navigator.sendBeacon(
|
|
2143
|
+
`${tracking.gatewayUrl}/q/${encodeURIComponent(tracking.code)}/click`,
|
|
2144
|
+
new Blob([body], { type: "application/json" })
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
var NUM_SPLIT = /(\$?[\d,]+\.?\d*[+★%]?)/g;
|
|
2149
|
+
var NUM_TEST = /^\$?[\d,]+\.?\d*[+★%]?$/;
|
|
2139
2150
|
function easeOutQuart(t) {
|
|
2140
2151
|
return 1 - Math.pow(1 - t, 4);
|
|
2141
2152
|
}
|
|
@@ -2192,147 +2203,69 @@ function AnimatedNumber({ raw }) {
|
|
|
2192
2203
|
] });
|
|
2193
2204
|
}
|
|
2194
2205
|
function AnimatedText({ text }) {
|
|
2195
|
-
const parts = text.split(
|
|
2206
|
+
const parts = text.split(NUM_SPLIT);
|
|
2196
2207
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: parts.map(
|
|
2197
|
-
(part, i) =>
|
|
2208
|
+
(part, i) => NUM_TEST.test(part) ? /* @__PURE__ */ jsxRuntime.jsx(AnimatedNumber, { raw: part }, i) : part
|
|
2198
2209
|
) });
|
|
2199
2210
|
}
|
|
2200
|
-
function trackClick(tracking, label, url, position) {
|
|
2201
|
-
if (!tracking?.gatewayUrl || !tracking?.code) return;
|
|
2202
|
-
const body = JSON.stringify({ label, url, position });
|
|
2203
|
-
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
2204
|
-
navigator.sendBeacon(
|
|
2205
|
-
`${tracking.gatewayUrl}/q/${encodeURIComponent(tracking.code)}/click`,
|
|
2206
|
-
new Blob([body], { type: "application/json" })
|
|
2207
|
-
);
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
function SectionRenderer({
|
|
2211
|
-
section,
|
|
2212
|
-
data,
|
|
2213
|
-
theme,
|
|
2214
|
-
tracking,
|
|
2215
|
-
onEvent
|
|
2216
|
-
}) {
|
|
2217
|
-
const [showCOA, setShowCOA] = react.useState(false);
|
|
2218
|
-
const el = (() => {
|
|
2219
|
-
switch (section.type) {
|
|
2220
|
-
case "hero":
|
|
2221
|
-
return /* @__PURE__ */ jsxRuntime.jsx(HeroSection, { section, theme, tracking, onEvent });
|
|
2222
|
-
case "text":
|
|
2223
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TextSection, { section, theme });
|
|
2224
|
-
case "image":
|
|
2225
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ImageSection, { section, theme });
|
|
2226
|
-
case "video":
|
|
2227
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VideoSection, { section, theme });
|
|
2228
|
-
case "gallery":
|
|
2229
|
-
return /* @__PURE__ */ jsxRuntime.jsx(GallerySection, { section, theme });
|
|
2230
|
-
case "cta":
|
|
2231
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CTASection, { section, theme, tracking, onEvent });
|
|
2232
|
-
case "stats":
|
|
2233
|
-
return /* @__PURE__ */ jsxRuntime.jsx(StatsSection, { section, theme });
|
|
2234
|
-
case "product_card":
|
|
2235
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ProductCardSection, { section, data, theme, tracking });
|
|
2236
|
-
case "coa_viewer":
|
|
2237
|
-
return /* @__PURE__ */ jsxRuntime.jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true), tracking });
|
|
2238
|
-
case "social_links":
|
|
2239
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SocialLinksSection, { section, theme });
|
|
2240
|
-
case "lead_capture":
|
|
2241
|
-
return /* @__PURE__ */ jsxRuntime.jsx(LeadCaptureSection, { section, data, theme, onEvent });
|
|
2242
|
-
case "divider":
|
|
2243
|
-
return /* @__PURE__ */ jsxRuntime.jsx(DividerSection, { theme });
|
|
2244
|
-
default:
|
|
2245
|
-
return null;
|
|
2246
|
-
}
|
|
2247
|
-
})();
|
|
2248
|
-
const sectionRef = react.useRef(null);
|
|
2249
|
-
react.useEffect(() => {
|
|
2250
|
-
const el2 = sectionRef.current;
|
|
2251
|
-
if (!el2 || typeof IntersectionObserver === "undefined") return;
|
|
2252
|
-
const obs = new IntersectionObserver(
|
|
2253
|
-
([entry]) => {
|
|
2254
|
-
if (entry.isIntersecting) {
|
|
2255
|
-
onEvent?.("section_view", {
|
|
2256
|
-
section_id: section.id,
|
|
2257
|
-
section_type: section.type
|
|
2258
|
-
});
|
|
2259
|
-
obs.disconnect();
|
|
2260
|
-
}
|
|
2261
|
-
},
|
|
2262
|
-
{ threshold: 0.5 }
|
|
2263
|
-
);
|
|
2264
|
-
obs.observe(el2);
|
|
2265
|
-
return () => obs.disconnect();
|
|
2266
|
-
}, [section.id, section.type, onEvent]);
|
|
2267
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: sectionRef, "data-section-id": section.id, "data-section-type": section.type, children: [
|
|
2268
|
-
el,
|
|
2269
|
-
showCOA && data?.coa && /* @__PURE__ */ jsxRuntime.jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
|
|
2270
|
-
] });
|
|
2271
|
-
}
|
|
2272
2211
|
function HeroSection({ section, theme, tracking, onEvent }) {
|
|
2273
2212
|
const { title, subtitle, background_image, cta_text, cta_url } = section.content;
|
|
2274
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2275
|
-
"
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2213
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
2214
|
+
position: "relative",
|
|
2215
|
+
minHeight: "60vh",
|
|
2216
|
+
display: "flex",
|
|
2217
|
+
flexDirection: "column",
|
|
2218
|
+
justifyContent: "center",
|
|
2219
|
+
alignItems: "center",
|
|
2220
|
+
textAlign: "center",
|
|
2221
|
+
padding: "3rem 1.5rem",
|
|
2222
|
+
backgroundImage: background_image ? `url(${background_image})` : void 0,
|
|
2223
|
+
backgroundSize: "cover",
|
|
2224
|
+
backgroundPosition: "center"
|
|
2225
|
+
}, children: [
|
|
2226
|
+
background_image && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
|
|
2227
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
|
|
2228
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h1", { style: {
|
|
2229
|
+
fontSize: "clamp(2rem, 8vw, 3rem)",
|
|
2230
|
+
fontWeight: 300,
|
|
2231
|
+
fontFamily: theme.fontDisplay || "inherit",
|
|
2232
|
+
margin: "0 0 1rem",
|
|
2233
|
+
lineHeight: 1.15,
|
|
2234
|
+
letterSpacing: "-0.02em",
|
|
2235
|
+
color: theme.fg
|
|
2236
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: title }) }),
|
|
2237
|
+
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
|
|
2238
|
+
fontSize: "0.85rem",
|
|
2239
|
+
color: theme.accent,
|
|
2240
|
+
margin: "0 0 2rem",
|
|
2241
|
+
lineHeight: 1.6,
|
|
2242
|
+
textTransform: "uppercase",
|
|
2243
|
+
letterSpacing: "0.15em"
|
|
2244
|
+
}, children: subtitle }),
|
|
2245
|
+
cta_text && cta_url && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2246
|
+
"a",
|
|
2247
|
+
{
|
|
2248
|
+
href: cta_url,
|
|
2249
|
+
onClick: () => {
|
|
2250
|
+
trackClick(tracking, cta_text, cta_url);
|
|
2251
|
+
onEvent?.("cta_click", { label: cta_text, url: cta_url });
|
|
2252
|
+
},
|
|
2253
|
+
style: {
|
|
2254
|
+
display: "inline-block",
|
|
2255
|
+
padding: "0.875rem 2rem",
|
|
2256
|
+
background: theme.fg,
|
|
2257
|
+
color: theme.bg,
|
|
2258
|
+
textDecoration: "none",
|
|
2303
2259
|
fontSize: "0.85rem",
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
href: cta_url,
|
|
2314
|
-
onClick: () => {
|
|
2315
|
-
trackClick(tracking, cta_text, cta_url);
|
|
2316
|
-
onEvent?.("cta_click", { label: cta_text, url: cta_url });
|
|
2317
|
-
},
|
|
2318
|
-
style: {
|
|
2319
|
-
display: "inline-block",
|
|
2320
|
-
padding: "0.875rem 2rem",
|
|
2321
|
-
background: theme.fg,
|
|
2322
|
-
color: theme.bg,
|
|
2323
|
-
textDecoration: "none",
|
|
2324
|
-
fontSize: "0.85rem",
|
|
2325
|
-
fontWeight: 500,
|
|
2326
|
-
letterSpacing: "0.08em",
|
|
2327
|
-
textTransform: "uppercase"
|
|
2328
|
-
},
|
|
2329
|
-
children: cta_text
|
|
2330
|
-
}
|
|
2331
|
-
)
|
|
2332
|
-
] })
|
|
2333
|
-
]
|
|
2334
|
-
}
|
|
2335
|
-
);
|
|
2260
|
+
fontWeight: 500,
|
|
2261
|
+
letterSpacing: "0.08em",
|
|
2262
|
+
textTransform: "uppercase"
|
|
2263
|
+
},
|
|
2264
|
+
children: cta_text
|
|
2265
|
+
}
|
|
2266
|
+
)
|
|
2267
|
+
] })
|
|
2268
|
+
] });
|
|
2336
2269
|
}
|
|
2337
2270
|
function TextSection({ section, theme }) {
|
|
2338
2271
|
const { heading, body } = section.content;
|
|
@@ -2354,14 +2287,7 @@ function ImageSection({ section, theme }) {
|
|
|
2354
2287
|
const contained = section.config?.contained !== false;
|
|
2355
2288
|
if (!url) return null;
|
|
2356
2289
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: contained ? "1.5rem" : 0, maxWidth: contained ? 640 : void 0, margin: contained ? "0 auto" : void 0 }, children: [
|
|
2357
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2358
|
-
"img",
|
|
2359
|
-
{
|
|
2360
|
-
src: url,
|
|
2361
|
-
alt: alt || "",
|
|
2362
|
-
style: { width: "100%", display: "block", objectFit: "cover" }
|
|
2363
|
-
}
|
|
2364
|
-
),
|
|
2290
|
+
/* @__PURE__ */ jsxRuntime.jsx("img", { src: url, alt: alt || "", style: { width: "100%", display: "block", objectFit: "cover" } }),
|
|
2365
2291
|
caption && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8rem", color: theme.muted, textAlign: "center", marginTop: "0.75rem" }, children: caption })
|
|
2366
2292
|
] });
|
|
2367
2293
|
}
|
|
@@ -2369,36 +2295,38 @@ function VideoSection({ section, theme }) {
|
|
|
2369
2295
|
const { url, poster } = section.content;
|
|
2370
2296
|
if (!url) return null;
|
|
2371
2297
|
const isEmbed = url.includes("youtube") || url.includes("youtu.be") || url.includes("vimeo");
|
|
2372
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: isEmbed ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", paddingBottom: "56.25%", height: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2373
|
-
"iframe",
|
|
2374
|
-
{
|
|
2375
|
-
src: toEmbedUrl(url),
|
|
2376
|
-
style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" },
|
|
2377
|
-
allow: "autoplay; fullscreen",
|
|
2378
|
-
title: "Video"
|
|
2379
|
-
}
|
|
2380
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2381
|
-
"video",
|
|
2382
|
-
{
|
|
2383
|
-
src: url,
|
|
2384
|
-
poster,
|
|
2385
|
-
controls: true,
|
|
2386
|
-
style: { width: "100%", display: "block", background: theme.surface }
|
|
2387
|
-
}
|
|
2388
|
-
) });
|
|
2298
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: isEmbed ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", paddingBottom: "56.25%", height: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx("iframe", { src: toEmbedUrl(url), style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", border: "none" }, allow: "autoplay; fullscreen", title: "Video" }) }) : /* @__PURE__ */ jsxRuntime.jsx("video", { src: url, poster, controls: true, style: { width: "100%", display: "block", background: theme.surface } }) });
|
|
2389
2299
|
}
|
|
2390
2300
|
function GallerySection({ section, theme }) {
|
|
2391
2301
|
const { images } = section.content;
|
|
2392
2302
|
const columns = section.config?.columns || 3;
|
|
2393
2303
|
if (!images || images.length === 0) return null;
|
|
2394
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 800, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: "0.5rem" }, children: images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { aspectRatio: "1", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2395
|
-
|
|
2304
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 800, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, gap: "0.5rem" }, children: images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { aspectRatio: "1", overflow: "hidden", background: theme.surface }, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: img.url, alt: img.alt || "", style: { width: "100%", height: "100%", objectFit: "cover", display: "block" } }) }, i)) }) });
|
|
2305
|
+
}
|
|
2306
|
+
function SocialLinksSection({ section, theme }) {
|
|
2307
|
+
const { links } = section.content;
|
|
2308
|
+
if (!links || links.length === 0) return null;
|
|
2309
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center", gap: "1.5rem", flexWrap: "wrap" }, children: links.map((link, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2310
|
+
"a",
|
|
2396
2311
|
{
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2312
|
+
href: link.url,
|
|
2313
|
+
target: "_blank",
|
|
2314
|
+
rel: "noopener noreferrer",
|
|
2315
|
+
style: { color: theme.muted, textDecoration: "none", fontSize: "0.85rem", fontWeight: 500, textTransform: "capitalize", letterSpacing: "0.03em" },
|
|
2316
|
+
children: link.platform
|
|
2317
|
+
},
|
|
2318
|
+
i
|
|
2319
|
+
)) });
|
|
2320
|
+
}
|
|
2321
|
+
function DividerSection({ theme }) {
|
|
2322
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1rem 1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } }) });
|
|
2323
|
+
}
|
|
2324
|
+
function toEmbedUrl(url) {
|
|
2325
|
+
const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
|
|
2326
|
+
if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
|
|
2327
|
+
const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
|
|
2328
|
+
if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
|
|
2329
|
+
return url;
|
|
2402
2330
|
}
|
|
2403
2331
|
function CTASection({ section, theme, tracking, onEvent }) {
|
|
2404
2332
|
const { title, subtitle, buttons } = section.content;
|
|
@@ -2461,29 +2389,15 @@ function StatsSection({ section, theme }) {
|
|
|
2461
2389
|
if (!stats || stats.length === 0) return null;
|
|
2462
2390
|
if (layout === "list") {
|
|
2463
2391
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: stats.map((stat, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2464
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
2465
|
-
|
|
2466
|
-
justifyContent: "space-between",
|
|
2467
|
-
alignItems: "baseline",
|
|
2468
|
-
padding: "0.625rem 0"
|
|
2469
|
-
}, children: [
|
|
2470
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
2471
|
-
fontSize: 12,
|
|
2472
|
-
textTransform: "uppercase",
|
|
2473
|
-
letterSpacing: "0.15em",
|
|
2474
|
-
color: `${theme.fg}66`
|
|
2475
|
-
}, children: stat.label }),
|
|
2392
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "baseline", padding: "0.625rem 0" }, children: [
|
|
2393
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, textTransform: "uppercase", letterSpacing: "0.15em", color: `${theme.fg}66` }, children: stat.label }),
|
|
2476
2394
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 14, fontWeight: 300, color: `${theme.fg}CC` }, children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedText, { text: stat.value }) })
|
|
2477
2395
|
] }),
|
|
2478
2396
|
i < stats.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: `1px solid ${theme.fg}0A`, margin: 0 } })
|
|
2479
2397
|
] }, i)) });
|
|
2480
2398
|
}
|
|
2481
2399
|
const columns = Math.min(stats.length, 4);
|
|
2482
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
2483
|
-
display: "grid",
|
|
2484
|
-
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
2485
|
-
border: `1px solid ${theme.fg}0F`
|
|
2486
|
-
}, children: stats.map((stat, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
2400
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", maxWidth: 640, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: `repeat(${columns}, 1fr)`, border: `1px solid ${theme.fg}0F` }, children: stats.map((stat, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
2487
2401
|
padding: "1.25rem 0.5rem",
|
|
2488
2402
|
textAlign: "center",
|
|
2489
2403
|
borderRight: i < stats.length - 1 ? `1px solid ${theme.fg}0F` : void 0
|
|
@@ -2542,13 +2456,7 @@ function ProductCardSection({ section, data, theme, tracking }) {
|
|
|
2542
2456
|
] })
|
|
2543
2457
|
] }) });
|
|
2544
2458
|
}
|
|
2545
|
-
function COAViewerSection({
|
|
2546
|
-
section,
|
|
2547
|
-
data,
|
|
2548
|
-
theme,
|
|
2549
|
-
onShowCOA,
|
|
2550
|
-
tracking
|
|
2551
|
-
}) {
|
|
2459
|
+
function COAViewerSection({ section, data, theme, onShowCOA, tracking }) {
|
|
2552
2460
|
const coa = data?.coa;
|
|
2553
2461
|
const c = section.content;
|
|
2554
2462
|
if (!coa) return null;
|
|
@@ -2587,6 +2495,28 @@ function COAViewerSection({
|
|
|
2587
2495
|
onShowCOA();
|
|
2588
2496
|
}, style: buttonStyle, children: buttonLabel }) });
|
|
2589
2497
|
}
|
|
2498
|
+
function COAModal({ coa, theme, onClose }) {
|
|
2499
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "fixed", inset: 0, zIndex: 9999, background: "rgba(0,0,0,0.95)", display: "flex", flexDirection: "column" }, children: [
|
|
2500
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
|
|
2501
|
+
display: "flex",
|
|
2502
|
+
justifyContent: "space-between",
|
|
2503
|
+
alignItems: "center",
|
|
2504
|
+
padding: "0.75rem 1rem",
|
|
2505
|
+
borderBottom: `1px solid ${theme.fg}10`
|
|
2506
|
+
}, children: [
|
|
2507
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fff", fontWeight: 500, fontSize: "0.85rem" }, children: coa.document_name || "Lab Results" }),
|
|
2508
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onClose, style: {
|
|
2509
|
+
background: `${theme.fg}10`,
|
|
2510
|
+
border: "none",
|
|
2511
|
+
color: "#fff",
|
|
2512
|
+
fontSize: "0.85rem",
|
|
2513
|
+
cursor: "pointer",
|
|
2514
|
+
padding: "0.375rem 0.75rem"
|
|
2515
|
+
}, children: "Close" })
|
|
2516
|
+
] }),
|
|
2517
|
+
/* @__PURE__ */ jsxRuntime.jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
|
|
2518
|
+
] });
|
|
2519
|
+
}
|
|
2590
2520
|
function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
2591
2521
|
const c = section.content;
|
|
2592
2522
|
const [firstName, setFirstName] = react.useState("");
|
|
@@ -2597,13 +2527,17 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
|
2597
2527
|
const gatewayUrl = c.gateway_url || data.gatewayUrl || "https://whale-gateway.fly.dev";
|
|
2598
2528
|
const storeId = c.store_id || data.store?.id;
|
|
2599
2529
|
const slug = c.landing_page_slug || data.landing_page?.slug;
|
|
2530
|
+
const heading = c.heading || "get 10% off your first visit.";
|
|
2531
|
+
const subtitle = c.subtitle || "drop your email and we will send you the code.";
|
|
2532
|
+
const buttonText = c.button_text || "Claim My Discount";
|
|
2533
|
+
const successHeading = c.success_heading || "You\u2019re in!";
|
|
2534
|
+
const successMessage = c.success_message || "Check your inbox for the discount code.";
|
|
2600
2535
|
async function handleSubmit(e) {
|
|
2601
2536
|
e.preventDefault();
|
|
2602
2537
|
if (!email || !storeId) return;
|
|
2603
2538
|
setStatus("loading");
|
|
2604
2539
|
setErrorMsg("");
|
|
2605
2540
|
const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
|
|
2606
|
-
const analyticsData = data.analyticsContext;
|
|
2607
2541
|
try {
|
|
2608
2542
|
const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
|
|
2609
2543
|
method: "POST",
|
|
@@ -2619,8 +2553,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
|
2619
2553
|
if (newsletterOptIn) t.push(c.newsletter_tag || "newsletter-subscriber");
|
|
2620
2554
|
return t.length > 0 ? t : void 0;
|
|
2621
2555
|
})(),
|
|
2622
|
-
visitor_id:
|
|
2623
|
-
session_id:
|
|
2556
|
+
visitor_id: data.analyticsContext?.visitorId || void 0,
|
|
2557
|
+
session_id: data.analyticsContext?.sessionId || void 0,
|
|
2624
2558
|
utm_source: urlParams?.get("utm_source") || void 0,
|
|
2625
2559
|
utm_medium: urlParams?.get("utm_medium") || void 0,
|
|
2626
2560
|
utm_campaign: urlParams?.get("utm_campaign") || void 0,
|
|
@@ -2638,11 +2572,6 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
|
2638
2572
|
setStatus("error");
|
|
2639
2573
|
}
|
|
2640
2574
|
}
|
|
2641
|
-
const heading = c.heading || "get 10% off your first visit.";
|
|
2642
|
-
const subtitle = c.subtitle || "drop your email and we will send you the code.";
|
|
2643
|
-
const buttonText = c.button_text || "Claim My Discount";
|
|
2644
|
-
const successHeading = c.success_heading || "You\u2019re in!";
|
|
2645
|
-
const successMessage = c.success_message || "Check your inbox for the discount code.";
|
|
2646
2575
|
const inputStyle = {
|
|
2647
2576
|
flex: 1,
|
|
2648
2577
|
minWidth: 0,
|
|
@@ -2657,41 +2586,10 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
|
2657
2586
|
fontFamily: "inherit",
|
|
2658
2587
|
transition: "border-color 0.2s"
|
|
2659
2588
|
};
|
|
2589
|
+
if (status === "success") return /* @__PURE__ */ jsxRuntime.jsx(SuccessState, { theme, heading: successHeading, message: successMessage, couponCode: c.coupon_code });
|
|
2660
2590
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "3.5rem 1.5rem", maxWidth: 560, margin: "0 auto" }, children: [
|
|
2661
2591
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `@keyframes lc-spin { to { transform: rotate(360deg) } }` }),
|
|
2662
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2663
|
-
background: theme.surface,
|
|
2664
|
-
border: `1px solid ${theme.fg}12`,
|
|
2665
|
-
padding: "clamp(2rem, 6vw, 3rem)"
|
|
2666
|
-
}, children: status === "success" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [
|
|
2667
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: {
|
|
2668
|
-
fontSize: "clamp(1.5rem, 5vw, 2rem)",
|
|
2669
|
-
fontWeight: 300,
|
|
2670
|
-
fontFamily: theme.fontDisplay || "inherit",
|
|
2671
|
-
margin: "0 0 0.75rem",
|
|
2672
|
-
lineHeight: 1.2,
|
|
2673
|
-
letterSpacing: "-0.02em",
|
|
2674
|
-
color: theme.fg
|
|
2675
|
-
}, children: successHeading }),
|
|
2676
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: {
|
|
2677
|
-
fontSize: "0.9rem",
|
|
2678
|
-
color: `${theme.fg}99`,
|
|
2679
|
-
margin: "0 0 1.5rem",
|
|
2680
|
-
lineHeight: 1.6,
|
|
2681
|
-
fontWeight: 300
|
|
2682
|
-
}, children: successMessage }),
|
|
2683
|
-
c.coupon_code && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
2684
|
-
display: "inline-block",
|
|
2685
|
-
padding: "0.75rem 2rem",
|
|
2686
|
-
background: `${theme.fg}08`,
|
|
2687
|
-
border: `1px dashed ${theme.fg}30`,
|
|
2688
|
-
fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
|
|
2689
|
-
fontWeight: 500,
|
|
2690
|
-
fontFamily: "monospace",
|
|
2691
|
-
letterSpacing: "0.12em",
|
|
2692
|
-
color: theme.accent
|
|
2693
|
-
}, children: c.coupon_code })
|
|
2694
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2592
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { background: theme.surface, border: `1px solid ${theme.fg}12`, padding: "clamp(2rem, 6vw, 3rem)" }, children: [
|
|
2695
2593
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "clamp(1.5rem, 4vw, 2rem)" }, children: [
|
|
2696
2594
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: {
|
|
2697
2595
|
fontSize: "clamp(1.5rem, 5vw, 2.25rem)",
|
|
@@ -2713,27 +2611,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
|
2713
2611
|
] }),
|
|
2714
2612
|
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
|
|
2715
2613
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.75rem", flexWrap: "wrap" }, children: [
|
|
2716
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2717
|
-
|
|
2718
|
-
{
|
|
2719
|
-
type: "text",
|
|
2720
|
-
placeholder: "First name",
|
|
2721
|
-
value: firstName,
|
|
2722
|
-
onChange: (e) => setFirstName(e.target.value),
|
|
2723
|
-
style: inputStyle
|
|
2724
|
-
}
|
|
2725
|
-
),
|
|
2726
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2727
|
-
"input",
|
|
2728
|
-
{
|
|
2729
|
-
type: "email",
|
|
2730
|
-
placeholder: "Email address",
|
|
2731
|
-
value: email,
|
|
2732
|
-
onChange: (e) => setEmail(e.target.value),
|
|
2733
|
-
required: true,
|
|
2734
|
-
style: inputStyle
|
|
2735
|
-
}
|
|
2736
|
-
)
|
|
2614
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "text", placeholder: "First name", value: firstName, onChange: (e) => setFirstName(e.target.value), style: inputStyle }),
|
|
2615
|
+
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "email", placeholder: "Email address", value: email, onChange: (e) => setEmail(e.target.value), required: true, style: inputStyle })
|
|
2737
2616
|
] }),
|
|
2738
2617
|
c.show_newsletter_opt_in !== false && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: {
|
|
2739
2618
|
display: "flex",
|
|
@@ -2751,121 +2630,130 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
|
|
|
2751
2630
|
type: "checkbox",
|
|
2752
2631
|
checked: newsletterOptIn,
|
|
2753
2632
|
onChange: (e) => setNewsletterOptIn(e.target.checked),
|
|
2754
|
-
style: {
|
|
2755
|
-
width: 16,
|
|
2756
|
-
height: 16,
|
|
2757
|
-
accentColor: theme.accent,
|
|
2758
|
-
cursor: "pointer",
|
|
2759
|
-
flexShrink: 0
|
|
2760
|
-
}
|
|
2633
|
+
style: { width: 16, height: 16, accentColor: theme.accent, cursor: "pointer", flexShrink: 0 }
|
|
2761
2634
|
}
|
|
2762
2635
|
),
|
|
2763
2636
|
c.newsletter_label || "Also sign me up for the newsletter \u2014 new drops, deals, and company news."
|
|
2764
2637
|
] }),
|
|
2765
2638
|
status === "error" && errorMsg && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8rem", color: "#e55", margin: 0, fontWeight: 400 }, children: errorMsg }),
|
|
2766
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2767
|
-
"
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
}
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
borderTopColor: theme.bg,
|
|
2797
|
-
borderRadius: "50%",
|
|
2798
|
-
animation: "lc-spin 0.8s linear infinite"
|
|
2799
|
-
} }),
|
|
2800
|
-
buttonText
|
|
2801
|
-
]
|
|
2802
|
-
}
|
|
2803
|
-
)
|
|
2639
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "submit", disabled: status === "loading", style: {
|
|
2640
|
+
width: "100%",
|
|
2641
|
+
padding: "0.875rem",
|
|
2642
|
+
background: theme.fg,
|
|
2643
|
+
color: theme.bg,
|
|
2644
|
+
border: "none",
|
|
2645
|
+
fontSize: "0.85rem",
|
|
2646
|
+
fontWeight: 500,
|
|
2647
|
+
cursor: status === "loading" ? "wait" : "pointer",
|
|
2648
|
+
letterSpacing: "0.08em",
|
|
2649
|
+
textTransform: "uppercase",
|
|
2650
|
+
fontFamily: "inherit",
|
|
2651
|
+
display: "flex",
|
|
2652
|
+
alignItems: "center",
|
|
2653
|
+
justifyContent: "center",
|
|
2654
|
+
gap: "0.5rem",
|
|
2655
|
+
opacity: status === "loading" ? 0.7 : 1,
|
|
2656
|
+
transition: "opacity 0.2s"
|
|
2657
|
+
}, children: [
|
|
2658
|
+
status === "loading" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
|
|
2659
|
+
display: "inline-block",
|
|
2660
|
+
width: 16,
|
|
2661
|
+
height: 16,
|
|
2662
|
+
border: `2px solid ${theme.bg}40`,
|
|
2663
|
+
borderTopColor: theme.bg,
|
|
2664
|
+
borderRadius: "50%",
|
|
2665
|
+
animation: "lc-spin 0.8s linear infinite"
|
|
2666
|
+
} }),
|
|
2667
|
+
buttonText
|
|
2668
|
+
] })
|
|
2804
2669
|
] })
|
|
2805
|
-
] })
|
|
2670
|
+
] })
|
|
2806
2671
|
] });
|
|
2807
2672
|
}
|
|
2808
|
-
function
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
}
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2673
|
+
function SuccessState({ theme, heading, message, couponCode }) {
|
|
2674
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "3.5rem 1.5rem", maxWidth: 560, margin: "0 auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { background: theme.surface, border: `1px solid ${theme.fg}12`, padding: "clamp(2rem, 6vw, 3rem)", textAlign: "center" }, children: [
|
|
2675
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: {
|
|
2676
|
+
fontSize: "clamp(1.5rem, 5vw, 2rem)",
|
|
2677
|
+
fontWeight: 300,
|
|
2678
|
+
fontFamily: theme.fontDisplay || "inherit",
|
|
2679
|
+
margin: "0 0 0.75rem",
|
|
2680
|
+
lineHeight: 1.2,
|
|
2681
|
+
letterSpacing: "-0.02em",
|
|
2682
|
+
color: theme.fg
|
|
2683
|
+
}, children: heading }),
|
|
2684
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.9rem", color: `${theme.fg}99`, margin: "0 0 1.5rem", lineHeight: 1.6, fontWeight: 300 }, children: message }),
|
|
2685
|
+
couponCode && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
|
|
2686
|
+
display: "inline-block",
|
|
2687
|
+
padding: "0.75rem 2rem",
|
|
2688
|
+
background: `${theme.fg}08`,
|
|
2689
|
+
border: `1px dashed ${theme.fg}30`,
|
|
2690
|
+
fontSize: "clamp(1.25rem, 4vw, 1.75rem)",
|
|
2691
|
+
fontWeight: 500,
|
|
2692
|
+
fontFamily: "monospace",
|
|
2693
|
+
letterSpacing: "0.12em",
|
|
2694
|
+
color: theme.accent
|
|
2695
|
+
}, children: couponCode })
|
|
2696
|
+
] }) });
|
|
2832
2697
|
}
|
|
2833
|
-
function
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2698
|
+
function SectionRenderer({
|
|
2699
|
+
section,
|
|
2700
|
+
data,
|
|
2701
|
+
theme,
|
|
2702
|
+
tracking,
|
|
2703
|
+
onEvent
|
|
2704
|
+
}) {
|
|
2705
|
+
const [showCOA, setShowCOA] = react.useState(false);
|
|
2706
|
+
const el = (() => {
|
|
2707
|
+
switch (section.type) {
|
|
2708
|
+
case "hero":
|
|
2709
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HeroSection, { section, theme, tracking, onEvent });
|
|
2710
|
+
case "text":
|
|
2711
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TextSection, { section, theme });
|
|
2712
|
+
case "image":
|
|
2713
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ImageSection, { section, theme });
|
|
2714
|
+
case "video":
|
|
2715
|
+
return /* @__PURE__ */ jsxRuntime.jsx(VideoSection, { section, theme });
|
|
2716
|
+
case "gallery":
|
|
2717
|
+
return /* @__PURE__ */ jsxRuntime.jsx(GallerySection, { section, theme });
|
|
2718
|
+
case "cta":
|
|
2719
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CTASection, { section, theme, tracking, onEvent });
|
|
2720
|
+
case "stats":
|
|
2721
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StatsSection, { section, theme });
|
|
2722
|
+
case "product_card":
|
|
2723
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ProductCardSection, { section, data, theme, tracking });
|
|
2724
|
+
case "coa_viewer":
|
|
2725
|
+
return /* @__PURE__ */ jsxRuntime.jsx(COAViewerSection, { section, data, theme, onShowCOA: () => setShowCOA(true), tracking });
|
|
2726
|
+
case "social_links":
|
|
2727
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SocialLinksSection, { section, theme });
|
|
2728
|
+
case "lead_capture":
|
|
2729
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LeadCaptureSection, { section, data, theme, onEvent });
|
|
2730
|
+
case "divider":
|
|
2731
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DividerSection, { theme });
|
|
2732
|
+
default:
|
|
2733
|
+
return null;
|
|
2734
|
+
}
|
|
2735
|
+
})();
|
|
2736
|
+
const sectionRef = react.useRef(null);
|
|
2737
|
+
react.useEffect(() => {
|
|
2738
|
+
const el2 = sectionRef.current;
|
|
2739
|
+
if (!el2 || typeof IntersectionObserver === "undefined") return;
|
|
2740
|
+
const obs = new IntersectionObserver(
|
|
2741
|
+
([entry]) => {
|
|
2742
|
+
if (entry.isIntersecting) {
|
|
2743
|
+
onEvent?.("section_view", { section_id: section.id, section_type: section.type });
|
|
2744
|
+
obs.disconnect();
|
|
2856
2745
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2746
|
+
},
|
|
2747
|
+
{ threshold: 0.5 }
|
|
2748
|
+
);
|
|
2749
|
+
obs.observe(el2);
|
|
2750
|
+
return () => obs.disconnect();
|
|
2751
|
+
}, [section.id, section.type, onEvent]);
|
|
2752
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: sectionRef, "data-section-id": section.id, "data-section-type": section.type, children: [
|
|
2753
|
+
el,
|
|
2754
|
+
showCOA && data?.coa && /* @__PURE__ */ jsxRuntime.jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
|
|
2860
2755
|
] });
|
|
2861
2756
|
}
|
|
2862
|
-
function toEmbedUrl(url) {
|
|
2863
|
-
const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([\w-]+)/);
|
|
2864
|
-
if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`;
|
|
2865
|
-
const vimeoMatch = url.match(/vimeo\.com\/(\d+)/);
|
|
2866
|
-
if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`;
|
|
2867
|
-
return url;
|
|
2868
|
-
}
|
|
2869
2757
|
function QRLandingPage({
|
|
2870
2758
|
code,
|
|
2871
2759
|
gatewayUrl = "https://whale-gateway.fly.dev",
|
|
@@ -3164,6 +3052,12 @@ function DefaultError({ message }) {
|
|
|
3164
3052
|
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#888" }, children: message || "Please try scanning again." })
|
|
3165
3053
|
] }) });
|
|
3166
3054
|
}
|
|
3055
|
+
function getInlinedData() {
|
|
3056
|
+
if (typeof window !== "undefined" && window.__LANDING_DATA__) {
|
|
3057
|
+
return window.__LANDING_DATA__;
|
|
3058
|
+
}
|
|
3059
|
+
return null;
|
|
3060
|
+
}
|
|
3167
3061
|
function LandingPage({
|
|
3168
3062
|
slug,
|
|
3169
3063
|
gatewayUrl = "https://whale-gateway.fly.dev",
|
|
@@ -3174,47 +3068,43 @@ function LandingPage({
|
|
|
3174
3068
|
analyticsContext,
|
|
3175
3069
|
enableAnalytics = true
|
|
3176
3070
|
}) {
|
|
3177
|
-
const
|
|
3178
|
-
const [
|
|
3071
|
+
const inlined = react.useRef(getInlinedData()).current;
|
|
3072
|
+
const [state, setState] = react.useState(inlined ? "ready" : "loading");
|
|
3073
|
+
const [data, setData] = react.useState(inlined);
|
|
3179
3074
|
const [errorMsg, setErrorMsg] = react.useState("");
|
|
3180
3075
|
react.useEffect(() => {
|
|
3181
3076
|
if (!slug) return;
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
const json = window.__LANDING_DATA__;
|
|
3185
|
-
setData(json);
|
|
3186
|
-
setState("ready");
|
|
3187
|
-
onDataLoaded?.(json);
|
|
3077
|
+
if (data) {
|
|
3078
|
+
onDataLoaded?.(data);
|
|
3188
3079
|
return;
|
|
3189
3080
|
}
|
|
3081
|
+
let cancelled = false;
|
|
3190
3082
|
async function load() {
|
|
3191
3083
|
try {
|
|
3192
3084
|
const res = await fetch(`${gatewayUrl}/l/${encodeURIComponent(slug)}`);
|
|
3193
|
-
if (
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
}
|
|
3198
|
-
if (res.status === 410) {
|
|
3199
|
-
setState("expired");
|
|
3200
|
-
return;
|
|
3201
|
-
}
|
|
3202
|
-
if (!res.ok) {
|
|
3203
|
-
const body = await res.json().catch(() => ({}));
|
|
3204
|
-
throw new Error(body?.error?.message ?? `Failed to load: ${res.status}`);
|
|
3205
|
-
}
|
|
3206
|
-
const json = await res.json();
|
|
3207
|
-
setData(json);
|
|
3208
|
-
setState("ready");
|
|
3209
|
-
onDataLoaded?.(json);
|
|
3085
|
+
if (cancelled) return;
|
|
3086
|
+
if (res.status === 404) {
|
|
3087
|
+
setState("not_found");
|
|
3088
|
+
return;
|
|
3210
3089
|
}
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3090
|
+
if (res.status === 410) {
|
|
3091
|
+
setState("expired");
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
if (!res.ok) {
|
|
3095
|
+
const body = await res.json().catch(() => ({}));
|
|
3096
|
+
throw new Error(body?.error?.message ?? `Failed to load: ${res.status}`);
|
|
3217
3097
|
}
|
|
3098
|
+
const json = await res.json();
|
|
3099
|
+
setData(json);
|
|
3100
|
+
setState("ready");
|
|
3101
|
+
onDataLoaded?.(json);
|
|
3102
|
+
} catch (err) {
|
|
3103
|
+
if (cancelled) return;
|
|
3104
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
3105
|
+
setErrorMsg(e.message);
|
|
3106
|
+
setState("error");
|
|
3107
|
+
onError?.(e);
|
|
3218
3108
|
}
|
|
3219
3109
|
}
|
|
3220
3110
|
load();
|
|
@@ -3222,12 +3112,22 @@ function LandingPage({
|
|
|
3222
3112
|
cancelled = true;
|
|
3223
3113
|
};
|
|
3224
3114
|
}, [slug, gatewayUrl]);
|
|
3225
|
-
if (state === "loading") return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3226
|
-
if (state === "not_found") return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3227
|
-
if (state === "expired") return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3228
|
-
if (state === "error") return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3115
|
+
if (state === "loading") return /* @__PURE__ */ jsxRuntime.jsx(StateScreen, { title: "", loading: true });
|
|
3116
|
+
if (state === "not_found") return /* @__PURE__ */ jsxRuntime.jsx(StateScreen, { title: "Page Not Found", subtitle: "This page does not exist or has been removed." });
|
|
3117
|
+
if (state === "expired") return /* @__PURE__ */ jsxRuntime.jsx(StateScreen, { title: "Page Expired", subtitle: "This page is no longer active." });
|
|
3118
|
+
if (state === "error") return /* @__PURE__ */ jsxRuntime.jsx(StateScreen, { title: "Something Went Wrong", subtitle: errorMsg || "Please try again later." });
|
|
3229
3119
|
if (!data) return null;
|
|
3230
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3120
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3121
|
+
PageLayout,
|
|
3122
|
+
{
|
|
3123
|
+
data,
|
|
3124
|
+
gatewayUrl,
|
|
3125
|
+
renderSection,
|
|
3126
|
+
onEvent,
|
|
3127
|
+
analyticsContext,
|
|
3128
|
+
enableAnalytics
|
|
3129
|
+
}
|
|
3130
|
+
);
|
|
3231
3131
|
}
|
|
3232
3132
|
function isSectionVisible(section, urlParams) {
|
|
3233
3133
|
const vis = section.config?.visibility;
|
|
@@ -3250,8 +3150,8 @@ function PageLayout({
|
|
|
3250
3150
|
const trackerRef = react.useRef(null);
|
|
3251
3151
|
react.useEffect(() => {
|
|
3252
3152
|
if (!enableAnalytics || typeof window === "undefined") return;
|
|
3253
|
-
const
|
|
3254
|
-
if (!
|
|
3153
|
+
const config = window.__LANDING_ANALYTICS__;
|
|
3154
|
+
if (!config?.slug) return;
|
|
3255
3155
|
let visitorId = localStorage.getItem("wt_vid") || "";
|
|
3256
3156
|
if (!visitorId) {
|
|
3257
3157
|
visitorId = crypto.randomUUID();
|
|
@@ -3263,8 +3163,8 @@ function PageLayout({
|
|
|
3263
3163
|
sessionStorage.setItem("wt_sid", sessionId);
|
|
3264
3164
|
}
|
|
3265
3165
|
import('../tracker-WYKTEQ4F.cjs').then(({ BehavioralTracker: BehavioralTracker2 }) => {
|
|
3266
|
-
const gwUrl =
|
|
3267
|
-
const slug =
|
|
3166
|
+
const gwUrl = config.gatewayUrl || gatewayUrl;
|
|
3167
|
+
const slug = config.slug;
|
|
3268
3168
|
const utmParams = new URLSearchParams(window.location.search);
|
|
3269
3169
|
const tracker = new BehavioralTracker2({
|
|
3270
3170
|
sessionId,
|
|
@@ -3275,65 +3175,44 @@ function PageLayout({
|
|
|
3275
3175
|
event_data: e.data,
|
|
3276
3176
|
session_id: batch.session_id,
|
|
3277
3177
|
visitor_id: batch.visitor_id,
|
|
3278
|
-
campaign_id:
|
|
3178
|
+
campaign_id: config.campaignId || utmParams.get("utm_campaign_id") || void 0,
|
|
3279
3179
|
utm_source: utmParams.get("utm_source") || void 0,
|
|
3280
3180
|
utm_medium: utmParams.get("utm_medium") || void 0,
|
|
3281
3181
|
utm_campaign: utmParams.get("utm_campaign") || void 0
|
|
3282
3182
|
}));
|
|
3283
|
-
|
|
3284
|
-
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
3285
|
-
navigator.sendBeacon(
|
|
3286
|
-
`${gwUrl}/l/${encodeURIComponent(slug)}/events`,
|
|
3287
|
-
new Blob([body], { type: "application/json" })
|
|
3288
|
-
);
|
|
3289
|
-
} else {
|
|
3290
|
-
await fetch(`${gwUrl}/l/${encodeURIComponent(slug)}/events`, {
|
|
3291
|
-
method: "POST",
|
|
3292
|
-
headers: { "Content-Type": "application/json" },
|
|
3293
|
-
body,
|
|
3294
|
-
keepalive: true
|
|
3295
|
-
});
|
|
3296
|
-
}
|
|
3183
|
+
sendEvents(gwUrl, slug, events);
|
|
3297
3184
|
}
|
|
3298
3185
|
});
|
|
3299
3186
|
tracker.setPageContext(window.location.href, window.location.pathname);
|
|
3300
3187
|
tracker.start();
|
|
3301
3188
|
trackerRef.current = tracker;
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
utm_source: utmParams.get("utm_source") || void 0,
|
|
3310
|
-
utm_medium: utmParams.get("utm_medium") || void 0,
|
|
3311
|
-
utm_campaign: utmParams.get("utm_campaign") || void 0
|
|
3312
|
-
}]
|
|
3313
|
-
});
|
|
3314
|
-
if (navigator.sendBeacon) {
|
|
3315
|
-
navigator.sendBeacon(
|
|
3316
|
-
`${gwUrl}/l/${encodeURIComponent(slug)}/events`,
|
|
3317
|
-
new Blob([pageViewBody], { type: "application/json" })
|
|
3318
|
-
);
|
|
3319
|
-
} else {
|
|
3320
|
-
fetch(`${gwUrl}/l/${encodeURIComponent(slug)}/events`, {
|
|
3321
|
-
method: "POST",
|
|
3322
|
-
headers: { "Content-Type": "application/json" },
|
|
3323
|
-
body: pageViewBody,
|
|
3324
|
-
keepalive: true
|
|
3325
|
-
}).catch(() => {
|
|
3326
|
-
});
|
|
3327
|
-
}
|
|
3189
|
+
sendEvents(gwUrl, slug, [{
|
|
3190
|
+
event_type: "page_view",
|
|
3191
|
+
event_data: { referrer: document.referrer, url: window.location.href },
|
|
3192
|
+
session_id: sessionId,
|
|
3193
|
+
visitor_id: visitorId,
|
|
3194
|
+
campaign_id: config.campaignId || void 0
|
|
3195
|
+
}]);
|
|
3328
3196
|
}).catch(() => {
|
|
3329
3197
|
});
|
|
3330
3198
|
return () => {
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
trackerRef.current = null;
|
|
3334
|
-
}
|
|
3199
|
+
trackerRef.current?.stop();
|
|
3200
|
+
trackerRef.current = null;
|
|
3335
3201
|
};
|
|
3336
3202
|
}, [enableAnalytics, gatewayUrl]);
|
|
3203
|
+
const handleEvent = react.useCallback((event, eventData) => {
|
|
3204
|
+
onEvent?.(event, eventData);
|
|
3205
|
+
if (!enableAnalytics || typeof window === "undefined") return;
|
|
3206
|
+
const config = window.__LANDING_ANALYTICS__;
|
|
3207
|
+
if (!config?.slug) return;
|
|
3208
|
+
sendEvents(config.gatewayUrl || gatewayUrl, config.slug, [{
|
|
3209
|
+
event_type: event,
|
|
3210
|
+
event_data: eventData,
|
|
3211
|
+
session_id: sessionStorage.getItem("wt_sid") || void 0,
|
|
3212
|
+
visitor_id: localStorage.getItem("wt_vid") || void 0,
|
|
3213
|
+
campaign_id: config.campaignId || void 0
|
|
3214
|
+
}]);
|
|
3215
|
+
}, [onEvent, enableAnalytics, gatewayUrl]);
|
|
3337
3216
|
const theme = {
|
|
3338
3217
|
bg: lp.background_color || store?.theme?.background || "#050505",
|
|
3339
3218
|
fg: lp.text_color || store?.theme?.foreground || "#fafafa",
|
|
@@ -3352,11 +3231,9 @@ function PageLayout({
|
|
|
3352
3231
|
lp.custom_css && /* @__PURE__ */ jsxRuntime.jsx("style", { children: lp.custom_css }),
|
|
3353
3232
|
logoUrl && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: logoUrl, alt: store?.name || "Store", style: { height: 40, objectFit: "contain" } }) }),
|
|
3354
3233
|
sorted.map((section) => {
|
|
3355
|
-
const defaultRenderer = () => /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
|
|
3356
|
-
if (renderSection) {
|
|
3357
|
-
|
|
3358
|
-
}
|
|
3359
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
|
|
3234
|
+
const defaultRenderer = () => /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme, onEvent: handleEvent }, section.id);
|
|
3235
|
+
if (renderSection) return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
|
|
3236
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme, onEvent: handleEvent }, section.id);
|
|
3360
3237
|
}),
|
|
3361
3238
|
store?.name && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "2rem 1.5rem", borderTop: `1px solid ${theme.surface}`, textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { fontSize: "0.75rem", color: theme.muted, margin: 0 }, children: [
|
|
3362
3239
|
"Powered by ",
|
|
@@ -3364,7 +3241,17 @@ function PageLayout({
|
|
|
3364
3241
|
] }) })
|
|
3365
3242
|
] });
|
|
3366
3243
|
}
|
|
3367
|
-
|
|
3244
|
+
function sendEvents(gwUrl, slug, events) {
|
|
3245
|
+
const body = JSON.stringify({ events });
|
|
3246
|
+
const url = `${gwUrl}/l/${encodeURIComponent(slug)}/events`;
|
|
3247
|
+
if (typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
3248
|
+
navigator.sendBeacon(url, new Blob([body], { type: "application/json" }));
|
|
3249
|
+
} else {
|
|
3250
|
+
fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body, keepalive: true }).catch(() => {
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
var screenStyle = {
|
|
3368
3255
|
minHeight: "100dvh",
|
|
3369
3256
|
display: "flex",
|
|
3370
3257
|
justifyContent: "center",
|
|
@@ -3375,28 +3262,14 @@ var containerStyle2 = {
|
|
|
3375
3262
|
textAlign: "center",
|
|
3376
3263
|
padding: "2rem"
|
|
3377
3264
|
};
|
|
3378
|
-
function
|
|
3379
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { style:
|
|
3380
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
}
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Page Not Found" }),
|
|
3387
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#888" }, children: "This page does not exist or has been removed." })
|
|
3388
|
-
] }) });
|
|
3389
|
-
}
|
|
3390
|
-
function DefaultExpired2() {
|
|
3391
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle2, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3392
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Page Expired" }),
|
|
3393
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#888" }, children: "This page is no longer active." })
|
|
3394
|
-
] }) });
|
|
3395
|
-
}
|
|
3396
|
-
function DefaultError2({ message }) {
|
|
3397
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: containerStyle2, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3398
|
-
/* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: "Something Went Wrong" }),
|
|
3399
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#888" }, children: message || "Please try again later." })
|
|
3265
|
+
function StateScreen({ title, subtitle, loading }) {
|
|
3266
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: screenStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3267
|
+
loading && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3268
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 32, height: 32, border: "2px solid #333", borderTopColor: "#fafafa", borderRadius: "50%", animation: "spin 0.8s linear infinite", margin: "0 auto 1rem" } }),
|
|
3269
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg) } }` })
|
|
3270
|
+
] }),
|
|
3271
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "1.5rem", marginBottom: "0.5rem" }, children: title }),
|
|
3272
|
+
subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#888" }, children: subtitle })
|
|
3400
3273
|
] }) });
|
|
3401
3274
|
}
|
|
3402
3275
|
|