@sonordev/site-kit 2.9.0 → 3.0.0

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.
Files changed (61) hide show
  1. package/dist/EngageWidget-LRE4VD7O.js +14 -0
  2. package/dist/{EngageWidget-JIXUBG47.js.map → EngageWidget-LRE4VD7O.js.map} +1 -1
  3. package/dist/EngageWidget-OAOLB6ED.mjs +5 -0
  4. package/dist/{EngageWidget-4W5EM4UO.mjs.map → EngageWidget-OAOLB6ED.mjs.map} +1 -1
  5. package/dist/chunk-2FEQ6TYD.mjs +37 -0
  6. package/dist/chunk-2FEQ6TYD.mjs.map +1 -0
  7. package/dist/{chunk-5QLM6LZ2.mjs → chunk-APRAGA5G.mjs} +3 -3
  8. package/dist/{chunk-5QLM6LZ2.mjs.map → chunk-APRAGA5G.mjs.map} +1 -1
  9. package/dist/{chunk-HANRPNKG.js → chunk-FMI65E5Y.js} +3 -3
  10. package/dist/{chunk-HANRPNKG.js.map → chunk-FMI65E5Y.js.map} +1 -1
  11. package/dist/{chunk-SLJJJWMV.js → chunk-FPU2QPO4.js} +338 -33
  12. package/dist/chunk-FPU2QPO4.js.map +1 -0
  13. package/dist/chunk-MLJXELPA.js +693 -0
  14. package/dist/chunk-MLJXELPA.js.map +1 -0
  15. package/dist/{chunk-IAV32YHL.mjs → chunk-RV45YMBJ.mjs} +338 -33
  16. package/dist/chunk-RV45YMBJ.mjs.map +1 -0
  17. package/dist/chunk-TCVHI3JP.mjs +686 -0
  18. package/dist/chunk-TCVHI3JP.mjs.map +1 -0
  19. package/dist/chunk-YBKALLBH.js +46 -0
  20. package/dist/chunk-YBKALLBH.js.map +1 -0
  21. package/dist/engage/index.d.mts +2 -2
  22. package/dist/engage/index.d.ts +2 -2
  23. package/dist/engage/index.js +5 -4
  24. package/dist/engage/index.js.map +1 -1
  25. package/dist/engage/index.mjs +2 -1
  26. package/dist/engage/index.mjs.map +1 -1
  27. package/dist/forms/index.js +19 -683
  28. package/dist/forms/index.js.map +1 -1
  29. package/dist/forms/index.mjs +4 -676
  30. package/dist/forms/index.mjs.map +1 -1
  31. package/dist/index.d.mts +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/layout/client.js +2 -2
  34. package/dist/layout/client.mjs +1 -1
  35. package/dist/layout/index.js +2 -2
  36. package/dist/layout/index.mjs +1 -1
  37. package/dist/middleware/index.d.mts +62 -1
  38. package/dist/middleware/index.d.ts +62 -1
  39. package/dist/middleware/index.js +105 -0
  40. package/dist/middleware/index.js.map +1 -1
  41. package/dist/middleware/index.mjs +103 -1
  42. package/dist/middleware/index.mjs.map +1 -1
  43. package/dist/slots/contract.d.mts +83 -0
  44. package/dist/slots/contract.d.ts +83 -0
  45. package/dist/slots/contract.js +41 -0
  46. package/dist/slots/contract.js.map +1 -0
  47. package/dist/slots/contract.mjs +4 -0
  48. package/dist/slots/contract.mjs.map +1 -0
  49. package/dist/slots/index.d.mts +104 -0
  50. package/dist/slots/index.d.ts +104 -0
  51. package/dist/slots/index.js +186 -0
  52. package/dist/slots/index.js.map +1 -0
  53. package/dist/slots/index.mjs +146 -0
  54. package/dist/slots/index.mjs.map +1 -0
  55. package/dist/{types-BnCwwUX3.d.ts → types-D_1iUxOQ.d.mts} +18 -1
  56. package/dist/{types-BnCwwUX3.d.mts → types-D_1iUxOQ.d.ts} +18 -1
  57. package/package.json +11 -1
  58. package/dist/EngageWidget-4W5EM4UO.mjs +0 -4
  59. package/dist/EngageWidget-JIXUBG47.js +0 -13
  60. package/dist/chunk-IAV32YHL.mjs.map +0 -1
  61. package/dist/chunk-SLJJJWMV.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
+ var chunkMLJXELPA_js = require('../chunk-MLJXELPA.js');
4
5
  require('../chunk-ZSMWDLMK.js');
5
6
  var React2 = require('react');
6
7
  var jsxRuntime = require('react/jsx-runtime');
@@ -326,680 +327,6 @@ async function initializeForms(forms) {
326
327
  console.error("[Site-Kit] Failed to sync forms:", error);
327
328
  }
328
329
  }
329
- function useFormTracking({
330
- formId,
331
- totalSteps,
332
- enabled = true,
333
- debug = false
334
- }) {
335
- const sessionIdRef = React2.useRef("");
336
- const startTimeRef = React2.useRef(0);
337
- const stepStartTimeRef = React2.useRef(0);
338
- const stepTimesRef = React2.useRef({});
339
- const currentStepRef = React2.useRef(1);
340
- const maxStepRef = React2.useRef(1);
341
- const analyticsIdRef = React2.useRef(null);
342
- React2.useEffect(() => {
343
- if (!enabled) return;
344
- if (!formId) {
345
- if (debug) console.warn("[Forms] Tracking skipped: formId is empty");
346
- return;
347
- }
348
- sessionIdRef.current = crypto.randomUUID();
349
- startTimeRef.current = Date.now();
350
- stepStartTimeRef.current = Date.now();
351
- const startTracking = async () => {
352
- const apiUrl = window.__SITE_KIT_API_URL__;
353
- const apiKey = window.__SITE_KIT_API_KEY__;
354
- if (!apiUrl || !apiKey) {
355
- if (debug) console.error("[Forms] Missing API URL or API key");
356
- return;
357
- }
358
- try {
359
- const response = await fetch(`${apiUrl}/api/public/forms/analytics/start`, {
360
- method: "POST",
361
- headers: {
362
- "Content-Type": "application/json",
363
- "x-api-key": apiKey
364
- },
365
- body: JSON.stringify({
366
- formId,
367
- sessionId: sessionIdRef.current,
368
- deviceType: getDeviceType()
369
- })
370
- });
371
- if (!response.ok) {
372
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
373
- }
374
- const data = await response.json();
375
- analyticsIdRef.current = data.id;
376
- if (debug) console.log("[Forms] Started tracking:", data.id);
377
- } catch (error) {
378
- if (debug) console.error("[Forms] Error starting tracking:", error);
379
- }
380
- };
381
- startTracking();
382
- const handleBeforeUnload = () => {
383
- if (!analyticsIdRef.current) return;
384
- const apiUrl = window.__SITE_KIT_API_URL__;
385
- const apiKey = window.__SITE_KIT_API_KEY__;
386
- if (!apiUrl || !apiKey) return;
387
- const now = Date.now();
388
- const currentStepTime = Math.floor((now - stepStartTimeRef.current) / 1e3);
389
- stepTimesRef.current[currentStepRef.current] = currentStepTime;
390
- const payload = JSON.stringify({
391
- analyticsId: analyticsIdRef.current,
392
- step: currentStepRef.current,
393
- maxStep: maxStepRef.current,
394
- stepTimes: stepTimesRef.current,
395
- totalTimeSeconds: Math.floor((now - startTimeRef.current) / 1e3)
396
- });
397
- const blob = new Blob([payload], { type: "application/json" });
398
- new Headers({
399
- "Content-Type": "application/json",
400
- "x-api-key": apiKey
401
- });
402
- const sent = navigator.sendBeacon(
403
- `${apiUrl}/api/public/forms/analytics/abandon`,
404
- blob
405
- );
406
- if (debug) console.log("[Forms] Abandonment tracked:", sent ? "success" : "failed");
407
- };
408
- window.addEventListener("beforeunload", handleBeforeUnload);
409
- return () => {
410
- window.removeEventListener("beforeunload", handleBeforeUnload);
411
- };
412
- }, [formId, enabled, debug]);
413
- const trackStepChange = React2.useCallback(async (step) => {
414
- if (!enabled || !analyticsIdRef.current) return;
415
- const apiUrl = window.__SITE_KIT_API_URL__;
416
- const apiKey = window.__SITE_KIT_API_KEY__;
417
- if (!apiUrl || !apiKey) {
418
- if (debug) console.error("[Forms] Missing API URL or API key");
419
- return;
420
- }
421
- const now = Date.now();
422
- const prevStepTime = Math.floor((now - stepStartTimeRef.current) / 1e3);
423
- stepTimesRef.current[currentStepRef.current] = prevStepTime;
424
- currentStepRef.current = step;
425
- maxStepRef.current = Math.max(maxStepRef.current, step);
426
- stepStartTimeRef.current = now;
427
- try {
428
- const response = await fetch(`${apiUrl}/api/public/forms/analytics/step`, {
429
- method: "POST",
430
- headers: {
431
- "Content-Type": "application/json",
432
- "x-api-key": apiKey
433
- },
434
- body: JSON.stringify({
435
- analyticsId: analyticsIdRef.current,
436
- step,
437
- maxStep: maxStepRef.current,
438
- stepTimes: stepTimesRef.current
439
- })
440
- });
441
- if (!response.ok) {
442
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
443
- }
444
- if (debug) console.log("[Forms] Step changed to:", step);
445
- } catch (error) {
446
- if (debug) console.error("[Forms] Error updating step:", error);
447
- }
448
- }, [enabled, debug]);
449
- const trackComplete = React2.useCallback(async () => {
450
- if (!enabled || !analyticsIdRef.current) return;
451
- const apiUrl = window.__SITE_KIT_API_URL__;
452
- const apiKey = window.__SITE_KIT_API_KEY__;
453
- if (!apiUrl || !apiKey) {
454
- if (debug) console.error("[Forms] Missing API URL or API key");
455
- return;
456
- }
457
- const now = Date.now();
458
- const finalStepTime = Math.floor((now - stepStartTimeRef.current) / 1e3);
459
- stepTimesRef.current[currentStepRef.current] = finalStepTime;
460
- try {
461
- const response = await fetch(`${apiUrl}/api/public/forms/analytics/complete`, {
462
- method: "POST",
463
- headers: {
464
- "Content-Type": "application/json",
465
- "x-api-key": apiKey
466
- },
467
- body: JSON.stringify({
468
- analyticsId: analyticsIdRef.current,
469
- totalSteps,
470
- stepTimes: stepTimesRef.current,
471
- totalTimeSeconds: Math.floor((now - startTimeRef.current) / 1e3)
472
- })
473
- });
474
- if (!response.ok) {
475
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
476
- }
477
- if (debug) console.log("[Forms] Form completed, recording conversion");
478
- try {
479
- await fetch(`${apiUrl}/api/public/analytics/conversion`, {
480
- method: "POST",
481
- headers: {
482
- "Content-Type": "application/json",
483
- "x-api-key": apiKey
484
- },
485
- body: JSON.stringify({
486
- sessionId: sessionIdRef.current,
487
- conversionType: "form_submission",
488
- metadata: { formId, analyticsId: analyticsIdRef.current },
489
- pagePath: typeof window !== "undefined" ? window.location.pathname : "/",
490
- referrer: typeof document !== "undefined" ? document.referrer || null : null,
491
- deviceType: getDeviceType()
492
- })
493
- });
494
- } catch (convErr) {
495
- if (debug) console.error("[Forms] Error recording conversion:", convErr);
496
- }
497
- } catch (error) {
498
- if (debug) console.error("[Forms] Error completing tracking:", error);
499
- }
500
- }, [enabled, totalSteps, formId, debug]);
501
- return {
502
- trackStepChange,
503
- trackComplete,
504
- sessionId: sessionIdRef.current
505
- };
506
- }
507
- function getDeviceType() {
508
- if (typeof window === "undefined") return "desktop";
509
- const ua = navigator.userAgent;
510
- if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
511
- if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
512
- return "desktop";
513
- }
514
-
515
- // src/forms/fetchWithRetry.ts
516
- function backoffDelay(attempt, response) {
517
- if (response?.status === 429) {
518
- const header = response.headers.get("retry-after");
519
- const seconds = header ? parseInt(header, 10) : NaN;
520
- if (!Number.isNaN(seconds) && seconds > 0) return Math.min(seconds * 1e3, 1e4);
521
- }
522
- return Math.min(8e3, 500 * 2 ** attempt);
523
- }
524
- async function fetchWithRetry(url, init, options = {}) {
525
- const retries = options.retries ?? 2;
526
- const timeoutMs = options.timeoutMs ?? 15e3;
527
- let lastError;
528
- for (let attempt = 0; attempt <= retries; attempt++) {
529
- const controller = new AbortController();
530
- const timer = setTimeout(() => controller.abort(), timeoutMs);
531
- try {
532
- const response = await fetch(url, { ...init, signal: controller.signal });
533
- clearTimeout(timer);
534
- if ((response.status === 429 || response.status >= 500) && attempt < retries) {
535
- await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt, response)));
536
- continue;
537
- }
538
- return response;
539
- } catch (error) {
540
- clearTimeout(timer);
541
- lastError = error;
542
- if (attempt < retries) {
543
- await new Promise((resolve) => setTimeout(resolve, backoffDelay(attempt)));
544
- continue;
545
- }
546
- }
547
- }
548
- throw lastError instanceof Error ? lastError : new Error("Network request failed after multiple attempts. Please try again.");
549
- }
550
-
551
- // src/forms/recaptcha.ts
552
- function getSiteKey(explicitSiteKey) {
553
- if (typeof window === "undefined") return void 0;
554
- if (explicitSiteKey) return explicitSiteKey;
555
- const win = window;
556
- return win.__SITE_KIT_RECAPTCHA_SITE_KEY__ ?? process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY;
557
- }
558
- var RECAPTCHA_LOAD_TIMEOUT_MS = 1e4;
559
- function loadScript(siteKey) {
560
- return new Promise((resolve) => {
561
- if (typeof window === "undefined") {
562
- resolve(null);
563
- return;
564
- }
565
- const win = window;
566
- if (win.grecaptcha?.enterprise) {
567
- resolve(win.grecaptcha);
568
- return;
569
- }
570
- let settled = false;
571
- const finish = (value) => {
572
- if (settled) return;
573
- settled = true;
574
- resolve(value);
575
- };
576
- const giveUp = setTimeout(() => finish(null), RECAPTCHA_LOAD_TIMEOUT_MS);
577
- const waitForReady = () => {
578
- if (settled) return;
579
- if (win.grecaptcha?.enterprise) {
580
- clearTimeout(giveUp);
581
- finish(win.grecaptcha);
582
- return;
583
- }
584
- setTimeout(waitForReady, 50);
585
- };
586
- const existing = document.querySelector('script[src*="recaptcha/enterprise"]');
587
- if (existing) {
588
- waitForReady();
589
- return;
590
- }
591
- const script = document.createElement("script");
592
- script.src = `https://www.google.com/recaptcha/enterprise.js?render=${siteKey}`;
593
- script.async = true;
594
- script.onload = () => waitForReady();
595
- script.onerror = () => {
596
- clearTimeout(giveUp);
597
- finish(null);
598
- };
599
- document.head.appendChild(script);
600
- });
601
- }
602
- async function getRecaptchaToken(explicitSiteKey) {
603
- const siteKey = getSiteKey(explicitSiteKey);
604
- if (!siteKey || typeof window === "undefined") return null;
605
- try {
606
- const grecaptcha = await loadScript(siteKey);
607
- if (!grecaptcha?.enterprise?.execute) return null;
608
- const token = await grecaptcha.enterprise.execute(siteKey, { action: "submit" });
609
- return token || null;
610
- } catch (err) {
611
- console.warn("[site-kit reCAPTCHA]", err);
612
- return null;
613
- }
614
- }
615
-
616
- // src/forms/submitForm.ts
617
- var DEFAULT_HONEYPOT_FIELD = "_sk_hp_url";
618
- function getApiConfig() {
619
- const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.sonor.io" : "https://api.sonor.io";
620
- const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
621
- return { apiUrl, apiKey };
622
- }
623
- function getUTMParams() {
624
- if (typeof window === "undefined") return {};
625
- const params = new URLSearchParams(window.location.search);
626
- const out = {};
627
- for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
628
- out[key] = params.get(key);
629
- }
630
- return out;
631
- }
632
- async function submitForm({
633
- config: config2,
634
- values,
635
- honeypotValue,
636
- formLoadedAt
637
- }) {
638
- const { apiUrl, apiKey } = getApiConfig();
639
- if (!apiKey) {
640
- throw new Error("API key is required. Set SONOR_API_KEY in your environment.");
641
- }
642
- const recaptchaToken = config2.recaptcha_enabled ? await getRecaptchaToken(config2.recaptcha_site_key) : null;
643
- if (config2.recaptcha_enabled && !recaptchaToken) {
644
- throw new Error("Verification could not be completed. Please try again.");
645
- }
646
- const honeypotFieldName = config2.honeypot_field || DEFAULT_HONEYPOT_FIELD;
647
- const utm = getUTMParams();
648
- const payload = {
649
- formId: config2.id,
650
- form_id: config2.id,
651
- data: {
652
- ...values,
653
- ...config2.honeypot_enabled ? { [honeypotFieldName]: honeypotValue } : {}
654
- },
655
- metadata: {
656
- pageUrl: typeof window !== "undefined" ? window.location.href : null,
657
- referrer: typeof document !== "undefined" ? document.referrer || null : null,
658
- userAgent: typeof navigator !== "undefined" ? navigator.userAgent : null,
659
- sessionId: typeof sessionStorage !== "undefined" ? sessionStorage.getItem("_sk_sid") : null,
660
- recaptchaToken: recaptchaToken || void 0,
661
- utmSource: utm.utm_source,
662
- utmMedium: utm.utm_medium,
663
- utmCampaign: utm.utm_campaign,
664
- // Sub-site host — preserved on form_submissions for lead attribution
665
- // across multi-site projects. Resolved by SiteKitClientProviders.
666
- site: typeof window !== "undefined" ? window.__SITE_KIT_SITE__ || window.location.host : void 0,
667
- _formLoadedAt: formLoadedAt
668
- }
669
- };
670
- const response = await fetchWithRetry(
671
- `${apiUrl}/api/public/forms/submit`,
672
- {
673
- method: "POST",
674
- headers: {
675
- "Content-Type": "application/json",
676
- Authorization: `Bearer ${apiKey}`
677
- },
678
- body: JSON.stringify(payload)
679
- },
680
- { retries: 2, timeoutMs: 2e4 }
681
- );
682
- const data = await response.json().catch(() => null);
683
- if (!response.ok) {
684
- const msg = (data?.message ?? data?.error ?? (response.statusText || `HTTP ${response.status}`)).toString().trim();
685
- throw new Error(
686
- msg ? `Failed to submit form: ${msg}` : `Failed to submit form (${response.status})`
687
- );
688
- }
689
- return data;
690
- }
691
-
692
- // src/forms/defaultSuccessMessage.ts
693
- var DEFAULT_FORM_SUCCESS_MESSAGE = "Thank you for your submission! We will be in touch soon.";
694
-
695
- // src/forms/useForm.ts
696
- function useForm(formIdOrSlug, options = {}) {
697
- const {
698
- projectId: optionsProjectId,
699
- onSuccess,
700
- onError,
701
- initialValues = {},
702
- redirectUrl
703
- } = options;
704
- const projectId = optionsProjectId || (typeof window !== "undefined" ? window.__SITE_KIT_PROJECT_ID__ : void 0);
705
- const [form, setForm] = React2.useState(null);
706
- const [isLoading, setIsLoading] = React2.useState(true);
707
- const [fetchError, setFetchError] = React2.useState(null);
708
- const [values, setValuesState] = React2.useState(initialValues);
709
- const [errors, setErrors] = React2.useState({});
710
- const [step, setStep] = React2.useState(1);
711
- const [isSubmitting, setIsSubmitting] = React2.useState(false);
712
- const [isComplete, setIsComplete] = React2.useState(false);
713
- const [submitError, setSubmitError] = React2.useState(null);
714
- const isSubmittingRef = React2.useRef(false);
715
- const formLoadedAt = React2.useRef(Date.now());
716
- const totalSteps = form?.total_steps || 1;
717
- const isMultiStep = form?.is_multi_step || false;
718
- const { trackStepChange, trackComplete } = useFormTracking({
719
- formId: form?.id || "",
720
- totalSteps
721
- });
722
- React2.useEffect(() => {
723
- const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.sonor.io" : "https://api.sonor.io";
724
- async function fetchForm() {
725
- try {
726
- setIsLoading(true);
727
- setFetchError(null);
728
- const readApiKey = () => typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
729
- let apiKey = readApiKey();
730
- for (let i = 0; i < 15 && !apiKey; i++) {
731
- await new Promise((r) => setTimeout(r, 100));
732
- apiKey = readApiKey();
733
- }
734
- if (!apiKey) {
735
- throw new Error("API key is required. Provide it via SiteKitLayout/SiteKitProvider or set SONOR_API_KEY.");
736
- }
737
- const url = `${apiUrl}/api/public/forms/config`;
738
- const init = {
739
- method: "POST",
740
- headers: {
741
- "Content-Type": "application/json",
742
- Authorization: `Bearer ${apiKey}`
743
- },
744
- body: JSON.stringify({
745
- formIdOrSlug
746
- })
747
- };
748
- const response = await fetchWithRetry(url, init, { retries: 3, timeoutMs: 15e3 });
749
- if (!response.ok) {
750
- throw new Error(`Failed to fetch form: ${response.statusText}`);
751
- }
752
- const data = await response.json();
753
- if (!data) throw new Error("Form not found");
754
- if (typeof window !== "undefined" && data.recaptcha_site_key) {
755
- ;
756
- window.__SITE_KIT_RECAPTCHA_SITE_KEY__ = data.recaptcha_site_key;
757
- }
758
- if (data.steps) {
759
- data.steps.sort((a, b) => (a.step_number || 0) - (b.step_number || 0));
760
- }
761
- if (data.fields) {
762
- data.fields.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
763
- }
764
- const total_steps = data.steps?.length || 1;
765
- const is_multi_step = total_steps > 1;
766
- const apiSuccess = typeof data.success_message === "string" ? data.success_message.trim() : "";
767
- const success_message = apiSuccess || (typeof data.successMessage === "string" ? String(data.successMessage).trim() : "") || DEFAULT_FORM_SUCCESS_MESSAGE;
768
- setForm({
769
- ...data,
770
- success_message,
771
- total_steps,
772
- is_multi_step
773
- });
774
- const defaults = {};
775
- for (const field2 of data.fields || []) {
776
- if (field2.default_value !== void 0 && field2.default_value !== null) {
777
- defaults[field2.slug] = field2.default_value;
778
- }
779
- }
780
- setValuesState({ ...defaults, ...initialValues });
781
- } catch (err) {
782
- setFetchError(err);
783
- } finally {
784
- setIsLoading(false);
785
- }
786
- }
787
- fetchForm();
788
- }, [formIdOrSlug, projectId]);
789
- const allFields = React2.useMemo(() => form?.fields || [], [form]);
790
- const fields = React2.useMemo(() => {
791
- if (!isMultiStep) return allFields;
792
- const currentStepConfig = form?.steps?.find((s) => s.step_number === step);
793
- if (!currentStepConfig) return allFields;
794
- return allFields.filter((f) => f.step_id === currentStepConfig.id);
795
- }, [form, step, allFields, isMultiStep]);
796
- const isFieldVisible = React2.useCallback((field2) => {
797
- if (!field2.conditional?.show_when) return true;
798
- const { field: condField, operator, value } = field2.conditional.show_when;
799
- const fieldValue = values[condField];
800
- switch (operator) {
801
- case "equals":
802
- return fieldValue === value;
803
- case "not_equals":
804
- return fieldValue !== value;
805
- case "contains":
806
- return String(fieldValue || "").includes(String(value));
807
- case "not_contains":
808
- return !String(fieldValue || "").includes(String(value));
809
- case "is_empty":
810
- return !fieldValue || fieldValue === "";
811
- case "not_empty":
812
- return !!fieldValue && fieldValue !== "";
813
- case "greater_than":
814
- return Number(fieldValue) > Number(value);
815
- case "less_than":
816
- return Number(fieldValue) < Number(value);
817
- default:
818
- return true;
819
- }
820
- }, [values]);
821
- const visibleFields = React2.useMemo(() => {
822
- return fields.filter(isFieldVisible);
823
- }, [fields, isFieldVisible]);
824
- const validateField = React2.useCallback((field2) => {
825
- if (!isFieldVisible(field2)) return null;
826
- if (["heading", "section_header", "paragraph", "hidden"].includes(field2.field_type)) return null;
827
- const value = values[field2.slug];
828
- const rules = field2.validation || {};
829
- if (field2.is_required && (!value || value === "")) {
830
- return rules.custom_error || `${field2.label} is required`;
831
- }
832
- if (!value) return null;
833
- const strValue = String(value);
834
- if (rules.min_length && strValue.length < rules.min_length) {
835
- return `${field2.label} must be at least ${rules.min_length} characters`;
836
- }
837
- if (rules.max_length && strValue.length > rules.max_length) {
838
- return `${field2.label} must be no more than ${rules.max_length} characters`;
839
- }
840
- if (rules.min !== void 0 && Number(value) < rules.min) {
841
- return `${field2.label} must be at least ${rules.min}`;
842
- }
843
- if (rules.max !== void 0 && Number(value) > rules.max) {
844
- return `${field2.label} must be no more than ${rules.max}`;
845
- }
846
- if (rules.pattern) {
847
- try {
848
- if (!new RegExp(rules.pattern).test(strValue)) {
849
- return rules.custom_error || `${field2.label} is invalid`;
850
- }
851
- } catch {
852
- }
853
- }
854
- if (field2.field_type === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(strValue)) {
855
- return "Please enter a valid email address";
856
- }
857
- if (field2.field_type === "phone" && !/^[\d\s\-\+\(\)]+$/.test(strValue)) {
858
- return "Please enter a valid phone number";
859
- }
860
- return null;
861
- }, [values, isFieldVisible]);
862
- const validate = React2.useCallback(() => {
863
- const newErrors = {};
864
- for (const field2 of fields) {
865
- const error = validateField(field2);
866
- if (error) {
867
- newErrors[field2.slug] = error;
868
- }
869
- }
870
- setErrors(newErrors);
871
- return Object.keys(newErrors).length === 0;
872
- }, [fields, validateField]);
873
- const validateAll = React2.useCallback(() => {
874
- const newErrors = {};
875
- for (const field2 of allFields) {
876
- const error = validateField(field2);
877
- if (error) newErrors[field2.slug] = error;
878
- }
879
- setErrors(newErrors);
880
- return Object.keys(newErrors).length === 0;
881
- }, [allFields, validateField]);
882
- const buildVisibleValues = React2.useCallback(() => {
883
- const out = {};
884
- for (const field2 of allFields) {
885
- if (!isFieldVisible(field2)) continue;
886
- if (field2.slug in values) out[field2.slug] = values[field2.slug];
887
- }
888
- return out;
889
- }, [allFields, values, isFieldVisible]);
890
- const setFieldValue = React2.useCallback((key, value) => {
891
- setValuesState((prev) => ({ ...prev, [key]: value }));
892
- setErrors((prev) => {
893
- if (prev[key]) {
894
- const next = { ...prev };
895
- delete next[key];
896
- return next;
897
- }
898
- return prev;
899
- });
900
- }, []);
901
- const setValues = React2.useCallback((newValues) => {
902
- setValuesState((prev) => ({ ...prev, ...newValues }));
903
- }, []);
904
- const clearErrors = React2.useCallback(() => {
905
- setErrors({});
906
- }, []);
907
- const canGoNext = step < totalSteps;
908
- const canGoPrev = step > 1;
909
- const isLastStep = step === totalSteps;
910
- const progress = Math.round(step / totalSteps * 100);
911
- const nextStep = React2.useCallback(() => {
912
- if (!validate()) return false;
913
- if (step < totalSteps) {
914
- const newStep = step + 1;
915
- setStep(newStep);
916
- trackStepChange(newStep);
917
- return true;
918
- }
919
- return false;
920
- }, [step, totalSteps, validate, trackStepChange]);
921
- const prevStep = React2.useCallback(() => {
922
- if (step > 1) {
923
- const newStep = step - 1;
924
- setStep(newStep);
925
- trackStepChange(newStep);
926
- }
927
- }, [step, trackStepChange]);
928
- const goToStep = React2.useCallback((targetStep) => {
929
- if (targetStep >= 1 && targetStep <= totalSteps) {
930
- setStep(targetStep);
931
- trackStepChange(targetStep);
932
- }
933
- }, [totalSteps, trackStepChange]);
934
- const submit = React2.useCallback(async () => {
935
- if (!form) return;
936
- if (isSubmittingRef.current) return;
937
- if (!validateAll()) return;
938
- isSubmittingRef.current = true;
939
- setIsSubmitting(true);
940
- setSubmitError(null);
941
- try {
942
- const data = await submitForm({
943
- config: form,
944
- values: buildVisibleValues(),
945
- honeypotValue: "",
946
- formLoadedAt: formLoadedAt.current
947
- });
948
- trackComplete();
949
- setIsComplete(true);
950
- onSuccess?.(data);
951
- const finalRedirect = redirectUrl !== false ? redirectUrl || form.redirect_url : void 0;
952
- if (finalRedirect) {
953
- window.location.href = finalRedirect;
954
- }
955
- } catch (error) {
956
- console.error("[useForm] Submission error:", error);
957
- setSubmitError("Something went wrong sending your submission. Please try again.");
958
- onError?.(error);
959
- } finally {
960
- isSubmittingRef.current = false;
961
- setIsSubmitting(false);
962
- }
963
- }, [form, validateAll, buildVisibleValues, trackComplete, onSuccess, onError, redirectUrl]);
964
- const reset = React2.useCallback(() => {
965
- setValuesState(initialValues);
966
- setErrors({});
967
- setStep(1);
968
- setIsComplete(false);
969
- setSubmitError(null);
970
- }, [initialValues]);
971
- return {
972
- form,
973
- isLoading,
974
- fetchError,
975
- submitError,
976
- allFields,
977
- fields,
978
- visibleFields,
979
- values,
980
- errors,
981
- setFieldValue,
982
- setValues,
983
- clearErrors,
984
- step,
985
- totalSteps,
986
- isMultiStep,
987
- progress,
988
- nextStep,
989
- prevStep,
990
- goToStep,
991
- canGoNext,
992
- canGoPrev,
993
- isLastStep,
994
- validate,
995
- validateField,
996
- isFieldVisible,
997
- submit,
998
- isSubmitting,
999
- isComplete,
1000
- reset
1001
- };
1002
- }
1003
330
  var DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
1004
331
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
1005
332
  function pad(n) {
@@ -1565,7 +892,7 @@ async function uploadFormFile(file) {
1565
892
  }
1566
893
  const body = new FormData();
1567
894
  body.append("file", file);
1568
- const response = await fetchWithRetry(
895
+ const response = await chunkMLJXELPA_js.fetchWithRetry(
1569
896
  `${apiUrl}/api/public/forms/upload`,
1570
897
  {
1571
898
  method: "POST",
@@ -1597,10 +924,10 @@ function FormClient({
1597
924
  const [isComplete, setIsComplete] = React2.useState(false);
1598
925
  const [submitError, setSubmitError] = React2.useState(null);
1599
926
  const [honeypotValue, setHoneypotValue] = React2.useState("");
1600
- const honeypotFieldName = config2.honeypot_field || DEFAULT_HONEYPOT_FIELD;
927
+ const honeypotFieldName = config2.honeypot_field || chunkMLJXELPA_js.DEFAULT_HONEYPOT_FIELD;
1601
928
  const formLoadedAt = React2.useRef(Date.now());
1602
929
  const isSubmittingRef = React2.useRef(false);
1603
- const { trackStepChange, trackComplete } = useFormTracking({
930
+ const { trackStepChange, trackComplete } = chunkMLJXELPA_js.useFormTracking({
1604
931
  formId: config2.id,
1605
932
  totalSteps: config2.total_steps
1606
933
  });
@@ -1759,7 +1086,7 @@ function FormClient({
1759
1086
  setIsSubmitting(true);
1760
1087
  setSubmitError(null);
1761
1088
  try {
1762
- const data = await submitForm({
1089
+ const data = await chunkMLJXELPA_js.submitForm({
1763
1090
  config: config2,
1764
1091
  values: buildVisibleValues(),
1765
1092
  honeypotValue: config2.honeypot_enabled ? honeypotValue : "",
@@ -1801,7 +1128,7 @@ function FormClient({
1801
1128
  };
1802
1129
  if (isComplete) {
1803
1130
  const raw = config2.success_message;
1804
- const message = typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : DEFAULT_FORM_SUCCESS_MESSAGE;
1131
+ const message = typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : chunkMLJXELPA_js.DEFAULT_FORM_SUCCESS_MESSAGE;
1805
1132
  return /* @__PURE__ */ jsxRuntime.jsx(
1806
1133
  "div",
1807
1134
  {
@@ -1924,7 +1251,7 @@ function ManagedForm({
1924
1251
  form,
1925
1252
  isLoading,
1926
1253
  fetchError
1927
- } = useForm(formId, {
1254
+ } = chunkMLJXELPA_js.useForm(formId, {
1928
1255
  projectId,
1929
1256
  onSuccess,
1930
1257
  onError
@@ -1947,7 +1274,18 @@ function ManagedForm({
1947
1274
  );
1948
1275
  }
1949
1276
 
1950
- exports.DEFAULT_FORM_SUCCESS_MESSAGE = DEFAULT_FORM_SUCCESS_MESSAGE;
1277
+ Object.defineProperty(exports, "DEFAULT_FORM_SUCCESS_MESSAGE", {
1278
+ enumerable: true,
1279
+ get: function () { return chunkMLJXELPA_js.DEFAULT_FORM_SUCCESS_MESSAGE; }
1280
+ });
1281
+ Object.defineProperty(exports, "useForm", {
1282
+ enumerable: true,
1283
+ get: function () { return chunkMLJXELPA_js.useForm; }
1284
+ });
1285
+ Object.defineProperty(exports, "useFormTracking", {
1286
+ enumerable: true,
1287
+ get: function () { return chunkMLJXELPA_js.useFormTracking; }
1288
+ });
1951
1289
  exports.DatePicker = DatePicker;
1952
1290
  exports.FormClient = FormClient;
1953
1291
  exports.FormFieldComponent = FormField;
@@ -1957,7 +1295,5 @@ exports.defineForm = defineForm;
1957
1295
  exports.field = field;
1958
1296
  exports.formsApi = formsApi;
1959
1297
  exports.initializeForms = initializeForms;
1960
- exports.useForm = useForm;
1961
- exports.useFormTracking = useFormTracking;
1962
1298
  //# sourceMappingURL=index.js.map
1963
1299
  //# sourceMappingURL=index.js.map