@neowhale/storefront 0.2.52 → 0.2.54

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.
@@ -2206,8 +2206,52 @@ function AnimatedText({ text }) {
2206
2206
  (part, i) => NUM_TEST.test(part) ? /* @__PURE__ */ jsx(AnimatedNumber, { raw: part }, i) : part
2207
2207
  ) });
2208
2208
  }
2209
- function HeroSection({ section, theme, tracking, onEvent }) {
2210
- const { title, subtitle, background_image, cta_text, cta_url, review_line } = section.content;
2209
+ var GOOGLE_G = '<svg width="18" height="18" viewBox="0 0 48 48"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59a14.5 14.5 0 010-9.18l-7.98-6.19a24.03 24.03 0 000 21.56l7.98-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>';
2210
+ function HeroSection({ section, theme, tracking, onEvent, data }) {
2211
+ const c = section.content;
2212
+ const [formOpen, setFormOpen] = useState(false);
2213
+ const [firstName, setFirstName] = useState("");
2214
+ const [email, setEmail] = useState("");
2215
+ const [status, setStatus] = useState("idle");
2216
+ const [errorMsg, setErrorMsg] = useState("");
2217
+ const gatewayUrl = data?.gatewayUrl || "https://whale-gateway.fly.dev";
2218
+ const storeId = data?.store?.id;
2219
+ const slug = data?.landing_page?.slug;
2220
+ async function handleSubmit(e) {
2221
+ e.preventDefault();
2222
+ if (!email || !storeId) return;
2223
+ setStatus("loading");
2224
+ setErrorMsg("");
2225
+ const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2226
+ try {
2227
+ const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
2228
+ method: "POST",
2229
+ headers: { "Content-Type": "application/json" },
2230
+ body: JSON.stringify({
2231
+ email,
2232
+ first_name: firstName || void 0,
2233
+ source: c.inline_form?.source || "landing_page",
2234
+ landing_page_slug: slug || void 0,
2235
+ tags: c.inline_form?.tags || void 0,
2236
+ visitor_id: data?.analyticsContext?.visitorId || void 0,
2237
+ session_id: data?.analyticsContext?.sessionId || void 0,
2238
+ utm_source: urlParams?.get("utm_source") || void 0,
2239
+ utm_medium: urlParams?.get("utm_medium") || void 0,
2240
+ utm_campaign: urlParams?.get("utm_campaign") || void 0
2241
+ })
2242
+ });
2243
+ if (!res.ok) {
2244
+ const body = await res.json().catch(() => ({}));
2245
+ throw new Error(body?.error?.message || "something went wrong.");
2246
+ }
2247
+ setStatus("success");
2248
+ onEvent?.("lead", { email, first_name: firstName || void 0, source: c.inline_form?.source || "landing_page" });
2249
+ } catch (err) {
2250
+ setErrorMsg(err instanceof Error ? err.message : "something went wrong.");
2251
+ setStatus("error");
2252
+ }
2253
+ }
2254
+ const hasInlineForm = !!c.inline_form;
2211
2255
  return /* @__PURE__ */ jsxs("div", { style: {
2212
2256
  position: "relative",
2213
2257
  minHeight: "60vh",
@@ -2217,36 +2261,56 @@ function HeroSection({ section, theme, tracking, onEvent }) {
2217
2261
  alignItems: "center",
2218
2262
  textAlign: "center",
2219
2263
  padding: "3rem 1.5rem",
2220
- backgroundImage: background_image ? `url(${background_image})` : void 0,
2264
+ backgroundImage: c.background_image ? `url(${c.background_image})` : void 0,
2221
2265
  backgroundSize: "cover",
2222
2266
  backgroundPosition: "center"
2223
2267
  }, children: [
2224
- background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
2225
- /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640 }, children: [
2226
- title && /* @__PURE__ */ jsx("h1", { style: {
2227
- fontSize: "clamp(2rem, 8vw, 3rem)",
2268
+ c.background_image && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.5)" } }),
2269
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1, maxWidth: 640, width: "100%" }, children: [
2270
+ c.title && /* @__PURE__ */ jsx("h1", { style: {
2271
+ fontSize: "clamp(2rem, 7vw, 3.25rem)",
2228
2272
  fontWeight: 300,
2229
2273
  fontFamily: theme.fontDisplay || "inherit",
2230
- margin: "0 0 1rem",
2231
- lineHeight: 1.15,
2232
- letterSpacing: "-0.02em",
2274
+ margin: "0 0 1.25rem",
2275
+ lineHeight: 1.08,
2276
+ letterSpacing: "-0.03em",
2233
2277
  color: theme.fg
2234
- }, children: /* @__PURE__ */ jsx(AnimatedText, { text: title }) }),
2235
- subtitle && /* @__PURE__ */ jsx("p", { style: {
2236
- fontSize: "0.85rem",
2278
+ }, children: /* @__PURE__ */ jsx(AnimatedText, { text: c.title }) }),
2279
+ c.subtitle && /* @__PURE__ */ jsx("p", { style: {
2280
+ fontSize: "clamp(0.9rem, 2.5vw, 1.1rem)",
2237
2281
  color: theme.accent,
2238
2282
  margin: "0 0 2rem",
2239
2283
  lineHeight: 1.6,
2240
2284
  textTransform: "uppercase",
2241
- letterSpacing: "0.15em"
2242
- }, children: subtitle }),
2243
- cta_text && cta_url && /* @__PURE__ */ jsx(
2285
+ letterSpacing: "0.1em"
2286
+ }, children: c.subtitle }),
2287
+ hasInlineForm ? /* @__PURE__ */ jsx(
2288
+ HeroInlineForm,
2289
+ {
2290
+ ctaText: c.cta_text || "claim it",
2291
+ formOpen,
2292
+ setFormOpen,
2293
+ firstName,
2294
+ setFirstName,
2295
+ email,
2296
+ setEmail,
2297
+ status,
2298
+ errorMsg,
2299
+ onSubmit: handleSubmit,
2300
+ successHeading: c.inline_form.success_heading || "you're in.",
2301
+ successMessage: c.inline_form.success_message || "check your inbox.",
2302
+ submitText: c.inline_form.button_text || "send my code",
2303
+ theme,
2304
+ onEvent,
2305
+ tracking
2306
+ }
2307
+ ) : c.cta_text && c.cta_url ? /* @__PURE__ */ jsx(
2244
2308
  "a",
2245
2309
  {
2246
- href: cta_url,
2310
+ href: c.cta_url,
2247
2311
  onClick: () => {
2248
- trackClick(tracking, cta_text, cta_url);
2249
- onEvent?.("cta_click", { label: cta_text, url: cta_url });
2312
+ trackClick(tracking, c.cta_text, c.cta_url);
2313
+ onEvent?.("cta_click", { label: c.cta_text, url: c.cta_url });
2250
2314
  },
2251
2315
  style: {
2252
2316
  display: "inline-block",
@@ -2259,13 +2323,111 @@ function HeroSection({ section, theme, tracking, onEvent }) {
2259
2323
  letterSpacing: "0.08em",
2260
2324
  textTransform: "uppercase"
2261
2325
  },
2262
- children: cta_text
2326
+ children: c.cta_text
2263
2327
  }
2264
- ),
2265
- review_line && review_line.count > 0 && /* @__PURE__ */ jsx(ReviewLine, { rating: review_line.rating, count: review_line.count, source: review_line.source, url: review_line.url, accent: theme.accent, fg: theme.fg })
2328
+ ) : null,
2329
+ c.review_line && c.review_line.count > 0 && /* @__PURE__ */ jsx(ReviewLine, { rating: c.review_line.rating, count: c.review_line.count, source: c.review_line.source, url: c.review_line.url, accent: theme.accent, fg: theme.fg })
2266
2330
  ] })
2267
2331
  ] });
2268
2332
  }
2333
+ function HeroInlineForm({ ctaText, formOpen, setFormOpen, firstName, setFirstName, email, setEmail, status, errorMsg, onSubmit, successHeading, successMessage, submitText, theme, onEvent, tracking }) {
2334
+ if (status === "success") {
2335
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "1.25rem 2rem", background: `${theme.fg}08`, border: `1px solid ${theme.fg}15`, maxWidth: 420, margin: "0 auto" }, children: [
2336
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "1.1rem", fontWeight: 300, color: theme.fg, margin: "0 0 0.25rem", fontFamily: theme.fontDisplay || "inherit" }, children: successHeading }),
2337
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: `${theme.fg}80`, margin: 0 }, children: successMessage })
2338
+ ] });
2339
+ }
2340
+ if (!formOpen) {
2341
+ return /* @__PURE__ */ jsx(
2342
+ "button",
2343
+ {
2344
+ onClick: () => {
2345
+ setFormOpen(true);
2346
+ trackClick(tracking, ctaText, "#form");
2347
+ onEvent?.("cta_click", { label: ctaText });
2348
+ },
2349
+ style: {
2350
+ display: "inline-block",
2351
+ padding: "0.875rem 2rem",
2352
+ background: theme.fg,
2353
+ color: theme.bg,
2354
+ border: "none",
2355
+ cursor: "pointer",
2356
+ fontSize: "0.85rem",
2357
+ fontWeight: 500,
2358
+ letterSpacing: "0.08em",
2359
+ textTransform: "uppercase",
2360
+ fontFamily: "inherit",
2361
+ transition: "transform 0.2s"
2362
+ },
2363
+ children: ctaText
2364
+ }
2365
+ );
2366
+ }
2367
+ const inputStyle = {
2368
+ flex: 1,
2369
+ minWidth: 0,
2370
+ padding: "0.875rem 1rem",
2371
+ background: theme.surface,
2372
+ border: `1px solid ${theme.fg}25`,
2373
+ color: theme.fg,
2374
+ fontSize: "0.9rem",
2375
+ fontWeight: 300,
2376
+ outline: "none",
2377
+ fontFamily: "inherit",
2378
+ boxSizing: "border-box"
2379
+ };
2380
+ return /* @__PURE__ */ jsxs("form", { onSubmit, style: { maxWidth: 480, margin: "0 auto" }, children: [
2381
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 0 }, children: [
2382
+ /* @__PURE__ */ jsx(
2383
+ "input",
2384
+ {
2385
+ type: "text",
2386
+ placeholder: "first name",
2387
+ value: firstName,
2388
+ autoFocus: true,
2389
+ onChange: (e) => setFirstName(e.target.value),
2390
+ style: { ...inputStyle, borderRight: "none" }
2391
+ }
2392
+ ),
2393
+ /* @__PURE__ */ jsx(
2394
+ "input",
2395
+ {
2396
+ type: "email",
2397
+ placeholder: "email",
2398
+ value: email,
2399
+ required: true,
2400
+ onChange: (e) => setEmail(e.target.value),
2401
+ style: { ...inputStyle, borderRight: "none" }
2402
+ }
2403
+ ),
2404
+ /* @__PURE__ */ jsx(
2405
+ "button",
2406
+ {
2407
+ type: "submit",
2408
+ disabled: status === "loading",
2409
+ style: {
2410
+ padding: "0.875rem 1.25rem",
2411
+ background: theme.fg,
2412
+ color: theme.bg,
2413
+ border: "none",
2414
+ fontSize: "0.75rem",
2415
+ fontWeight: 600,
2416
+ letterSpacing: "0.08em",
2417
+ textTransform: "uppercase",
2418
+ cursor: status === "loading" ? "wait" : "pointer",
2419
+ fontFamily: "inherit",
2420
+ whiteSpace: "nowrap",
2421
+ flexShrink: 0,
2422
+ opacity: status === "loading" ? 0.7 : 1
2423
+ },
2424
+ children: status === "loading" ? "..." : submitText
2425
+ }
2426
+ )
2427
+ ] }),
2428
+ status === "error" && errorMsg && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.75rem", color: "#e55", margin: "0.5rem 0 0", textAlign: "left" }, children: errorMsg })
2429
+ ] });
2430
+ }
2269
2431
  function TextSection({ section, theme }) {
2270
2432
  const { heading, body } = section.content;
2271
2433
  const align = section.config?.align || "left";
@@ -2417,25 +2579,21 @@ function toEmbedUrl(url) {
2417
2579
  }
2418
2580
  function ReviewLine({ rating, count, source, url, accent, fg }) {
2419
2581
  const label = `${rating.toFixed(1)} \xB7 ${count} ${source || "reviews"}`;
2420
- const stars = /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", gap: "1px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsx("svg", { width: 14, height: 14, viewBox: "0 0 20 20", fill: s <= Math.round(rating) ? accent : `${fg}20`, children: /* @__PURE__ */ jsx("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" }) }, s)) });
2421
- const text = /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8rem", color: `${fg}80`, fontWeight: 500 }, children: label });
2422
- const inner = /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem" }, children: [
2423
- stars,
2424
- text
2582
+ const content = /* @__PURE__ */ jsxs("div", { style: {
2583
+ marginTop: "1.5rem",
2584
+ display: "flex",
2585
+ alignItems: "center",
2586
+ justifyContent: "center",
2587
+ gap: "0.625rem"
2588
+ }, children: [
2589
+ /* @__PURE__ */ jsx("span", { dangerouslySetInnerHTML: { __html: GOOGLE_G } }),
2590
+ /* @__PURE__ */ jsx("span", { style: { display: "inline-flex", gap: "2px" }, children: [1, 2, 3, 4, 5].map((s) => /* @__PURE__ */ jsx("svg", { width: 16, height: 16, viewBox: "0 0 20 20", fill: s <= Math.round(rating) ? "#FBBC05" : `${fg}20`, children: /* @__PURE__ */ jsx("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" }) }, s)) }),
2591
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.9rem", color: `${fg}AA`, fontWeight: 500 }, children: label })
2425
2592
  ] });
2426
2593
  if (url) {
2427
- return /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", textAlign: "center" }, children: /* @__PURE__ */ jsx(
2428
- "a",
2429
- {
2430
- href: url,
2431
- target: "_blank",
2432
- rel: "noopener noreferrer",
2433
- style: { textDecoration: "none", display: "inline-flex", alignItems: "center", gap: "0.5rem" },
2434
- children: inner
2435
- }
2436
- ) });
2594
+ return /* @__PURE__ */ jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: "none", display: "block" }, children: content });
2437
2595
  }
2438
- return /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", display: "flex", justifyContent: "center" }, children: inner });
2596
+ return content;
2439
2597
  }
2440
2598
  function CTASection({ section, theme, tracking, onEvent }) {
2441
2599
  const { title, subtitle, buttons } = section.content;
@@ -3290,7 +3448,7 @@ function SectionRenderer({
3290
3448
  const el = (() => {
3291
3449
  switch (section.type) {
3292
3450
  case "hero":
3293
- return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent });
3451
+ return /* @__PURE__ */ jsx(HeroSection, { section, theme, tracking, onEvent, data });
3294
3452
  case "collage_hero":
3295
3453
  return /* @__PURE__ */ jsx(CollageHeroSection, { section, theme, tracking, onEvent });
3296
3454
  case "text":