@neowhale/storefront 0.2.27 → 0.2.29

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.
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunk7KXJLHGA_cjs = require('../chunk-7KXJLHGA.cjs');
4
- var chunkVAA2KKCH_cjs = require('../chunk-VAA2KKCH.cjs');
3
+ var chunkUW2U5BRY_cjs = require('../chunk-UW2U5BRY.cjs');
4
+ var chunkV5KCIYKG_cjs = require('../chunk-V5KCIYKG.cjs');
5
5
  var react = require('react');
6
6
  var navigation = require('next/navigation');
7
7
  var vanilla = require('zustand/vanilla');
@@ -499,6 +499,44 @@ function useAnalytics() {
499
499
  },
500
500
  [track]
501
501
  );
502
+ const captureGeolocation = react.useCallback(
503
+ async (options) => {
504
+ if (!trackingEnabled) return null;
505
+ if (typeof navigator === "undefined" || !navigator.geolocation) return null;
506
+ try {
507
+ const position = await new Promise((resolve, reject) => {
508
+ navigator.geolocation.getCurrentPosition(resolve, reject, {
509
+ timeout: options?.timeout ?? 1e4,
510
+ enableHighAccuracy: options?.enableHighAccuracy ?? true
511
+ });
512
+ });
513
+ const { latitude, longitude, accuracy } = position.coords;
514
+ const sessionId = await getOrCreateSession();
515
+ if (sessionId && !sessionId.startsWith("local-")) {
516
+ await client.updateSession(sessionId, {
517
+ latitude,
518
+ longitude,
519
+ geolocation_source: "browser_gps",
520
+ geolocation_accuracy: accuracy
521
+ });
522
+ }
523
+ track("location_granted", {
524
+ accuracy,
525
+ trigger: "sdk",
526
+ device_type: detectDevice()
527
+ });
528
+ return position;
529
+ } catch (error) {
530
+ track("location_denied", {
531
+ error: error instanceof Error ? error.message : "unknown",
532
+ trigger: "sdk",
533
+ device_type: detectDevice()
534
+ });
535
+ return null;
536
+ }
537
+ },
538
+ [client, getOrCreateSession, track, trackingEnabled]
539
+ );
502
540
  return {
503
541
  track,
504
542
  trackPageView,
@@ -513,6 +551,7 @@ function useAnalytics() {
513
551
  updateSessionCart,
514
552
  updateSessionOrder,
515
553
  getOrCreateSession,
554
+ captureGeolocation,
516
555
  /** Whether tracking is globally enabled for this storefront */
517
556
  trackingEnabled,
518
557
  /** Configured recording sample rate (0–1) for behavioral session replays */
@@ -635,7 +674,7 @@ function PixelInitializer({ onReady, onTheme }) {
635
674
  onTheme(config.theme);
636
675
  }
637
676
  if (ctx.config.trackingEnabled && config.pixels && config.pixels.length > 0) {
638
- const manager = new chunk7KXJLHGA_cjs.PixelManager(config.pixels);
677
+ const manager = new chunkUW2U5BRY_cjs.PixelManager(config.pixels);
639
678
  await manager.initialize();
640
679
  onReady(manager);
641
680
  }
@@ -1027,7 +1066,7 @@ function BehavioralTrackerComponent({ pathname }) {
1027
1066
  const baseUrl = config.proxyPath;
1028
1067
  const endpoint = `${baseUrl}/v1/stores/${config.storeId}/storefront/behavioral`;
1029
1068
  const sendBatch = async (batch) => {
1030
- await chunkVAA2KKCH_cjs.resilientSend(endpoint, batch, {
1069
+ await chunkV5KCIYKG_cjs.resilientSend(endpoint, batch, {
1031
1070
  "Content-Type": "application/json",
1032
1071
  "x-api-key": config.apiKey
1033
1072
  });
@@ -1213,7 +1252,7 @@ function FingerprintCollector() {
1213
1252
  collectFingerprint().then(async (fp) => {
1214
1253
  const baseUrl = config.proxyPath;
1215
1254
  const url = `${baseUrl}/v1/stores/${config.storeId}/storefront/fingerprints`;
1216
- await chunkVAA2KKCH_cjs.resilientSend(url, fp, {
1255
+ await chunkV5KCIYKG_cjs.resilientSend(url, fp, {
1217
1256
  "Content-Type": "application/json",
1218
1257
  "x-api-key": config.apiKey
1219
1258
  }).catch(() => {
@@ -1594,7 +1633,7 @@ function SessionRecorderComponent() {
1594
1633
  const sid = sessionId;
1595
1634
  const recorder = new SessionRecorder({
1596
1635
  sendChunk: async (events, sequence) => {
1597
- await chunkVAA2KKCH_cjs.resilientSend(url, {
1636
+ await chunkV5KCIYKG_cjs.resilientSend(url, {
1598
1637
  session_id: sid,
1599
1638
  visitor_id: visitorId,
1600
1639
  events,
@@ -1673,7 +1712,7 @@ function WhaleProvider({
1673
1712
  trackingEnabled: trackingEnabled ?? envBool("NEXT_PUBLIC_TRACKING_ENABLED") ?? true,
1674
1713
  recordingRate: recordingRate ?? envNumber("NEXT_PUBLIC_RECORDING_RATE") ?? 0.1
1675
1714
  };
1676
- const client = new chunkVAA2KKCH_cjs.WhaleClient({
1715
+ const client = new chunkV5KCIYKG_cjs.WhaleClient({
1677
1716
  storeId,
1678
1717
  apiKey,
1679
1718
  gatewayUrl: resolvedConfig.gatewayUrl,
@@ -2429,13 +2468,14 @@ function SectionRenderer({
2429
2468
  section,
2430
2469
  data,
2431
2470
  theme,
2432
- tracking
2471
+ tracking,
2472
+ onEvent
2433
2473
  }) {
2434
2474
  const [showCOA, setShowCOA] = react.useState(false);
2435
2475
  const el = (() => {
2436
2476
  switch (section.type) {
2437
2477
  case "hero":
2438
- return /* @__PURE__ */ jsxRuntime.jsx(HeroSection, { section, theme, tracking });
2478
+ return /* @__PURE__ */ jsxRuntime.jsx(HeroSection, { section, theme, tracking, onEvent });
2439
2479
  case "text":
2440
2480
  return /* @__PURE__ */ jsxRuntime.jsx(TextSection, { section, theme });
2441
2481
  case "image":
@@ -2445,7 +2485,7 @@ function SectionRenderer({
2445
2485
  case "gallery":
2446
2486
  return /* @__PURE__ */ jsxRuntime.jsx(GallerySection, { section, theme });
2447
2487
  case "cta":
2448
- return /* @__PURE__ */ jsxRuntime.jsx(CTASection, { section, theme, tracking });
2488
+ return /* @__PURE__ */ jsxRuntime.jsx(CTASection, { section, theme, tracking, onEvent });
2449
2489
  case "stats":
2450
2490
  return /* @__PURE__ */ jsxRuntime.jsx(StatsSection, { section, theme });
2451
2491
  case "product_card":
@@ -2455,7 +2495,7 @@ function SectionRenderer({
2455
2495
  case "social_links":
2456
2496
  return /* @__PURE__ */ jsxRuntime.jsx(SocialLinksSection, { section, theme });
2457
2497
  case "lead_capture":
2458
- return /* @__PURE__ */ jsxRuntime.jsx(LeadCaptureSection, { section, data, theme });
2498
+ return /* @__PURE__ */ jsxRuntime.jsx(LeadCaptureSection, { section, data, theme, onEvent });
2459
2499
  case "divider":
2460
2500
  return /* @__PURE__ */ jsxRuntime.jsx(DividerSection, { theme });
2461
2501
  default:
@@ -2467,7 +2507,7 @@ function SectionRenderer({
2467
2507
  showCOA && data?.coa && /* @__PURE__ */ jsxRuntime.jsx(COAModal, { coa: data.coa, theme, onClose: () => setShowCOA(false) })
2468
2508
  ] });
2469
2509
  }
2470
- function HeroSection({ section, theme, tracking }) {
2510
+ function HeroSection({ section, theme, tracking, onEvent }) {
2471
2511
  const { title, subtitle, background_image, cta_text, cta_url } = section.content;
2472
2512
  return /* @__PURE__ */ jsxRuntime.jsxs(
2473
2513
  "div",
@@ -2509,7 +2549,10 @@ function HeroSection({ section, theme, tracking }) {
2509
2549
  "a",
2510
2550
  {
2511
2551
  href: cta_url,
2512
- onClick: () => trackClick(tracking, cta_text, cta_url),
2552
+ onClick: () => {
2553
+ trackClick(tracking, cta_text, cta_url);
2554
+ onEvent?.("cta_click", { label: cta_text, url: cta_url });
2555
+ },
2513
2556
  style: {
2514
2557
  display: "inline-block",
2515
2558
  padding: "0.875rem 2rem",
@@ -2595,7 +2638,7 @@ function GallerySection({ section, theme }) {
2595
2638
  }
2596
2639
  ) }, i)) }) });
2597
2640
  }
2598
- function CTASection({ section, theme, tracking }) {
2641
+ function CTASection({ section, theme, tracking, onEvent }) {
2599
2642
  const { title, subtitle, buttons } = section.content;
2600
2643
  if (!buttons || buttons.length === 0) return null;
2601
2644
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "2rem 1.5rem", maxWidth: 480, margin: "0 auto", display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
@@ -2624,7 +2667,10 @@ function CTASection({ section, theme, tracking }) {
2624
2667
  "a",
2625
2668
  {
2626
2669
  href: btn.url,
2627
- onClick: () => trackClick(tracking, btn.text, btn.url, i),
2670
+ onClick: () => {
2671
+ trackClick(tracking, btn.text, btn.url, i);
2672
+ onEvent?.("cta_click", { label: btn.text, url: btn.url });
2673
+ },
2628
2674
  style: {
2629
2675
  display: "block",
2630
2676
  width: "100%",
@@ -2779,7 +2825,7 @@ function COAViewerSection({
2779
2825
  onShowCOA();
2780
2826
  }, style: buttonStyle, children: buttonLabel }) });
2781
2827
  }
2782
- function LeadCaptureSection({ section, data, theme }) {
2828
+ function LeadCaptureSection({ section, data, theme, onEvent }) {
2783
2829
  const c = section.content;
2784
2830
  const [firstName, setFirstName] = react.useState("");
2785
2831
  const [email, setEmail] = react.useState("");
@@ -2816,6 +2862,7 @@ function LeadCaptureSection({ section, data, theme }) {
2816
2862
  throw new Error(body?.error?.message || "Something went wrong. Please try again.");
2817
2863
  }
2818
2864
  setStatus("success");
2865
+ onEvent?.("lead", { email, first_name: firstName || void 0, source: c.source || "landing_page", landing_page_slug: slug || void 0 });
2819
2866
  } catch (err) {
2820
2867
  setErrorMsg(err instanceof Error ? err.message : "Something went wrong. Please try again.");
2821
2868
  setStatus("error");
@@ -3352,7 +3399,8 @@ function LandingPage({
3352
3399
  gatewayUrl = "https://whale-gateway.fly.dev",
3353
3400
  renderSection,
3354
3401
  onDataLoaded,
3355
- onError
3402
+ onError,
3403
+ onEvent
3356
3404
  }) {
3357
3405
  const [state, setState] = react.useState("loading");
3358
3406
  const [data, setData] = react.useState(null);
@@ -3400,12 +3448,13 @@ function LandingPage({
3400
3448
  if (state === "expired") return /* @__PURE__ */ jsxRuntime.jsx(DefaultExpired2, {});
3401
3449
  if (state === "error") return /* @__PURE__ */ jsxRuntime.jsx(DefaultError2, { message: errorMsg });
3402
3450
  if (!data) return null;
3403
- return /* @__PURE__ */ jsxRuntime.jsx(PageLayout, { data, gatewayUrl, renderSection });
3451
+ return /* @__PURE__ */ jsxRuntime.jsx(PageLayout, { data, gatewayUrl, renderSection, onEvent });
3404
3452
  }
3405
3453
  function PageLayout({
3406
3454
  data,
3407
3455
  gatewayUrl,
3408
- renderSection
3456
+ renderSection,
3457
+ onEvent
3409
3458
  }) {
3410
3459
  const { landing_page: lp, store } = data;
3411
3460
  const theme = {
@@ -3425,11 +3474,11 @@ function PageLayout({
3425
3474
  lp.custom_css && /* @__PURE__ */ jsxRuntime.jsx("style", { children: lp.custom_css }),
3426
3475
  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" } }) }),
3427
3476
  sorted.map((section) => {
3428
- const defaultRenderer = () => /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme }, section.id);
3477
+ const defaultRenderer = () => /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
3429
3478
  if (renderSection) {
3430
3479
  return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderSection(section, defaultRenderer) }, section.id);
3431
3480
  }
3432
- return /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme }, section.id);
3481
+ return /* @__PURE__ */ jsxRuntime.jsx(SectionRenderer, { section, data: sectionData, theme, onEvent }, section.id);
3433
3482
  }),
3434
3483
  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: [
3435
3484
  "Powered by ",