@uptrademedia/site-kit 1.2.4 → 1.2.6

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.
@@ -174,8 +174,9 @@ function getDeviceType() {
174
174
  }
175
175
 
176
176
  // src/forms/recaptcha.ts
177
- function getSiteKey() {
177
+ function getSiteKey(explicitSiteKey) {
178
178
  if (typeof window === "undefined") return void 0;
179
+ if (explicitSiteKey) return explicitSiteKey;
179
180
  const win = window;
180
181
  return win.__SITE_KIT_RECAPTCHA_SITE_KEY__ ?? process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY;
181
182
  }
@@ -207,8 +208,8 @@ function loadScript(siteKey) {
207
208
  document.head.appendChild(script);
208
209
  });
209
210
  }
210
- async function getRecaptchaToken() {
211
- const siteKey = getSiteKey();
211
+ async function getRecaptchaToken(explicitSiteKey) {
212
+ const siteKey = getSiteKey(explicitSiteKey);
212
213
  if (!siteKey || typeof window === "undefined") return null;
213
214
  try {
214
215
  const grecaptcha = await loadScript(siteKey);
@@ -283,6 +284,10 @@ function useForm(formIdOrSlug, options = {}) {
283
284
  }
284
285
  const data = await response.json();
285
286
  if (!data) throw new Error("Form not found");
287
+ if (typeof window !== "undefined" && data.recaptcha_site_key) {
288
+ ;
289
+ window.__SITE_KIT_RECAPTCHA_SITE_KEY__ = data.recaptcha_site_key;
290
+ }
286
291
  if (data.steps) {
287
292
  data.steps.sort((a, b) => (a.step_number || 0) - (b.step_number || 0));
288
293
  }
@@ -443,11 +448,18 @@ function useForm(formIdOrSlug, options = {}) {
443
448
  if (!apiKey) {
444
449
  throw new Error("API key is required. Set NEXT_PUBLIC_UPTRADE_API_KEY in your .env");
445
450
  }
446
- const recaptchaToken = await getRecaptchaToken();
451
+ const honeypotFieldName = form.honeypot_field || "website";
452
+ const recaptchaToken = form.recaptcha_enabled ? await getRecaptchaToken(form.recaptcha_site_key) : null;
453
+ if (form.recaptcha_enabled && !recaptchaToken) {
454
+ throw new Error("reCAPTCHA is required but could not be initialized.");
455
+ }
447
456
  const utm = getUTMParams();
448
457
  const submission = {
449
458
  formId: form.id,
450
- data: values,
459
+ data: {
460
+ ...values,
461
+ ...form.honeypot_enabled ? { [honeypotFieldName]: "" } : {}
462
+ },
451
463
  metadata: {
452
464
  pageUrl: typeof window !== "undefined" ? window.location.href : null,
453
465
  referrer: typeof document !== "undefined" ? document.referrer || null : null,
@@ -541,6 +553,7 @@ function normalizeOptions(options) {
541
553
  return [];
542
554
  }
543
555
  function FormField({ field: field2, value, error, onChange, classPrefix = "uptrade-form" }) {
556
+ const inputId = `uptrade-${field2.id || field2.slug}`;
544
557
  const options = normalizeOptions(field2.options);
545
558
  const baseInputStyle = {
546
559
  width: "100%",
@@ -580,13 +593,14 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
580
593
  const hideLabel = !!field2.hide_label;
581
594
  const effectivePlaceholder = hideLabel ? field2.label : field2.placeholder;
582
595
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${classPrefix}__field ${classPrefix}__field--${field2.field_type}`, children: [
583
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: `${classPrefix}__label`, style: { ...labelStyle, ...hideLabel ? { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0,0,0,0)", whiteSpace: "nowrap", border: 0 } : {} }, children: [
596
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: inputId, className: `${classPrefix}__label`, style: { ...labelStyle, ...hideLabel ? { position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0,0,0,0)", whiteSpace: "nowrap", border: 0 } : {} }, children: [
584
597
  field2.label,
585
598
  field2.is_required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--uptrade-error-color, #ef4444)" }, children: " *" })
586
599
  ] }),
587
600
  ["text", "email", "phone", "tel", "number"].includes(field2.field_type) && /* @__PURE__ */ jsxRuntime.jsx(
588
601
  "input",
589
602
  {
603
+ id: inputId,
590
604
  className: `${classPrefix}__input`,
591
605
  type: field2.field_type === "phone" || field2.field_type === "tel" ? "tel" : field2.field_type,
592
606
  name: field2.slug,
@@ -600,6 +614,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
600
614
  field2.field_type === "textarea" && /* @__PURE__ */ jsxRuntime.jsx(
601
615
  "textarea",
602
616
  {
617
+ id: inputId,
603
618
  className: `${classPrefix}__textarea`,
604
619
  name: field2.slug,
605
620
  value: String(value || ""),
@@ -613,6 +628,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
613
628
  field2.field_type === "select" && /* @__PURE__ */ jsxRuntime.jsxs(
614
629
  "select",
615
630
  {
631
+ id: inputId,
616
632
  className: `${classPrefix}__select`,
617
633
  name: field2.slug,
618
634
  value: String(value || ""),
@@ -628,6 +644,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
628
644
  field2.field_type === "multi-select" && /* @__PURE__ */ jsxRuntime.jsx(
629
645
  "select",
630
646
  {
647
+ id: inputId,
631
648
  className: `${classPrefix}__select ${classPrefix}__select--multi`,
632
649
  name: field2.slug,
633
650
  value: Array.isArray(value) ? value : [],
@@ -690,6 +707,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
690
707
  field2.field_type === "date" && /* @__PURE__ */ jsxRuntime.jsx(
691
708
  "input",
692
709
  {
710
+ id: inputId,
693
711
  className: `${classPrefix}__input ${classPrefix}__input--date`,
694
712
  type: "date",
695
713
  name: field2.slug,
@@ -702,6 +720,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
702
720
  field2.field_type === "time" && /* @__PURE__ */ jsxRuntime.jsx(
703
721
  "input",
704
722
  {
723
+ id: inputId,
705
724
  className: `${classPrefix}__input ${classPrefix}__input--time`,
706
725
  type: "time",
707
726
  name: field2.slug,
@@ -714,6 +733,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
714
733
  field2.field_type === "datetime" && /* @__PURE__ */ jsxRuntime.jsx(
715
734
  "input",
716
735
  {
736
+ id: inputId,
717
737
  className: `${classPrefix}__input ${classPrefix}__input--datetime`,
718
738
  type: "datetime-local",
719
739
  name: field2.slug,
@@ -726,6 +746,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
726
746
  field2.field_type === "file" && /* @__PURE__ */ jsxRuntime.jsx(
727
747
  "input",
728
748
  {
749
+ id: inputId,
729
750
  className: `${classPrefix}__input ${classPrefix}__input--file`,
730
751
  type: "file",
731
752
  name: field2.slug,
@@ -760,6 +781,7 @@ function FormField({ field: field2, value, error, onChange, classPrefix = "uptra
760
781
  /* @__PURE__ */ jsxRuntime.jsx(
761
782
  "input",
762
783
  {
784
+ id: inputId,
763
785
  type: "range",
764
786
  name: field2.slug,
765
787
  value: Number(value || 50),
@@ -787,6 +809,8 @@ function FormClient({
787
809
  const [step, setStep] = react.useState(1);
788
810
  const [isSubmitting, setIsSubmitting] = react.useState(false);
789
811
  const [isComplete, setIsComplete] = react.useState(false);
812
+ const [honeypotValue, setHoneypotValue] = react.useState("");
813
+ const honeypotFieldName = config.honeypot_field || "website";
790
814
  const { trackStepChange, trackComplete } = useFormTracking({
791
815
  formId: config.id,
792
816
  totalSteps: config.total_steps
@@ -910,12 +934,18 @@ function FormClient({
910
934
  if (!apiKey) {
911
935
  throw new Error("API key is required. Set NEXT_PUBLIC_UPTRADE_API_KEY in your .env");
912
936
  }
913
- const recaptchaToken = await getRecaptchaToken();
937
+ const recaptchaToken = config.recaptcha_enabled ? await getRecaptchaToken(config.recaptcha_site_key) : null;
938
+ if (config.recaptcha_enabled && !recaptchaToken) {
939
+ throw new Error("reCAPTCHA is required but could not be initialized.");
940
+ }
914
941
  const utm = getUTMParams2();
915
942
  const submission = {
916
943
  form_id: config.id,
917
944
  project_id: config.project_id,
918
- data: values,
945
+ data: {
946
+ ...values,
947
+ ...config.honeypot_enabled ? { [honeypotFieldName]: honeypotValue } : {}
948
+ },
919
949
  routing_type: config.form_type,
920
950
  status: "new",
921
951
  metadata: {
@@ -954,7 +984,7 @@ function FormClient({
954
984
  } finally {
955
985
  setIsSubmitting(false);
956
986
  }
957
- }, [config, values, validateStep, trackComplete, onSuccess, onError]);
987
+ }, [config, honeypotFieldName, honeypotValue, values, validateStep, trackComplete, onSuccess, onError]);
958
988
  const progress = react.useMemo(() => {
959
989
  return Math.round(step / config.total_steps * 100);
960
990
  }, [step, config.total_steps]);
@@ -1042,7 +1072,7 @@ function FormClient({
1042
1072
  "input",
1043
1073
  {
1044
1074
  type: "text",
1045
- name: "website",
1075
+ name: honeypotFieldName,
1046
1076
  tabIndex: -1,
1047
1077
  autoComplete: "off",
1048
1078
  style: {
@@ -1051,7 +1081,9 @@ function FormClient({
1051
1081
  opacity: 0,
1052
1082
  pointerEvents: "none"
1053
1083
  },
1084
+ value: honeypotValue,
1054
1085
  onChange: (e) => {
1086
+ setHoneypotValue(e.target.value);
1055
1087
  if (e.target.value) {
1056
1088
  console.warn("[Forms] Honeypot triggered");
1057
1089
  }