@neowhale/storefront 0.2.30 → 0.2.31

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.
@@ -236,6 +236,8 @@ declare function useAnalytics(): {
236
236
  trackingEnabled: boolean;
237
237
  /** Configured recording sample rate (0–1) for behavioral session replays */
238
238
  recordingRate: number;
239
+ /** Stable visitor ID for cross-session attribution */
240
+ visitorId: string;
239
241
  };
240
242
 
241
243
  declare function useWhaleClient(): WhaleClient;
@@ -430,8 +432,12 @@ interface LandingPageProps {
430
432
  onDataLoaded?: (data: LandingPageRenderData) => void;
431
433
  onError?: (error: Error) => void;
432
434
  onEvent?: (event: string, data: Record<string, unknown>) => void;
435
+ analyticsContext?: {
436
+ visitorId?: string;
437
+ sessionId?: string;
438
+ };
433
439
  }
434
- declare function LandingPage({ slug, gatewayUrl, renderSection, onDataLoaded, onError, onEvent, }: LandingPageProps): react_jsx_runtime.JSX.Element | null;
440
+ declare function LandingPage({ slug, gatewayUrl, renderSection, onDataLoaded, onError, onEvent, analyticsContext, }: LandingPageProps): react_jsx_runtime.JSX.Element | null;
435
441
 
436
442
  interface SectionTheme {
437
443
  bg: string;
@@ -236,6 +236,8 @@ declare function useAnalytics(): {
236
236
  trackingEnabled: boolean;
237
237
  /** Configured recording sample rate (0–1) for behavioral session replays */
238
238
  recordingRate: number;
239
+ /** Stable visitor ID for cross-session attribution */
240
+ visitorId: string;
239
241
  };
240
242
 
241
243
  declare function useWhaleClient(): WhaleClient;
@@ -430,8 +432,12 @@ interface LandingPageProps {
430
432
  onDataLoaded?: (data: LandingPageRenderData) => void;
431
433
  onError?: (error: Error) => void;
432
434
  onEvent?: (event: string, data: Record<string, unknown>) => void;
435
+ analyticsContext?: {
436
+ visitorId?: string;
437
+ sessionId?: string;
438
+ };
433
439
  }
434
- declare function LandingPage({ slug, gatewayUrl, renderSection, onDataLoaded, onError, onEvent, }: LandingPageProps): react_jsx_runtime.JSX.Element | null;
440
+ declare function LandingPage({ slug, gatewayUrl, renderSection, onDataLoaded, onError, onEvent, analyticsContext, }: LandingPageProps): react_jsx_runtime.JSX.Element | null;
435
441
 
436
442
  interface SectionTheme {
437
443
  bg: string;
@@ -553,7 +553,9 @@ function useAnalytics() {
553
553
  /** Whether tracking is globally enabled for this storefront */
554
554
  trackingEnabled,
555
555
  /** Configured recording sample rate (0–1) for behavioral session replays */
556
- recordingRate: config.recordingRate
556
+ recordingRate: config.recordingRate,
557
+ /** Stable visitor ID for cross-session attribution */
558
+ visitorId: getVisitorId(config.storagePrefix)
557
559
  };
558
560
  }
559
561
  function useAuth() {
@@ -2900,6 +2902,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2900
2902
  if (!email || !storeId) return;
2901
2903
  setStatus("loading");
2902
2904
  setErrorMsg("");
2905
+ const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2906
+ const analyticsData = data.analyticsContext;
2903
2907
  try {
2904
2908
  const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
2905
2909
  method: "POST",
@@ -2914,7 +2918,13 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2914
2918
  const t = [...c.tags || []];
2915
2919
  if (newsletterOptIn) t.push(c.newsletter_tag || "newsletter-subscriber");
2916
2920
  return t.length > 0 ? t : void 0;
2917
- })()
2921
+ })(),
2922
+ visitor_id: analyticsData?.visitorId || void 0,
2923
+ session_id: analyticsData?.sessionId || void 0,
2924
+ utm_source: urlParams?.get("utm_source") || void 0,
2925
+ utm_medium: urlParams?.get("utm_medium") || void 0,
2926
+ utm_campaign: urlParams?.get("utm_campaign") || void 0,
2927
+ utm_content: urlParams?.get("utm_content") || void 0
2918
2928
  })
2919
2929
  });
2920
2930
  if (!res.ok) {
@@ -3460,7 +3470,8 @@ function LandingPage({
3460
3470
  renderSection,
3461
3471
  onDataLoaded,
3462
3472
  onError,
3463
- onEvent
3473
+ onEvent,
3474
+ analyticsContext
3464
3475
  }) {
3465
3476
  const [state, setState] = useState("loading");
3466
3477
  const [data, setData] = useState(null);
@@ -3508,13 +3519,14 @@ function LandingPage({
3508
3519
  if (state === "expired") return /* @__PURE__ */ jsx(DefaultExpired2, {});
3509
3520
  if (state === "error") return /* @__PURE__ */ jsx(DefaultError2, { message: errorMsg });
3510
3521
  if (!data) return null;
3511
- return /* @__PURE__ */ jsx(PageLayout, { data, gatewayUrl, renderSection, onEvent });
3522
+ return /* @__PURE__ */ jsx(PageLayout, { data, gatewayUrl, renderSection, onEvent, analyticsContext });
3512
3523
  }
3513
3524
  function PageLayout({
3514
3525
  data,
3515
3526
  gatewayUrl,
3516
3527
  renderSection,
3517
- onEvent
3528
+ onEvent,
3529
+ analyticsContext
3518
3530
  }) {
3519
3531
  const { landing_page: lp, store } = data;
3520
3532
  const theme = {
@@ -3529,7 +3541,7 @@ function PageLayout({
3529
3541
  const fontFamily = lp.font_family || theme.fontDisplay || "system-ui, -apple-system, sans-serif";
3530
3542
  const logoUrl = store?.logo_url;
3531
3543
  const sorted = [...lp.sections].sort((a, b) => a.order - b.order);
3532
- const sectionData = { ...data, gatewayUrl, landing_page: { slug: lp.slug } };
3544
+ const sectionData = { ...data, gatewayUrl, landing_page: { slug: lp.slug }, analyticsContext };
3533
3545
  return /* @__PURE__ */ jsxs("div", { style: { minHeight: "100dvh", background: theme.bg, color: theme.fg, fontFamily }, children: [
3534
3546
  lp.custom_css && /* @__PURE__ */ jsx("style", { children: lp.custom_css }),
3535
3547
  logoUrl && /* @__PURE__ */ jsx("div", { style: { padding: "1.5rem", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx("img", { src: logoUrl, alt: store?.name || "Store", style: { height: 40, objectFit: "contain" } }) }),