@neowhale/storefront 0.2.57 → 0.2.58

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.
@@ -1130,6 +1130,25 @@ var WhaleStorefront = (function (exports) {
1130
1130
  }
1131
1131
 
1132
1132
  // src/react/components/sections/lead-capture-section.tsx
1133
+ var TURNSTILE_SITE_KEY = "0x4AAAAAACwmUPgmyfw6pWfT";
1134
+ var TURNSTILE_SCRIPT_URL = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
1135
+ var turnstilePromise = null;
1136
+ function loadTurnstileScript() {
1137
+ if (turnstilePromise) return turnstilePromise;
1138
+ if (typeof window !== "undefined" && window.turnstile) {
1139
+ turnstilePromise = Promise.resolve();
1140
+ return turnstilePromise;
1141
+ }
1142
+ turnstilePromise = new Promise((resolve, reject) => {
1143
+ const script = document.createElement("script");
1144
+ script.src = TURNSTILE_SCRIPT_URL;
1145
+ script.async = true;
1146
+ script.onload = () => resolve();
1147
+ script.onerror = () => reject(new Error("Failed to load Turnstile script"));
1148
+ document.head.appendChild(script);
1149
+ });
1150
+ return turnstilePromise;
1151
+ }
1133
1152
  function LeadCaptureSection({ section, data, theme, onEvent }) {
1134
1153
  const c = section.content;
1135
1154
  const [firstName, setFirstName] = useState("");
@@ -1138,6 +1157,41 @@ var WhaleStorefront = (function (exports) {
1138
1157
  const [status, setStatus] = useState("idle");
1139
1158
  const [errorMsg, setErrorMsg] = useState("");
1140
1159
  const [serverMessage, setServerMessage] = useState(null);
1160
+ const turnstileRef = useRef(null);
1161
+ const turnstileToken = useRef(null);
1162
+ const turnstileWidgetId = useRef(null);
1163
+ const pendingSubmit = useRef(null);
1164
+ const onTurnstileToken = useCallback((token) => {
1165
+ turnstileToken.current = token;
1166
+ if (pendingSubmit.current) {
1167
+ const cb = pendingSubmit.current;
1168
+ pendingSubmit.current = null;
1169
+ cb();
1170
+ }
1171
+ }, []);
1172
+ useEffect(() => {
1173
+ if (typeof window === "undefined") return;
1174
+ let widgetId = null;
1175
+ loadTurnstileScript().then(() => {
1176
+ const el = turnstileRef.current;
1177
+ if (!el || !window.turnstile) return;
1178
+ widgetId = window.turnstile.render(el, {
1179
+ sitekey: TURNSTILE_SITE_KEY,
1180
+ callback: onTurnstileToken,
1181
+ "expired-callback": () => {
1182
+ turnstileToken.current = null;
1183
+ },
1184
+ size: "invisible"
1185
+ });
1186
+ turnstileWidgetId.current = widgetId;
1187
+ }).catch(() => {
1188
+ });
1189
+ return () => {
1190
+ if (widgetId != null && window.turnstile) {
1191
+ window.turnstile.remove(widgetId);
1192
+ }
1193
+ };
1194
+ }, [onTurnstileToken]);
1141
1195
  const gatewayUrl = c.gateway_url || data.gatewayUrl || "https://whale-gateway.fly.dev";
1142
1196
  const storeId = c.store_id || data.store?.id;
1143
1197
  const slug = c.landing_page_slug || data.landing_page?.slug;
@@ -1146,11 +1200,7 @@ var WhaleStorefront = (function (exports) {
1146
1200
  const buttonText = c.button_text || "Claim My Discount";
1147
1201
  const successHeading = c.success_heading || "You\u2019re in!";
1148
1202
  const successMessage = c.success_message || "Check your inbox for the discount code.";
1149
- async function handleSubmit(e) {
1150
- e.preventDefault();
1151
- if (!email || !storeId) return;
1152
- setStatus("loading");
1153
- setErrorMsg("");
1203
+ async function submitLead(cfToken) {
1154
1204
  const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
1155
1205
  try {
1156
1206
  const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
@@ -1172,7 +1222,8 @@ var WhaleStorefront = (function (exports) {
1172
1222
  utm_source: urlParams?.get("utm_source") || void 0,
1173
1223
  utm_medium: urlParams?.get("utm_medium") || void 0,
1174
1224
  utm_campaign: urlParams?.get("utm_campaign") || void 0,
1175
- utm_content: urlParams?.get("utm_content") || void 0
1225
+ utm_content: urlParams?.get("utm_content") || void 0,
1226
+ cf_turnstile_response: cfToken || void 0
1176
1227
  })
1177
1228
  });
1178
1229
  if (!res.ok) {
@@ -1193,6 +1244,22 @@ var WhaleStorefront = (function (exports) {
1193
1244
  setStatus("error");
1194
1245
  }
1195
1246
  }
1247
+ async function handleSubmit(e) {
1248
+ e.preventDefault();
1249
+ if (!email || !storeId) return;
1250
+ setStatus("loading");
1251
+ setErrorMsg("");
1252
+ if (turnstileToken.current) {
1253
+ return submitLead(turnstileToken.current);
1254
+ }
1255
+ const turnstile = window.turnstile;
1256
+ if (turnstile && turnstileWidgetId.current != null) {
1257
+ pendingSubmit.current = () => submitLead(turnstileToken.current);
1258
+ turnstile.execute(turnstileWidgetId.current);
1259
+ } else {
1260
+ return submitLead(null);
1261
+ }
1262
+ }
1196
1263
  const inputStyle = {
1197
1264
  flex: 1,
1198
1265
  minWidth: 0,
@@ -1286,7 +1353,8 @@ var WhaleStorefront = (function (exports) {
1286
1353
  animation: "lc-spin 0.8s linear infinite"
1287
1354
  } }),
1288
1355
  buttonText
1289
- ] })
1356
+ ] }),
1357
+ /* @__PURE__ */ jsx("div", { ref: turnstileRef, style: { display: "none" } })
1290
1358
  ] })
1291
1359
  ] })
1292
1360
  ] });
@@ -2865,6 +2865,25 @@ function COAModal({ coa, theme, onClose }) {
2865
2865
  /* @__PURE__ */ jsxRuntime.jsx("iframe", { src: coa.url, style: { flex: 1, border: "none", background: "#fff" }, title: "Lab Results" })
2866
2866
  ] });
2867
2867
  }
2868
+ var TURNSTILE_SITE_KEY = "0x4AAAAAACwmUPgmyfw6pWfT";
2869
+ var TURNSTILE_SCRIPT_URL = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
2870
+ var turnstilePromise = null;
2871
+ function loadTurnstileScript() {
2872
+ if (turnstilePromise) return turnstilePromise;
2873
+ if (typeof window !== "undefined" && window.turnstile) {
2874
+ turnstilePromise = Promise.resolve();
2875
+ return turnstilePromise;
2876
+ }
2877
+ turnstilePromise = new Promise((resolve, reject) => {
2878
+ const script = document.createElement("script");
2879
+ script.src = TURNSTILE_SCRIPT_URL;
2880
+ script.async = true;
2881
+ script.onload = () => resolve();
2882
+ script.onerror = () => reject(new Error("Failed to load Turnstile script"));
2883
+ document.head.appendChild(script);
2884
+ });
2885
+ return turnstilePromise;
2886
+ }
2868
2887
  function LeadCaptureSection({ section, data, theme, onEvent }) {
2869
2888
  const c = section.content;
2870
2889
  const [firstName, setFirstName] = react.useState("");
@@ -2873,6 +2892,41 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2873
2892
  const [status, setStatus] = react.useState("idle");
2874
2893
  const [errorMsg, setErrorMsg] = react.useState("");
2875
2894
  const [serverMessage, setServerMessage] = react.useState(null);
2895
+ const turnstileRef = react.useRef(null);
2896
+ const turnstileToken = react.useRef(null);
2897
+ const turnstileWidgetId = react.useRef(null);
2898
+ const pendingSubmit = react.useRef(null);
2899
+ const onTurnstileToken = react.useCallback((token) => {
2900
+ turnstileToken.current = token;
2901
+ if (pendingSubmit.current) {
2902
+ const cb = pendingSubmit.current;
2903
+ pendingSubmit.current = null;
2904
+ cb();
2905
+ }
2906
+ }, []);
2907
+ react.useEffect(() => {
2908
+ if (typeof window === "undefined") return;
2909
+ let widgetId = null;
2910
+ loadTurnstileScript().then(() => {
2911
+ const el = turnstileRef.current;
2912
+ if (!el || !window.turnstile) return;
2913
+ widgetId = window.turnstile.render(el, {
2914
+ sitekey: TURNSTILE_SITE_KEY,
2915
+ callback: onTurnstileToken,
2916
+ "expired-callback": () => {
2917
+ turnstileToken.current = null;
2918
+ },
2919
+ size: "invisible"
2920
+ });
2921
+ turnstileWidgetId.current = widgetId;
2922
+ }).catch(() => {
2923
+ });
2924
+ return () => {
2925
+ if (widgetId != null && window.turnstile) {
2926
+ window.turnstile.remove(widgetId);
2927
+ }
2928
+ };
2929
+ }, [onTurnstileToken]);
2876
2930
  const gatewayUrl = c.gateway_url || data.gatewayUrl || "https://whale-gateway.fly.dev";
2877
2931
  const storeId = c.store_id || data.store?.id;
2878
2932
  const slug = c.landing_page_slug || data.landing_page?.slug;
@@ -2881,11 +2935,7 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2881
2935
  const buttonText = c.button_text || "Claim My Discount";
2882
2936
  const successHeading = c.success_heading || "You\u2019re in!";
2883
2937
  const successMessage = c.success_message || "Check your inbox for the discount code.";
2884
- async function handleSubmit(e) {
2885
- e.preventDefault();
2886
- if (!email || !storeId) return;
2887
- setStatus("loading");
2888
- setErrorMsg("");
2938
+ async function submitLead(cfToken) {
2889
2939
  const urlParams = typeof window !== "undefined" ? new URLSearchParams(window.location.search) : null;
2890
2940
  try {
2891
2941
  const res = await fetch(`${gatewayUrl}/v1/stores/${storeId}/storefront/leads`, {
@@ -2907,7 +2957,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2907
2957
  utm_source: urlParams?.get("utm_source") || void 0,
2908
2958
  utm_medium: urlParams?.get("utm_medium") || void 0,
2909
2959
  utm_campaign: urlParams?.get("utm_campaign") || void 0,
2910
- utm_content: urlParams?.get("utm_content") || void 0
2960
+ utm_content: urlParams?.get("utm_content") || void 0,
2961
+ cf_turnstile_response: cfToken || void 0
2911
2962
  })
2912
2963
  });
2913
2964
  if (!res.ok) {
@@ -2928,6 +2979,22 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
2928
2979
  setStatus("error");
2929
2980
  }
2930
2981
  }
2982
+ async function handleSubmit(e) {
2983
+ e.preventDefault();
2984
+ if (!email || !storeId) return;
2985
+ setStatus("loading");
2986
+ setErrorMsg("");
2987
+ if (turnstileToken.current) {
2988
+ return submitLead(turnstileToken.current);
2989
+ }
2990
+ const turnstile = window.turnstile;
2991
+ if (turnstile && turnstileWidgetId.current != null) {
2992
+ pendingSubmit.current = () => submitLead(turnstileToken.current);
2993
+ turnstile.execute(turnstileWidgetId.current);
2994
+ } else {
2995
+ return submitLead(null);
2996
+ }
2997
+ }
2931
2998
  const inputStyle = {
2932
2999
  flex: 1,
2933
3000
  minWidth: 0,
@@ -3021,7 +3088,8 @@ function LeadCaptureSection({ section, data, theme, onEvent }) {
3021
3088
  animation: "lc-spin 0.8s linear infinite"
3022
3089
  } }),
3023
3090
  buttonText
3024
- ] })
3091
+ ] }),
3092
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: turnstileRef, style: { display: "none" } })
3025
3093
  ] })
3026
3094
  ] })
3027
3095
  ] });