@uptrademedia/site-kit 1.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 (120) hide show
  1. package/README.md +305 -0
  2. package/dist/analytics/index.js +88 -0
  3. package/dist/analytics/index.js.map +1 -0
  4. package/dist/analytics/index.mjs +70 -0
  5. package/dist/analytics/index.mjs.map +1 -0
  6. package/dist/api-N35S3EES.js +57 -0
  7. package/dist/api-N35S3EES.js.map +1 -0
  8. package/dist/api-SYBTK7Z7.mjs +4 -0
  9. package/dist/api-SYBTK7Z7.mjs.map +1 -0
  10. package/dist/blog/index.js +200 -0
  11. package/dist/blog/index.js.map +1 -0
  12. package/dist/blog/index.mjs +194 -0
  13. package/dist/blog/index.mjs.map +1 -0
  14. package/dist/chunk-3MUOUXHV.js +3721 -0
  15. package/dist/chunk-3MUOUXHV.js.map +1 -0
  16. package/dist/chunk-4HVYXYQL 2.mjs +255 -0
  17. package/dist/chunk-4HVYXYQL.mjs +255 -0
  18. package/dist/chunk-4HVYXYQL.mjs.map +1 -0
  19. package/dist/chunk-7H6I3ECV.mjs +120 -0
  20. package/dist/chunk-7H6I3ECV.mjs.map +1 -0
  21. package/dist/chunk-COI6GOX2.mjs +3679 -0
  22. package/dist/chunk-COI6GOX2.mjs.map +1 -0
  23. package/dist/chunk-EQCVQC35.js +35 -0
  24. package/dist/chunk-EQCVQC35.js 2.map +1 -0
  25. package/dist/chunk-EQCVQC35.js.map +1 -0
  26. package/dist/chunk-FEBYQGY4 2.mjs +251 -0
  27. package/dist/chunk-FEBYQGY4.mjs +251 -0
  28. package/dist/chunk-FEBYQGY4.mjs.map +1 -0
  29. package/dist/chunk-FKVJOT2F.mjs +796 -0
  30. package/dist/chunk-FKVJOT2F.mjs.map +1 -0
  31. package/dist/chunk-GQ6ZOU2N.mjs +134 -0
  32. package/dist/chunk-GQ6ZOU2N.mjs.map +1 -0
  33. package/dist/chunk-HCFPU7TU.js +137 -0
  34. package/dist/chunk-HCFPU7TU.js.map +1 -0
  35. package/dist/chunk-NYKRE2FL 2.mjs +31 -0
  36. package/dist/chunk-NYKRE2FL.mjs +31 -0
  37. package/dist/chunk-NYKRE2FL.mjs 2.map +1 -0
  38. package/dist/chunk-NYKRE2FL.mjs.map +1 -0
  39. package/dist/chunk-QP5NCO2E.js +133 -0
  40. package/dist/chunk-QP5NCO2E.js.map +1 -0
  41. package/dist/chunk-RV7H3I6J.js +255 -0
  42. package/dist/chunk-RV7H3I6J.js 2.map +1 -0
  43. package/dist/chunk-RV7H3I6J.js.map +1 -0
  44. package/dist/chunk-SBVEYCSV.js +140 -0
  45. package/dist/chunk-SBVEYCSV.js.map +1 -0
  46. package/dist/chunk-TUKGA3UK.js +257 -0
  47. package/dist/chunk-TUKGA3UK.js 2.map +1 -0
  48. package/dist/chunk-TUKGA3UK.js.map +1 -0
  49. package/dist/chunk-V3F5J6CV.js +801 -0
  50. package/dist/chunk-V3F5J6CV.js.map +1 -0
  51. package/dist/chunk-WPSRS352.mjs +135 -0
  52. package/dist/chunk-WPSRS352.mjs.map +1 -0
  53. package/dist/commerce/index.js +157 -0
  54. package/dist/commerce/index.js.map +1 -0
  55. package/dist/commerce/index.mjs +4 -0
  56. package/dist/commerce/index.mjs.map +1 -0
  57. package/dist/commerce/server.js +186 -0
  58. package/dist/commerce/server.js.map +1 -0
  59. package/dist/commerce/server.mjs +176 -0
  60. package/dist/commerce/server.mjs.map +1 -0
  61. package/dist/engage/index.js +50 -0
  62. package/dist/engage/index.js.map +1 -0
  63. package/dist/engage/index.mjs +44 -0
  64. package/dist/engage/index.mjs.map +1 -0
  65. package/dist/forms/index.js +1053 -0
  66. package/dist/forms/index.js.map +1 -0
  67. package/dist/forms/index.mjs +1035 -0
  68. package/dist/forms/index.mjs.map +1 -0
  69. package/dist/generators-7Y5ABRYV 2.mjs +161 -0
  70. package/dist/generators-7Y5ABRYV.mjs +161 -0
  71. package/dist/generators-7Y5ABRYV.mjs 2.map +1 -0
  72. package/dist/generators-7Y5ABRYV.mjs.map +1 -0
  73. package/dist/generators-GWIYCA5M.js +171 -0
  74. package/dist/generators-GWIYCA5M.js 2.map +1 -0
  75. package/dist/generators-GWIYCA5M.js.map +1 -0
  76. package/dist/index 2.mjs +74 -0
  77. package/dist/index.js +326 -0
  78. package/dist/index.js 2.map +1 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/index.mjs +222 -0
  81. package/dist/index.mjs.map +1 -0
  82. package/dist/migrator-V6KS75EA 2.mjs +265 -0
  83. package/dist/migrator-V6KS75EA.mjs +265 -0
  84. package/dist/migrator-V6KS75EA.mjs 2.map +1 -0
  85. package/dist/migrator-V6KS75EA.mjs.map +1 -0
  86. package/dist/migrator-XKM7YQCY.js +272 -0
  87. package/dist/migrator-XKM7YQCY.js 2.map +1 -0
  88. package/dist/migrator-XKM7YQCY.js.map +1 -0
  89. package/dist/scanner-MF7P3CDE 2.mjs +14386 -0
  90. package/dist/scanner-MF7P3CDE.mjs +14386 -0
  91. package/dist/scanner-MF7P3CDE.mjs 2.map +1 -0
  92. package/dist/scanner-MF7P3CDE.mjs.map +1 -0
  93. package/dist/scanner-NT6YG4TD 2.js +14397 -0
  94. package/dist/scanner-NT6YG4TD.js +14397 -0
  95. package/dist/scanner-NT6YG4TD.js 2.map +1 -0
  96. package/dist/scanner-NT6YG4TD.js.map +1 -0
  97. package/dist/seo/index.js +447 -0
  98. package/dist/seo/index.js.map +1 -0
  99. package/dist/seo/index.mjs +411 -0
  100. package/dist/seo/index.mjs.map +1 -0
  101. package/dist/seo/server.js +66 -0
  102. package/dist/seo/server.js.map +1 -0
  103. package/dist/seo/server.mjs +5 -0
  104. package/dist/seo/server.mjs.map +1 -0
  105. package/dist/setup/index.js +1050 -0
  106. package/dist/setup/index.js.map +1 -0
  107. package/dist/setup/index.mjs +1046 -0
  108. package/dist/setup/index.mjs.map +1 -0
  109. package/dist/sitemap/index.js +212 -0
  110. package/dist/sitemap/index.js.map +1 -0
  111. package/dist/sitemap/index.mjs +206 -0
  112. package/dist/sitemap/index.mjs.map +1 -0
  113. package/dist/web-vitals-BH55V7EJ.js +252 -0
  114. package/dist/web-vitals-BH55V7EJ.js 2.map +1 -0
  115. package/dist/web-vitals-BH55V7EJ.js.map +1 -0
  116. package/dist/web-vitals-RJYPWAR3 2.mjs +241 -0
  117. package/dist/web-vitals-RJYPWAR3.mjs +241 -0
  118. package/dist/web-vitals-RJYPWAR3.mjs 2.map +1 -0
  119. package/dist/web-vitals-RJYPWAR3.mjs.map +1 -0
  120. package/package.json +118 -0
@@ -0,0 +1,1035 @@
1
+ export { configureFormsApi, field, formsApi } from '../chunk-FEBYQGY4.mjs';
2
+ import '../chunk-NYKRE2FL.mjs';
3
+ import { useRef, useEffect, useCallback, useState, useMemo } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ function useFormTracking({
7
+ formId,
8
+ totalSteps,
9
+ enabled = true,
10
+ debug = false
11
+ }) {
12
+ const sessionIdRef = useRef("");
13
+ const startTimeRef = useRef(0);
14
+ const stepStartTimeRef = useRef(0);
15
+ const stepTimesRef = useRef({});
16
+ const currentStepRef = useRef(1);
17
+ const maxStepRef = useRef(1);
18
+ const analyticsIdRef = useRef(null);
19
+ useEffect(() => {
20
+ if (!enabled) return;
21
+ if (!formId) {
22
+ if (debug) console.warn("[Forms] Tracking skipped: formId is empty");
23
+ return;
24
+ }
25
+ sessionIdRef.current = crypto.randomUUID();
26
+ startTimeRef.current = Date.now();
27
+ stepStartTimeRef.current = Date.now();
28
+ const startTracking = async () => {
29
+ const apiUrl = window.__SITE_KIT_API_URL__;
30
+ const apiKey = window.__SITE_KIT_API_KEY__;
31
+ if (!apiUrl || !apiKey) {
32
+ if (debug) console.error("[Forms] Missing API URL or API key");
33
+ return;
34
+ }
35
+ try {
36
+ const response = await fetch(`${apiUrl}/api/public/forms/analytics/start`, {
37
+ method: "POST",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ "x-api-key": apiKey
41
+ },
42
+ body: JSON.stringify({
43
+ formId,
44
+ sessionId: sessionIdRef.current,
45
+ deviceType: getDeviceType()
46
+ })
47
+ });
48
+ if (!response.ok) {
49
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
50
+ }
51
+ const data = await response.json();
52
+ analyticsIdRef.current = data.id;
53
+ if (debug) console.log("[Forms] Started tracking:", data.id);
54
+ } catch (error) {
55
+ if (debug) console.error("[Forms] Error starting tracking:", error);
56
+ }
57
+ };
58
+ startTracking();
59
+ const handleBeforeUnload = () => {
60
+ if (!analyticsIdRef.current) return;
61
+ const apiUrl = window.__SITE_KIT_API_URL__;
62
+ const apiKey = window.__SITE_KIT_API_KEY__;
63
+ if (!apiUrl || !apiKey) return;
64
+ const now = Date.now();
65
+ const currentStepTime = Math.floor((now - stepStartTimeRef.current) / 1e3);
66
+ stepTimesRef.current[currentStepRef.current] = currentStepTime;
67
+ const payload = JSON.stringify({
68
+ analyticsId: analyticsIdRef.current,
69
+ step: currentStepRef.current,
70
+ maxStep: maxStepRef.current,
71
+ stepTimes: stepTimesRef.current,
72
+ totalTimeSeconds: Math.floor((now - startTimeRef.current) / 1e3)
73
+ });
74
+ const blob = new Blob([payload], { type: "application/json" });
75
+ new Headers({
76
+ "Content-Type": "application/json",
77
+ "x-api-key": apiKey
78
+ });
79
+ const sent = navigator.sendBeacon(
80
+ `${apiUrl}/api/public/forms/analytics/abandon`,
81
+ blob
82
+ );
83
+ if (debug) console.log("[Forms] Abandonment tracked:", sent ? "success" : "failed");
84
+ };
85
+ window.addEventListener("beforeunload", handleBeforeUnload);
86
+ return () => {
87
+ window.removeEventListener("beforeunload", handleBeforeUnload);
88
+ };
89
+ }, [formId, enabled, debug]);
90
+ const trackStepChange = useCallback(async (step) => {
91
+ if (!enabled || !analyticsIdRef.current) return;
92
+ const apiUrl = window.__SITE_KIT_API_URL__;
93
+ const apiKey = window.__SITE_KIT_API_KEY__;
94
+ if (!apiUrl || !apiKey) {
95
+ if (debug) console.error("[Forms] Missing API URL or API key");
96
+ return;
97
+ }
98
+ const now = Date.now();
99
+ const prevStepTime = Math.floor((now - stepStartTimeRef.current) / 1e3);
100
+ stepTimesRef.current[currentStepRef.current] = prevStepTime;
101
+ currentStepRef.current = step;
102
+ maxStepRef.current = Math.max(maxStepRef.current, step);
103
+ stepStartTimeRef.current = now;
104
+ try {
105
+ const response = await fetch(`${apiUrl}/api/public/forms/analytics/step`, {
106
+ method: "POST",
107
+ headers: {
108
+ "Content-Type": "application/json",
109
+ "x-api-key": apiKey
110
+ },
111
+ body: JSON.stringify({
112
+ analyticsId: analyticsIdRef.current,
113
+ step,
114
+ maxStep: maxStepRef.current,
115
+ stepTimes: stepTimesRef.current
116
+ })
117
+ });
118
+ if (!response.ok) {
119
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
120
+ }
121
+ if (debug) console.log("[Forms] Step changed to:", step);
122
+ } catch (error) {
123
+ if (debug) console.error("[Forms] Error updating step:", error);
124
+ }
125
+ }, [enabled, debug]);
126
+ const trackComplete = useCallback(async () => {
127
+ if (!enabled || !analyticsIdRef.current) return;
128
+ const apiUrl = window.__SITE_KIT_API_URL__;
129
+ const apiKey = window.__SITE_KIT_API_KEY__;
130
+ if (!apiUrl || !apiKey) {
131
+ if (debug) console.error("[Forms] Missing API URL or API key");
132
+ return;
133
+ }
134
+ const now = Date.now();
135
+ const finalStepTime = Math.floor((now - stepStartTimeRef.current) / 1e3);
136
+ stepTimesRef.current[currentStepRef.current] = finalStepTime;
137
+ try {
138
+ const response = await fetch(`${apiUrl}/api/public/forms/analytics/complete`, {
139
+ method: "POST",
140
+ headers: {
141
+ "Content-Type": "application/json",
142
+ "x-api-key": apiKey
143
+ },
144
+ body: JSON.stringify({
145
+ analyticsId: analyticsIdRef.current,
146
+ totalSteps,
147
+ stepTimes: stepTimesRef.current,
148
+ totalTimeSeconds: Math.floor((now - startTimeRef.current) / 1e3)
149
+ })
150
+ });
151
+ if (!response.ok) {
152
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
153
+ }
154
+ if (debug) console.log("[Forms] Form completed (conversion auto-logged via trigger)");
155
+ } catch (error) {
156
+ if (debug) console.error("[Forms] Error completing tracking:", error);
157
+ }
158
+ }, [enabled, totalSteps, formId, debug]);
159
+ return {
160
+ trackStepChange,
161
+ trackComplete,
162
+ sessionId: sessionIdRef.current
163
+ };
164
+ }
165
+ function getDeviceType() {
166
+ if (typeof window === "undefined") return "desktop";
167
+ const ua = navigator.userAgent;
168
+ if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
169
+ if (/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(ua)) return "mobile";
170
+ return "desktop";
171
+ }
172
+
173
+ // src/forms/useForm.ts
174
+ function getUTMParams() {
175
+ if (typeof window === "undefined") return {};
176
+ const params = new URLSearchParams(window.location.search);
177
+ return {
178
+ utm_source: params.get("utm_source"),
179
+ utm_medium: params.get("utm_medium"),
180
+ utm_campaign: params.get("utm_campaign"),
181
+ utm_term: params.get("utm_term"),
182
+ utm_content: params.get("utm_content")
183
+ };
184
+ }
185
+ function useForm(formIdOrSlug, options = {}) {
186
+ const {
187
+ projectId: optionsProjectId,
188
+ onSuccess,
189
+ onError,
190
+ initialValues = {},
191
+ redirectUrl
192
+ } = options;
193
+ const projectId = optionsProjectId || (typeof window !== "undefined" ? window.__SITE_KIT_PROJECT_ID__ : void 0);
194
+ const [form, setForm] = useState(null);
195
+ const [isLoading, setIsLoading] = useState(true);
196
+ const [fetchError, setFetchError] = useState(null);
197
+ const [values, setValuesState] = useState(initialValues);
198
+ const [errors, setErrors] = useState({});
199
+ const [step, setStep] = useState(1);
200
+ const [isSubmitting, setIsSubmitting] = useState(false);
201
+ const [isComplete, setIsComplete] = useState(false);
202
+ const totalSteps = form?.total_steps || 1;
203
+ const isMultiStep = form?.is_multi_step || false;
204
+ const { trackStepChange, trackComplete } = useFormTracking({
205
+ formId: form?.id || "",
206
+ totalSteps
207
+ });
208
+ useEffect(() => {
209
+ if (!projectId) {
210
+ setFetchError(new Error("projectId is required. Provide it via SiteKitProvider or useForm options."));
211
+ setIsLoading(false);
212
+ return;
213
+ }
214
+ async function fetchForm() {
215
+ try {
216
+ setIsLoading(true);
217
+ const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
218
+ const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
219
+ if (!apiKey) {
220
+ throw new Error("API key is required. Set NEXT_PUBLIC_UPTRADE_API_KEY in your .env");
221
+ }
222
+ const response = await fetch(`${apiUrl}/api/public/forms/config`, {
223
+ method: "POST",
224
+ headers: {
225
+ "Content-Type": "application/json",
226
+ "Authorization": `Bearer ${apiKey}`
227
+ },
228
+ body: JSON.stringify({
229
+ formIdOrSlug
230
+ })
231
+ });
232
+ if (!response.ok) {
233
+ throw new Error(`Failed to fetch form: ${response.statusText}`);
234
+ }
235
+ const data = await response.json();
236
+ if (!data) throw new Error("Form not found");
237
+ if (data.steps) {
238
+ data.steps.sort((a, b) => (a.step_number || 0) - (b.step_number || 0));
239
+ }
240
+ if (data.fields) {
241
+ data.fields.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
242
+ }
243
+ const total_steps = data.steps?.length || 1;
244
+ const is_multi_step = total_steps > 1;
245
+ setForm({
246
+ ...data,
247
+ total_steps,
248
+ is_multi_step
249
+ });
250
+ const defaults = {};
251
+ for (const field2 of data.fields || []) {
252
+ if (field2.default_value !== void 0 && field2.default_value !== null) {
253
+ defaults[field2.slug] = field2.default_value;
254
+ }
255
+ }
256
+ setValuesState({ ...defaults, ...initialValues });
257
+ } catch (err) {
258
+ setFetchError(err);
259
+ } finally {
260
+ setIsLoading(false);
261
+ }
262
+ }
263
+ fetchForm();
264
+ }, [formIdOrSlug, projectId]);
265
+ const allFields = useMemo(() => form?.fields || [], [form]);
266
+ const fields = useMemo(() => {
267
+ if (!isMultiStep) return allFields;
268
+ const currentStepConfig = form?.steps?.find((s) => s.step_number === step);
269
+ if (!currentStepConfig) return allFields;
270
+ return allFields.filter((f) => f.step_id === currentStepConfig.id);
271
+ }, [form, step, allFields, isMultiStep]);
272
+ const isFieldVisible = useCallback((field2) => {
273
+ if (!field2.conditional?.show_when) return true;
274
+ const { field: condField, operator, value } = field2.conditional.show_when;
275
+ const fieldValue = values[condField];
276
+ switch (operator) {
277
+ case "equals":
278
+ return fieldValue === value;
279
+ case "not_equals":
280
+ return fieldValue !== value;
281
+ case "contains":
282
+ return String(fieldValue || "").includes(String(value));
283
+ case "not_contains":
284
+ return !String(fieldValue || "").includes(String(value));
285
+ case "is_empty":
286
+ return !fieldValue || fieldValue === "";
287
+ case "not_empty":
288
+ return !!fieldValue && fieldValue !== "";
289
+ case "greater_than":
290
+ return Number(fieldValue) > Number(value);
291
+ case "less_than":
292
+ return Number(fieldValue) < Number(value);
293
+ default:
294
+ return true;
295
+ }
296
+ }, [values]);
297
+ const visibleFields = useMemo(() => {
298
+ return fields.filter(isFieldVisible);
299
+ }, [fields, isFieldVisible]);
300
+ const validateField = useCallback((field2) => {
301
+ if (!isFieldVisible(field2)) return null;
302
+ const value = values[field2.slug];
303
+ const rules = field2.validation || {};
304
+ if (field2.is_required && (!value || value === "")) {
305
+ return rules.custom_error || `${field2.label} is required`;
306
+ }
307
+ if (!value) return null;
308
+ const strValue = String(value);
309
+ if (rules.min_length && strValue.length < rules.min_length) {
310
+ return `${field2.label} must be at least ${rules.min_length} characters`;
311
+ }
312
+ if (rules.max_length && strValue.length > rules.max_length) {
313
+ return `${field2.label} must be no more than ${rules.max_length} characters`;
314
+ }
315
+ if (rules.min !== void 0 && Number(value) < rules.min) {
316
+ return `${field2.label} must be at least ${rules.min}`;
317
+ }
318
+ if (rules.max !== void 0 && Number(value) > rules.max) {
319
+ return `${field2.label} must be no more than ${rules.max}`;
320
+ }
321
+ if (rules.pattern && !new RegExp(rules.pattern).test(strValue)) {
322
+ return rules.custom_error || `${field2.label} is invalid`;
323
+ }
324
+ if (field2.field_type === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(strValue)) {
325
+ return "Please enter a valid email address";
326
+ }
327
+ if (field2.field_type === "phone" && !/^[\d\s\-\+\(\)]+$/.test(strValue)) {
328
+ return "Please enter a valid phone number";
329
+ }
330
+ return null;
331
+ }, [values, isFieldVisible]);
332
+ const validate = useCallback(() => {
333
+ const newErrors = {};
334
+ for (const field2 of fields) {
335
+ const error = validateField(field2);
336
+ if (error) {
337
+ newErrors[field2.slug] = error;
338
+ }
339
+ }
340
+ setErrors(newErrors);
341
+ return Object.keys(newErrors).length === 0;
342
+ }, [fields, validateField]);
343
+ const setFieldValue = useCallback((key, value) => {
344
+ setValuesState((prev) => ({ ...prev, [key]: value }));
345
+ setErrors((prev) => {
346
+ if (prev[key]) {
347
+ const next = { ...prev };
348
+ delete next[key];
349
+ return next;
350
+ }
351
+ return prev;
352
+ });
353
+ }, []);
354
+ const setValues = useCallback((newValues) => {
355
+ setValuesState((prev) => ({ ...prev, ...newValues }));
356
+ }, []);
357
+ const clearErrors = useCallback(() => {
358
+ setErrors({});
359
+ }, []);
360
+ const canGoNext = step < totalSteps;
361
+ const canGoPrev = step > 1;
362
+ const isLastStep = step === totalSteps;
363
+ const progress = Math.round(step / totalSteps * 100);
364
+ const nextStep = useCallback(() => {
365
+ if (!validate()) return false;
366
+ if (step < totalSteps) {
367
+ const newStep = step + 1;
368
+ setStep(newStep);
369
+ trackStepChange(newStep);
370
+ return true;
371
+ }
372
+ return false;
373
+ }, [step, totalSteps, validate, trackStepChange]);
374
+ const prevStep = useCallback(() => {
375
+ if (step > 1) {
376
+ const newStep = step - 1;
377
+ setStep(newStep);
378
+ trackStepChange(newStep);
379
+ }
380
+ }, [step, trackStepChange]);
381
+ const goToStep = useCallback((targetStep) => {
382
+ if (targetStep >= 1 && targetStep <= totalSteps) {
383
+ setStep(targetStep);
384
+ trackStepChange(targetStep);
385
+ }
386
+ }, [totalSteps, trackStepChange]);
387
+ const submit = useCallback(async () => {
388
+ if (!form) return;
389
+ if (!validate()) return;
390
+ setIsSubmitting(true);
391
+ try {
392
+ const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
393
+ const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
394
+ if (!apiKey) {
395
+ throw new Error("API key is required. Set NEXT_PUBLIC_UPTRADE_API_KEY in your .env");
396
+ }
397
+ const submission = {
398
+ formId: form.id,
399
+ data: values,
400
+ metadata: {
401
+ pageUrl: typeof window !== "undefined" ? window.location.href : null,
402
+ referrer: typeof document !== "undefined" ? document.referrer || null : null,
403
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : null,
404
+ sessionId: typeof sessionStorage !== "undefined" ? sessionStorage.getItem("_uptrade_sid") : null,
405
+ ...getUTMParams()
406
+ }
407
+ };
408
+ const response = await fetch(`${apiUrl}/api/public/forms/submit`, {
409
+ method: "POST",
410
+ headers: {
411
+ "Content-Type": "application/json",
412
+ "Authorization": `Bearer ${apiKey}`
413
+ },
414
+ body: JSON.stringify(submission)
415
+ });
416
+ if (!response.ok) {
417
+ throw new Error(`Form submission failed: ${response.statusText}`);
418
+ }
419
+ const data = await response.json();
420
+ trackComplete();
421
+ setIsComplete(true);
422
+ onSuccess?.(data);
423
+ const finalRedirect = redirectUrl !== false ? redirectUrl || form.redirect_url : void 0;
424
+ if (finalRedirect) {
425
+ window.location.href = finalRedirect;
426
+ }
427
+ } catch (error) {
428
+ console.error("[useForm] Submission error:", error);
429
+ onError?.(error);
430
+ } finally {
431
+ setIsSubmitting(false);
432
+ }
433
+ }, [form, values, validate, trackComplete, onSuccess, onError, redirectUrl]);
434
+ const reset = useCallback(() => {
435
+ setValuesState(initialValues);
436
+ setErrors({});
437
+ setStep(1);
438
+ setIsComplete(false);
439
+ }, [initialValues]);
440
+ return {
441
+ form,
442
+ isLoading,
443
+ fetchError,
444
+ allFields,
445
+ fields,
446
+ visibleFields,
447
+ values,
448
+ errors,
449
+ setFieldValue,
450
+ setValues,
451
+ clearErrors,
452
+ step,
453
+ totalSteps,
454
+ isMultiStep,
455
+ progress,
456
+ nextStep,
457
+ prevStep,
458
+ goToStep,
459
+ canGoNext,
460
+ canGoPrev,
461
+ isLastStep,
462
+ validate,
463
+ validateField,
464
+ isFieldVisible,
465
+ submit,
466
+ isSubmitting,
467
+ isComplete,
468
+ reset
469
+ };
470
+ }
471
+ function FormField({ field: field2, value, error, onChange, classPrefix = "uptrade-form" }) {
472
+ const baseInputStyle = {
473
+ width: "100%",
474
+ padding: "var(--uptrade-input-padding, 10px 12px)",
475
+ fontSize: "var(--uptrade-font-size, 16px)",
476
+ border: error ? "1px solid var(--uptrade-input-border-error, #ef4444)" : "1px solid var(--uptrade-input-border, #d1d5db)",
477
+ borderRadius: "var(--uptrade-input-radius, 6px)",
478
+ backgroundColor: "var(--uptrade-input-bg, #ffffff)",
479
+ outline: "none",
480
+ fontFamily: "inherit"
481
+ };
482
+ const labelStyle = {
483
+ display: "block",
484
+ marginBottom: 6,
485
+ fontWeight: "var(--uptrade-label-weight, 500)",
486
+ color: "var(--uptrade-label-color, inherit)"
487
+ };
488
+ const errorStyle = {
489
+ color: "var(--uptrade-error-color, #ef4444)",
490
+ fontSize: 14,
491
+ marginTop: 4
492
+ };
493
+ const helpStyle = {
494
+ color: "var(--uptrade-help-color, #6b7280)",
495
+ fontSize: 14,
496
+ marginTop: 4
497
+ };
498
+ if (field2.field_type === "heading") {
499
+ return /* @__PURE__ */ jsx("h3", { className: `${classPrefix}__heading`, style: { margin: "16px 0 8px" }, children: field2.label });
500
+ }
501
+ if (field2.field_type === "paragraph") {
502
+ return /* @__PURE__ */ jsx("p", { className: `${classPrefix}__paragraph`, style: { color: "var(--uptrade-help-color, #6b7280)", margin: "8px 0" }, children: field2.help_text || field2.label });
503
+ }
504
+ if (field2.field_type === "hidden") {
505
+ return /* @__PURE__ */ jsx("input", { type: "hidden", name: field2.slug, value: String(value || "") });
506
+ }
507
+ return /* @__PURE__ */ jsxs("div", { className: `${classPrefix}__field ${classPrefix}__field--${field2.field_type}`, children: [
508
+ /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__label`, style: labelStyle, children: [
509
+ field2.label,
510
+ field2.is_required && /* @__PURE__ */ jsx("span", { style: { color: "var(--uptrade-error-color, #ef4444)" }, children: " *" })
511
+ ] }),
512
+ ["text", "email", "phone", "number"].includes(field2.field_type) && /* @__PURE__ */ jsx(
513
+ "input",
514
+ {
515
+ className: `${classPrefix}__input`,
516
+ type: field2.field_type === "phone" ? "tel" : field2.field_type,
517
+ name: field2.slug,
518
+ value: String(value || ""),
519
+ placeholder: field2.placeholder,
520
+ required: field2.is_required,
521
+ onChange: (e) => onChange(e.target.value),
522
+ style: baseInputStyle
523
+ }
524
+ ),
525
+ field2.field_type === "textarea" && /* @__PURE__ */ jsx(
526
+ "textarea",
527
+ {
528
+ className: `${classPrefix}__textarea`,
529
+ name: field2.slug,
530
+ value: String(value || ""),
531
+ placeholder: field2.placeholder,
532
+ required: field2.is_required,
533
+ rows: 4,
534
+ onChange: (e) => onChange(e.target.value),
535
+ style: { ...baseInputStyle, resize: "vertical" }
536
+ }
537
+ ),
538
+ field2.field_type === "select" && /* @__PURE__ */ jsxs(
539
+ "select",
540
+ {
541
+ className: `${classPrefix}__select`,
542
+ name: field2.slug,
543
+ value: String(value || ""),
544
+ required: field2.is_required,
545
+ onChange: (e) => onChange(e.target.value),
546
+ style: baseInputStyle,
547
+ children: [
548
+ /* @__PURE__ */ jsx("option", { value: "", children: field2.placeholder || "Select an option" }),
549
+ field2.options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
550
+ ]
551
+ }
552
+ ),
553
+ field2.field_type === "multi-select" && /* @__PURE__ */ jsx(
554
+ "select",
555
+ {
556
+ className: `${classPrefix}__select ${classPrefix}__select--multi`,
557
+ name: field2.slug,
558
+ value: Array.isArray(value) ? value : [],
559
+ required: field2.is_required,
560
+ multiple: true,
561
+ onChange: (e) => {
562
+ const selected = Array.from(e.target.selectedOptions, (opt) => opt.value);
563
+ onChange(selected);
564
+ },
565
+ style: { ...baseInputStyle, height: 120 },
566
+ children: field2.options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
567
+ }
568
+ ),
569
+ field2.field_type === "radio" && /* @__PURE__ */ jsx("div", { className: `${classPrefix}__radio-group`, style: { display: "flex", flexDirection: "column", gap: 8 }, children: field2.options?.map((option) => /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__radio-option`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
570
+ /* @__PURE__ */ jsx(
571
+ "input",
572
+ {
573
+ type: "radio",
574
+ name: field2.slug,
575
+ value: option.value,
576
+ checked: value === option.value,
577
+ onChange: () => onChange(option.value)
578
+ }
579
+ ),
580
+ option.label
581
+ ] }, option.value)) }),
582
+ field2.field_type === "checkbox" && (!field2.options || field2.options.length === 0) && /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__checkbox`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
583
+ /* @__PURE__ */ jsx(
584
+ "input",
585
+ {
586
+ type: "checkbox",
587
+ name: field2.slug,
588
+ checked: !!value,
589
+ onChange: (e) => onChange(e.target.checked)
590
+ }
591
+ ),
592
+ field2.help_text || field2.label
593
+ ] }),
594
+ field2.field_type === "checkbox" && field2.options && field2.options.length > 0 && /* @__PURE__ */ jsx("div", { className: `${classPrefix}__checkbox-group`, style: { display: "flex", flexDirection: "column", gap: 8 }, children: field2.options.map((option) => {
595
+ const selectedValues = Array.isArray(value) ? value : [];
596
+ const isChecked = selectedValues.includes(option.value);
597
+ return /* @__PURE__ */ jsxs("label", { className: `${classPrefix}__checkbox-option`, style: { display: "flex", alignItems: "center", gap: 8 }, children: [
598
+ /* @__PURE__ */ jsx(
599
+ "input",
600
+ {
601
+ type: "checkbox",
602
+ checked: isChecked,
603
+ onChange: () => {
604
+ if (isChecked) {
605
+ onChange(selectedValues.filter((v) => v !== option.value));
606
+ } else {
607
+ onChange([...selectedValues, option.value]);
608
+ }
609
+ }
610
+ }
611
+ ),
612
+ option.label
613
+ ] }, option.value);
614
+ }) }),
615
+ field2.field_type === "date" && /* @__PURE__ */ jsx(
616
+ "input",
617
+ {
618
+ className: `${classPrefix}__input ${classPrefix}__input--date`,
619
+ type: "date",
620
+ name: field2.slug,
621
+ value: String(value || ""),
622
+ required: field2.is_required,
623
+ onChange: (e) => onChange(e.target.value),
624
+ style: baseInputStyle
625
+ }
626
+ ),
627
+ field2.field_type === "time" && /* @__PURE__ */ jsx(
628
+ "input",
629
+ {
630
+ className: `${classPrefix}__input ${classPrefix}__input--time`,
631
+ type: "time",
632
+ name: field2.slug,
633
+ value: String(value || ""),
634
+ required: field2.is_required,
635
+ onChange: (e) => onChange(e.target.value),
636
+ style: baseInputStyle
637
+ }
638
+ ),
639
+ field2.field_type === "datetime" && /* @__PURE__ */ jsx(
640
+ "input",
641
+ {
642
+ className: `${classPrefix}__input ${classPrefix}__input--datetime`,
643
+ type: "datetime-local",
644
+ name: field2.slug,
645
+ value: String(value || ""),
646
+ required: field2.is_required,
647
+ onChange: (e) => onChange(e.target.value),
648
+ style: baseInputStyle
649
+ }
650
+ ),
651
+ field2.field_type === "file" && /* @__PURE__ */ jsx(
652
+ "input",
653
+ {
654
+ className: `${classPrefix}__input ${classPrefix}__input--file`,
655
+ type: "file",
656
+ name: field2.slug,
657
+ required: field2.is_required,
658
+ onChange: (e) => {
659
+ const file = e.target.files?.[0];
660
+ if (file) {
661
+ onChange(file.name);
662
+ }
663
+ },
664
+ style: baseInputStyle
665
+ }
666
+ ),
667
+ field2.field_type === "rating" && /* @__PURE__ */ jsx("div", { className: `${classPrefix}__rating`, style: { display: "flex", gap: 4 }, children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsx(
668
+ "button",
669
+ {
670
+ type: "button",
671
+ onClick: () => onChange(star),
672
+ className: `${classPrefix}__rating-star`,
673
+ style: {
674
+ background: "none",
675
+ border: "none",
676
+ fontSize: 24,
677
+ cursor: "pointer",
678
+ color: value >= star ? "var(--uptrade-rating-active, #fbbf24)" : "var(--uptrade-rating-inactive, #d1d5db)"
679
+ },
680
+ children: "\u2605"
681
+ },
682
+ star
683
+ )) }),
684
+ field2.field_type === "slider" && /* @__PURE__ */ jsxs("div", { className: `${classPrefix}__slider`, children: [
685
+ /* @__PURE__ */ jsx(
686
+ "input",
687
+ {
688
+ type: "range",
689
+ name: field2.slug,
690
+ value: Number(value || 50),
691
+ min: field2.validation?.min || 0,
692
+ max: field2.validation?.max || 100,
693
+ onChange: (e) => onChange(Number(e.target.value)),
694
+ style: { width: "100%" }
695
+ }
696
+ ),
697
+ /* @__PURE__ */ jsx("div", { className: `${classPrefix}__slider-value`, style: { textAlign: "center", marginTop: 4 }, children: value || 50 })
698
+ ] }),
699
+ error && /* @__PURE__ */ jsx("p", { className: `${classPrefix}__error`, style: errorStyle, children: error }),
700
+ field2.help_text && !error && field2.field_type !== "checkbox" && /* @__PURE__ */ jsx("p", { className: `${classPrefix}__help`, style: helpStyle, children: field2.help_text })
701
+ ] });
702
+ }
703
+ function FormClient({
704
+ config,
705
+ className,
706
+ onSuccess,
707
+ onError,
708
+ customRender
709
+ }) {
710
+ const [values, setValues] = useState({});
711
+ const [errors, setErrors] = useState({});
712
+ const [step, setStep] = useState(1);
713
+ const [isSubmitting, setIsSubmitting] = useState(false);
714
+ const [isComplete, setIsComplete] = useState(false);
715
+ const { trackStepChange, trackComplete } = useFormTracking({
716
+ formId: config.id,
717
+ totalSteps: config.total_steps
718
+ });
719
+ const currentFields = useMemo(() => {
720
+ if (!config.is_multi_step) {
721
+ return config.fields || [];
722
+ }
723
+ const currentStepConfig = config.steps?.find((s) => s.step_number === step);
724
+ if (!currentStepConfig) return config.fields || [];
725
+ return (config.fields || []).filter((f) => f.step_id === currentStepConfig.id);
726
+ }, [config, step]);
727
+ const isFieldVisible = useCallback((field2) => {
728
+ if (!field2.conditional?.show_when) return true;
729
+ const { field: condField, operator, value } = field2.conditional.show_when;
730
+ const fieldValue = values[condField];
731
+ switch (operator) {
732
+ case "equals":
733
+ return fieldValue === value;
734
+ case "not_equals":
735
+ return fieldValue !== value;
736
+ case "contains":
737
+ return String(fieldValue).includes(String(value));
738
+ case "not_contains":
739
+ return !String(fieldValue).includes(String(value));
740
+ case "is_empty":
741
+ return !fieldValue || fieldValue === "";
742
+ case "not_empty":
743
+ return !!fieldValue && fieldValue !== "";
744
+ case "greater_than":
745
+ return Number(fieldValue) > Number(value);
746
+ case "less_than":
747
+ return Number(fieldValue) < Number(value);
748
+ default:
749
+ return true;
750
+ }
751
+ }, [values]);
752
+ const validateField = useCallback((field2) => {
753
+ if (!isFieldVisible(field2)) return null;
754
+ const value = values[field2.slug];
755
+ const rules = field2.validation || {};
756
+ if (field2.is_required && (!value || value === "")) {
757
+ return rules.custom_error || `${field2.label} is required`;
758
+ }
759
+ if (!value) return null;
760
+ const strValue = String(value);
761
+ if (rules.min_length && strValue.length < rules.min_length) {
762
+ return `${field2.label} must be at least ${rules.min_length} characters`;
763
+ }
764
+ if (rules.max_length && strValue.length > rules.max_length) {
765
+ return `${field2.label} must be no more than ${rules.max_length} characters`;
766
+ }
767
+ if (rules.min !== void 0 && Number(value) < rules.min) {
768
+ return `${field2.label} must be at least ${rules.min}`;
769
+ }
770
+ if (rules.max !== void 0 && Number(value) > rules.max) {
771
+ return `${field2.label} must be no more than ${rules.max}`;
772
+ }
773
+ if (rules.pattern && !new RegExp(rules.pattern).test(strValue)) {
774
+ return rules.custom_error || `${field2.label} is invalid`;
775
+ }
776
+ if (field2.field_type === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(strValue)) {
777
+ return "Please enter a valid email address";
778
+ }
779
+ if (field2.field_type === "phone" && !/^[\d\s\-\+\(\)]+$/.test(strValue)) {
780
+ return "Please enter a valid phone number";
781
+ }
782
+ return null;
783
+ }, [values, isFieldVisible]);
784
+ const validateStep = useCallback(() => {
785
+ const newErrors = {};
786
+ for (const field2 of currentFields) {
787
+ const error = validateField(field2);
788
+ if (error) {
789
+ newErrors[field2.slug] = error;
790
+ }
791
+ }
792
+ setErrors(newErrors);
793
+ return Object.keys(newErrors).length === 0;
794
+ }, [currentFields, validateField]);
795
+ const setFieldValue = useCallback((key, value) => {
796
+ setValues((prev) => ({ ...prev, [key]: value }));
797
+ if (errors[key]) {
798
+ setErrors((prev) => {
799
+ const next = { ...prev };
800
+ delete next[key];
801
+ return next;
802
+ });
803
+ }
804
+ }, [errors]);
805
+ const nextStep = useCallback(() => {
806
+ if (!validateStep()) return false;
807
+ if (step < config.total_steps) {
808
+ const newStep = step + 1;
809
+ setStep(newStep);
810
+ trackStepChange(newStep);
811
+ return true;
812
+ }
813
+ return false;
814
+ }, [step, config.total_steps, validateStep, trackStepChange]);
815
+ const prevStep = useCallback(() => {
816
+ if (step > 1) {
817
+ const newStep = step - 1;
818
+ setStep(newStep);
819
+ trackStepChange(newStep);
820
+ }
821
+ }, [step, trackStepChange]);
822
+ const goToStep = useCallback((targetStep) => {
823
+ if (targetStep >= 1 && targetStep <= config.total_steps) {
824
+ setStep(targetStep);
825
+ trackStepChange(targetStep);
826
+ }
827
+ }, [config.total_steps, trackStepChange]);
828
+ const submit = useCallback(async () => {
829
+ if (!validateStep()) return;
830
+ setIsSubmitting(true);
831
+ try {
832
+ const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
833
+ const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ : void 0;
834
+ if (!apiKey) {
835
+ throw new Error("API key is required. Set NEXT_PUBLIC_UPTRADE_API_KEY in your .env");
836
+ }
837
+ const submission = {
838
+ form_id: config.id,
839
+ project_id: config.project_id,
840
+ data: values,
841
+ routing_type: config.form_type,
842
+ status: "new",
843
+ page_url: typeof window !== "undefined" ? window.location.href : null,
844
+ referrer: typeof document !== "undefined" ? document.referrer || null : null,
845
+ user_agent: typeof navigator !== "undefined" ? navigator.userAgent : null,
846
+ session_id: typeof sessionStorage !== "undefined" ? sessionStorage.getItem("_uptrade_sid") : null,
847
+ ...getUTMParams2()
848
+ };
849
+ const response = await fetch(`${apiUrl}/api/public/forms/submit`, {
850
+ method: "POST",
851
+ headers: {
852
+ "Content-Type": "application/json",
853
+ "Authorization": `Bearer ${apiKey}`
854
+ },
855
+ body: JSON.stringify(submission)
856
+ });
857
+ if (!response.ok) {
858
+ throw new Error(`Failed to submit form: ${response.statusText}`);
859
+ }
860
+ const data = await response.json();
861
+ trackComplete();
862
+ setIsComplete(true);
863
+ onSuccess?.(data);
864
+ if (config.redirect_url) {
865
+ window.location.href = config.redirect_url;
866
+ }
867
+ } catch (error) {
868
+ console.error("[Forms] Submission error:", error);
869
+ onError?.(error);
870
+ } finally {
871
+ setIsSubmitting(false);
872
+ }
873
+ }, [config, values, validateStep, trackComplete, onSuccess, onError]);
874
+ const progress = useMemo(() => {
875
+ return Math.round(step / config.total_steps * 100);
876
+ }, [step, config.total_steps]);
877
+ const renderProps = {
878
+ config,
879
+ fields: currentFields,
880
+ step,
881
+ totalSteps: config.total_steps,
882
+ values,
883
+ errors,
884
+ isSubmitting,
885
+ progress,
886
+ nextStep,
887
+ prevStep,
888
+ goToStep,
889
+ submit,
890
+ setFieldValue,
891
+ isFieldVisible
892
+ };
893
+ if (isComplete) {
894
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx("p", { children: config.success_message }) });
895
+ }
896
+ if (customRender) {
897
+ return /* @__PURE__ */ jsx(Fragment, { children: customRender(renderProps) });
898
+ }
899
+ return /* @__PURE__ */ jsxs(
900
+ "form",
901
+ {
902
+ className: `uptrade-form ${className || ""}`,
903
+ onSubmit: (e) => {
904
+ e.preventDefault();
905
+ if (step < config.total_steps) {
906
+ nextStep();
907
+ } else {
908
+ submit();
909
+ }
910
+ },
911
+ children: [
912
+ config.is_multi_step && /* @__PURE__ */ jsxs("div", { className: "uptrade-form__progress", style: { marginBottom: 20 }, children: [
913
+ /* @__PURE__ */ jsxs("div", { className: "uptrade-form__progress-header", style: { display: "flex", justifyContent: "space-between", marginBottom: 8 }, children: [
914
+ /* @__PURE__ */ jsxs("span", { className: "uptrade-form__progress-step", children: [
915
+ "Step ",
916
+ step,
917
+ " of ",
918
+ config.total_steps
919
+ ] }),
920
+ /* @__PURE__ */ jsxs("span", { className: "uptrade-form__progress-percent", children: [
921
+ progress,
922
+ "%"
923
+ ] })
924
+ ] }),
925
+ /* @__PURE__ */ jsx("div", { className: "uptrade-form__progress-track", style: {
926
+ height: 4,
927
+ backgroundColor: "var(--uptrade-progress-bg, #e5e7eb)",
928
+ borderRadius: 2,
929
+ overflow: "hidden"
930
+ }, children: /* @__PURE__ */ jsx("div", { className: "uptrade-form__progress-bar", style: {
931
+ width: `${progress}%`,
932
+ height: "100%",
933
+ backgroundColor: "var(--uptrade-progress-fill, #3b82f6)",
934
+ transition: "width 0.3s ease"
935
+ } }) })
936
+ ] }),
937
+ /* @__PURE__ */ jsx("div", { className: "uptrade-form__fields", style: { display: "flex", flexWrap: "wrap", gap: 16 }, children: currentFields.map((field2) => {
938
+ if (!isFieldVisible(field2)) return null;
939
+ const widthMap = {
940
+ full: "100%",
941
+ half: "calc(50% - 8px)",
942
+ third: "calc(33.33% - 11px)",
943
+ quarter: "calc(25% - 12px)"
944
+ };
945
+ const widthStyle = widthMap[field2.width] || "100%";
946
+ return /* @__PURE__ */ jsx("div", { className: `uptrade-form__field-wrapper uptrade-form__field-wrapper--${field2.width}`, style: { width: widthStyle }, children: /* @__PURE__ */ jsx(
947
+ FormField,
948
+ {
949
+ field: field2,
950
+ value: values[field2.slug],
951
+ error: errors[field2.slug],
952
+ onChange: (value) => setFieldValue(field2.slug, value)
953
+ }
954
+ ) }, field2.id);
955
+ }) }),
956
+ /* @__PURE__ */ jsxs("div", { className: "uptrade-form__actions", style: {
957
+ display: "flex",
958
+ justifyContent: step > 1 ? "space-between" : "flex-end",
959
+ marginTop: 24
960
+ }, children: [
961
+ step > 1 && /* @__PURE__ */ jsx("button", { type: "button", className: "uptrade-form__btn uptrade-form__btn--back", onClick: prevStep, children: "Back" }),
962
+ /* @__PURE__ */ jsx("button", { type: "submit", className: "uptrade-form__btn uptrade-form__btn--submit", disabled: isSubmitting, children: isSubmitting ? "Submitting..." : step < config.total_steps ? "Next" : config.submit_button_text })
963
+ ] }),
964
+ config.honeypot_enabled && /* @__PURE__ */ jsx(
965
+ "input",
966
+ {
967
+ type: "text",
968
+ name: "website",
969
+ tabIndex: -1,
970
+ autoComplete: "off",
971
+ style: {
972
+ position: "absolute",
973
+ left: -9999,
974
+ opacity: 0,
975
+ pointerEvents: "none"
976
+ },
977
+ onChange: (e) => {
978
+ if (e.target.value) {
979
+ console.warn("[Forms] Honeypot triggered");
980
+ }
981
+ }
982
+ }
983
+ )
984
+ ]
985
+ }
986
+ );
987
+ }
988
+ function getUTMParams2() {
989
+ if (typeof window === "undefined") return {};
990
+ const params = new URLSearchParams(window.location.search);
991
+ const utmParams = {};
992
+ for (const key of ["utm_source", "utm_medium", "utm_campaign"]) {
993
+ const value = params.get(key);
994
+ if (value) utmParams[key] = value;
995
+ }
996
+ return utmParams;
997
+ }
998
+ function ManagedForm({
999
+ formId,
1000
+ projectId,
1001
+ className,
1002
+ onSuccess,
1003
+ onError,
1004
+ children
1005
+ }) {
1006
+ const {
1007
+ form,
1008
+ isLoading,
1009
+ fetchError
1010
+ } = useForm(formId, {
1011
+ projectId,
1012
+ onSuccess,
1013
+ onError
1014
+ });
1015
+ if (isLoading) {
1016
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx("p", { style: { color: "#6b7280" }, children: "Loading form..." }) });
1017
+ }
1018
+ if (fetchError || !form) {
1019
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx("p", { style: { color: "#ef4444" }, children: fetchError?.message || "Form not found or inactive." }) });
1020
+ }
1021
+ return /* @__PURE__ */ jsx(
1022
+ FormClient,
1023
+ {
1024
+ config: form,
1025
+ className,
1026
+ onSuccess,
1027
+ onError,
1028
+ customRender: children
1029
+ }
1030
+ );
1031
+ }
1032
+
1033
+ export { FormClient, FormField as FormFieldComponent, ManagedForm, useForm, useFormTracking };
1034
+ //# sourceMappingURL=index.mjs.map
1035
+ //# sourceMappingURL=index.mjs.map