@marvalt/digivalt-core 0.2.30 → 0.2.32
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.
- package/CHANGELOG.md +28 -0
- package/GLOBAL-SHORTCODES.md +7 -7
- package/README.md +10 -8
- package/bin/env-example-fragments.cjs +6 -0
- package/bin/init.cjs +44 -5
- package/dist/generators/index.d.ts +3 -0
- package/dist/generators/prerender-seo.d.ts +6 -0
- package/dist/generators/seo-manifest.d.ts +3 -0
- package/dist/generators/seo-urls.d.ts +29 -0
- package/dist/generators/sitemap-paths.d.ts +5 -9
- package/dist/generators.cjs +1005 -155
- package/dist/generators.cjs.map +1 -1
- package/dist/generators.d.ts +86 -6
- package/dist/generators.esm.js +992 -156
- package/dist/generators.esm.js.map +1 -1
- package/dist/index.cjs +1019 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +103 -9
- package/dist/index.esm.js +1006 -157
- package/dist/index.esm.js.map +1 -1
- package/dist/registry/index.d.ts +20 -0
- package/dist/registry.cjs +1528 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.d.ts +26 -0
- package/dist/registry.esm.js +1523 -0
- package/dist/registry.esm.js.map +1 -0
- package/dist/seo/DigiValtRouteSeo.d.ts +12 -0
- package/dist/seo/DigiValtSeoHead.d.ts +2 -0
- package/dist/seo/buildPageJsonLd.d.ts +11 -0
- package/dist/seo/html.d.ts +8 -0
- package/dist/seo/index.d.ts +7 -0
- package/dist/seo/resolvePageSeo.d.ts +17 -0
- package/dist/seo/seoTypes.d.ts +57 -0
- package/dist/seo.cjs +307 -86
- package/dist/seo.cjs.map +1 -1
- package/dist/seo.d.ts +105 -2
- package/dist/seo.esm.js +300 -87
- package/dist/seo.esm.js.map +1 -1
- package/dist/static/wpStatic.d.ts +16 -0
- package/dist/static.cjs +10 -0
- package/dist/static.cjs.map +1 -1
- package/dist/static.d.ts +38 -2
- package/dist/static.esm.js +10 -1
- package/dist/static.esm.js.map +1 -1
- package/dist/utils/notifications.d.ts +3 -0
- package/docs/AI-LOCAL-WORDPRESS-BOOTSTRAP.md +7 -3
- package/docs/AI-MASTER-PROMPT.md +37 -0
- package/docs/ENVIRONMENT.md +3 -3
- package/docs/README.md +4 -3
- package/docs/SEO-PERFORMANCE.md +42 -11
- package/docs/SHORTCODES.md +4 -5
- package/package.json +17 -6
- package/template/DIGIVALT_SETUP.md +14 -8
- package/template/functions/robots.txt.ts +27 -0
- package/template/functions/sitemap.xml.ts +143 -65
- package/template/scripts/generate.ts +3 -10
- package/template/scripts/prerender-seo.ts +10 -0
- package/docs/LOVABLE-MASTER-PROMPT.md +0 -140
|
@@ -0,0 +1,1528 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var reactQuery = require('@tanstack/react-query');
|
|
6
|
+
var helmetAsync = require('react-helmet-async');
|
|
7
|
+
var wparser = require('@marvalt/wparser');
|
|
8
|
+
var madapter = require('@marvalt/madapter');
|
|
9
|
+
var plister = require('@marvalt/plister');
|
|
10
|
+
var reactRouterDom = require('react-router-dom');
|
|
11
|
+
var wadapter = require('@marvalt/wadapter');
|
|
12
|
+
require('clsx');
|
|
13
|
+
require('tailwind-merge');
|
|
14
|
+
|
|
15
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
16
|
+
/**
|
|
17
|
+
* Prefix a root-absolute path with Vite's `import.meta.env.BASE_URL`.
|
|
18
|
+
* Used so Pages Function URLs (`/api/...`) work when the app is built with a non-root `base`
|
|
19
|
+
* (e.g. path mounts via digivalt-uworker).
|
|
20
|
+
*
|
|
21
|
+
* - Absolute http(s) URLs and protocol-relative URLs are returned unchanged.
|
|
22
|
+
* - Empty / unset BASE_URL (`/` or `./`) leaves root-absolute paths as-is.
|
|
23
|
+
*/
|
|
24
|
+
function withBasePath(path) {
|
|
25
|
+
if (/^https?:\/\//i.test(path) || path.startsWith("//")) {
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
const baseRaw = typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('registry.cjs', document.baseURI).href)) }) !== "undefined" && undefined && typeof undefined.BASE_URL === "string"
|
|
29
|
+
? undefined.BASE_URL
|
|
30
|
+
: "/";
|
|
31
|
+
const base = baseRaw === "./" ? "/" : baseRaw;
|
|
32
|
+
if (!base || base === "/") {
|
|
33
|
+
return path;
|
|
34
|
+
}
|
|
35
|
+
const baseNorm = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
36
|
+
if (path.startsWith(baseNorm + "/") || path === baseNorm) {
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
if (path.startsWith("/")) {
|
|
40
|
+
return `${baseNorm}${path}`;
|
|
41
|
+
}
|
|
42
|
+
return `${baseNorm}/${path}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const getImportMetaEnv = () => {
|
|
46
|
+
try {
|
|
47
|
+
return typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('registry.cjs', document.baseURI).href)) }) !== 'undefined' ? undefined : undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const getEnv = (key) => {
|
|
54
|
+
const viteEnv = getImportMetaEnv();
|
|
55
|
+
if (viteEnv && key in viteEnv) {
|
|
56
|
+
const value = viteEnv[key];
|
|
57
|
+
return value == null ? undefined : String(value);
|
|
58
|
+
}
|
|
59
|
+
if (typeof process !== 'undefined' && process.env && key in process.env) {
|
|
60
|
+
const value = process.env[key];
|
|
61
|
+
return value == null ? undefined : String(value);
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
};
|
|
65
|
+
const isDevEnvironment = () => {
|
|
66
|
+
const viteEnv = getImportMetaEnv();
|
|
67
|
+
if (viteEnv && 'DEV' in viteEnv) {
|
|
68
|
+
return Boolean(viteEnv.DEV);
|
|
69
|
+
}
|
|
70
|
+
return typeof process !== 'undefined' && process.env?.NODE_ENV === 'development';
|
|
71
|
+
};
|
|
72
|
+
const isBrowserEnvironment = () => {
|
|
73
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// @ts-nocheck
|
|
77
|
+
// Gravity Forms data store with proper environment handling
|
|
78
|
+
const transformForm = (formData) => {
|
|
79
|
+
// Transform fields to ensure proper structure
|
|
80
|
+
const transformedFields = (formData.fields || []).map((field) => ({
|
|
81
|
+
id: field.id.toString(), // Ensure ID is string
|
|
82
|
+
type: field.type,
|
|
83
|
+
label: field.label,
|
|
84
|
+
description: field.description,
|
|
85
|
+
isRequired: field.isRequired ?? field.is_required ?? field.required ?? false,
|
|
86
|
+
placeholder: field.placeholder,
|
|
87
|
+
defaultValue: field.defaultValue ?? field.default_value,
|
|
88
|
+
cssClass: field.cssClass ?? field.css_class,
|
|
89
|
+
content: field.content,
|
|
90
|
+
inputs: field.inputs || null,
|
|
91
|
+
choices: field.choices || [],
|
|
92
|
+
validation: {
|
|
93
|
+
message: field.errorMessage ?? field.validationMessage ?? field.validation_message,
|
|
94
|
+
pattern: field.inputMaskValue
|
|
95
|
+
},
|
|
96
|
+
conditionalLogic: field.conditionalLogic ?? field.conditional_logic
|
|
97
|
+
}));
|
|
98
|
+
return {
|
|
99
|
+
id: formData.id.toString(), // Ensure ID is string
|
|
100
|
+
title: formData.title || formData.name || `Form ${formData.id}`,
|
|
101
|
+
description: formData.description,
|
|
102
|
+
fields: transformedFields,
|
|
103
|
+
button: {
|
|
104
|
+
text: formData.button?.text || 'Submit',
|
|
105
|
+
type: formData.button?.type || 'text',
|
|
106
|
+
imageUrl: formData.button?.imageUrl,
|
|
107
|
+
},
|
|
108
|
+
confirmation: {
|
|
109
|
+
type: formData.confirmation?.type || 'message',
|
|
110
|
+
message: formData.confirmation?.message,
|
|
111
|
+
url: formData.confirmation?.url,
|
|
112
|
+
pageId: formData.confirmation?.pageId,
|
|
113
|
+
queryString: formData.confirmation?.queryString,
|
|
114
|
+
isDefault: formData.confirmation?.isDefault,
|
|
115
|
+
},
|
|
116
|
+
settings: {
|
|
117
|
+
enableHoneypot: formData.enableHoneypot || false,
|
|
118
|
+
enableAnimation: formData.enableAnimation || false,
|
|
119
|
+
validationSummary: formData.validationSummary || false,
|
|
120
|
+
saveAndContinue: formData.save?.enabled || false,
|
|
121
|
+
limitEntries: {
|
|
122
|
+
enabled: formData.limitEntries || false,
|
|
123
|
+
limit: formData.limitEntriesCount,
|
|
124
|
+
message: formData.limitEntriesMessage
|
|
125
|
+
},
|
|
126
|
+
scheduleForm: {
|
|
127
|
+
enabled: formData.scheduleForm || false,
|
|
128
|
+
start: formData.scheduleStart,
|
|
129
|
+
end: formData.scheduleEnd,
|
|
130
|
+
message: formData.scheduleMessage
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
isActive: formData.isActive === true || formData.is_active === "1" || formData.is_active === true,
|
|
134
|
+
isPublished: formData.isPublished === true || formData.is_trash === "0" || formData.is_trash === false || formData.is_active === "1" || formData.is_active === true,
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
// Default fallback data
|
|
138
|
+
const defaultData = {
|
|
139
|
+
generated_at: new Date().toISOString(),
|
|
140
|
+
total_forms: 0,
|
|
141
|
+
forms: []
|
|
142
|
+
};
|
|
143
|
+
// Load static data from the generated JSON file
|
|
144
|
+
let gravityFormsData = defaultData;
|
|
145
|
+
// Function to load data dynamically
|
|
146
|
+
const loadGravityFormsData = async () => {
|
|
147
|
+
if (!isBrowserEnvironment()) {
|
|
148
|
+
return gravityFormsData;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
console.log('🔄 Loading Gravity Forms data from /gravity-forms-data.json...');
|
|
152
|
+
const response = await fetch('/gravity-forms-data.json');
|
|
153
|
+
if (response.ok) {
|
|
154
|
+
const jsonData = await response.json();
|
|
155
|
+
console.log('🔍 Gravity Forms Data Load Debug:', {
|
|
156
|
+
totalForms: jsonData.total_forms,
|
|
157
|
+
formsCount: jsonData.forms?.length || 0,
|
|
158
|
+
firstForm: jsonData.forms?.[0] ? {
|
|
159
|
+
id: jsonData.forms[0].id,
|
|
160
|
+
title: jsonData.forms[0].title,
|
|
161
|
+
is_active: jsonData.forms[0].is_active,
|
|
162
|
+
isActive: jsonData.forms[0].isActive,
|
|
163
|
+
isPublished: jsonData.forms[0].isPublished
|
|
164
|
+
} : null
|
|
165
|
+
});
|
|
166
|
+
// Transform the data to ensure it matches our interface
|
|
167
|
+
const transformedForms = jsonData.forms.map((form) => transformForm(form));
|
|
168
|
+
gravityFormsData = {
|
|
169
|
+
generated_at: jsonData.generated_at,
|
|
170
|
+
total_forms: jsonData.total_forms,
|
|
171
|
+
forms: transformedForms
|
|
172
|
+
};
|
|
173
|
+
console.log('🔍 Form Transformation Debug:', {
|
|
174
|
+
originalForms: jsonData.forms.map((f) => ({ id: f.id, title: f.title, is_active: f.is_active })),
|
|
175
|
+
transformedForms: transformedForms.map(f => ({ id: f.id, title: f.title, isActive: f.isActive, isPublished: f.isPublished }))
|
|
176
|
+
});
|
|
177
|
+
console.log('🔍 Transformed Forms Debug:', {
|
|
178
|
+
totalForms: gravityFormsData.total_forms,
|
|
179
|
+
formsCount: gravityFormsData.forms.length,
|
|
180
|
+
activeForms: gravityFormsData.forms.filter(f => f.isActive).length,
|
|
181
|
+
publishedForms: gravityFormsData.forms.filter(f => f.isPublished).length
|
|
182
|
+
});
|
|
183
|
+
return gravityFormsData;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.error('❌ Failed to load gravityForms.json:', response.status, response.statusText);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.error('❌ Error loading gravityForms.json:', error);
|
|
191
|
+
}
|
|
192
|
+
return defaultData;
|
|
193
|
+
};
|
|
194
|
+
const getFormById$1 = (id) => {
|
|
195
|
+
const idStr = String(id);
|
|
196
|
+
const idNum = typeof id === 'number' ? id : parseInt(idStr, 10);
|
|
197
|
+
return gravityFormsData.forms.find(form => String(form.id) === idStr || form.id === idNum);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/** Notify an app without depending on an app-local toast component. */
|
|
201
|
+
function notify(level, message) {
|
|
202
|
+
if (typeof window !== 'undefined') {
|
|
203
|
+
window.dispatchEvent(new CustomEvent('digivalt:notification', {
|
|
204
|
+
detail: { level, message },
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
const logger = level === 'error' ? console.error : console.info;
|
|
208
|
+
logger(`[DigiValt] ${message}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Decodes HTML entities in a string
|
|
213
|
+
* Handles both named entities (&, ") and numeric entities (', ’)
|
|
214
|
+
* @param text - The text string with HTML entities
|
|
215
|
+
* @returns Decoded text string
|
|
216
|
+
*/
|
|
217
|
+
function decodeHtmlEntities(text) {
|
|
218
|
+
if (!text)
|
|
219
|
+
return '';
|
|
220
|
+
// Use browser's built-in decoder if available (most efficient)
|
|
221
|
+
if (typeof document !== 'undefined') {
|
|
222
|
+
const textarea = document.createElement('textarea');
|
|
223
|
+
textarea.innerHTML = text;
|
|
224
|
+
return textarea.value;
|
|
225
|
+
}
|
|
226
|
+
// Fallback for server-side or when document is not available
|
|
227
|
+
// Decode common named entities
|
|
228
|
+
const namedEntities = {
|
|
229
|
+
'&': '&',
|
|
230
|
+
'<': '<',
|
|
231
|
+
'>': '>',
|
|
232
|
+
'"': '"',
|
|
233
|
+
''': "'",
|
|
234
|
+
' ': ' ',
|
|
235
|
+
'©': '©',
|
|
236
|
+
'®': '®',
|
|
237
|
+
'™': '™',
|
|
238
|
+
'…': '…',
|
|
239
|
+
'—': '—',
|
|
240
|
+
'–': '–',
|
|
241
|
+
'‘': '\u2018', // Left single quotation mark
|
|
242
|
+
'’': '\u2019', // Right single quotation mark
|
|
243
|
+
'“': '\u201C', // Left double quotation mark
|
|
244
|
+
'”': '\u201D', // Right double quotation mark
|
|
245
|
+
};
|
|
246
|
+
let decoded = text;
|
|
247
|
+
// Decode numeric entities (', ’, etc.)
|
|
248
|
+
decoded = decoded.replace(/&#(\d+);/g, (match, dec) => {
|
|
249
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
250
|
+
});
|
|
251
|
+
// Decode hex entities (', etc.)
|
|
252
|
+
decoded = decoded.replace(/&#x([0-9a-fA-F]+);/g, (match, hex) => {
|
|
253
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
254
|
+
});
|
|
255
|
+
// Decode named entities
|
|
256
|
+
Object.entries(namedEntities).forEach(([entity, char]) => {
|
|
257
|
+
decoded = decoded.replace(new RegExp(entity, 'g'), char);
|
|
258
|
+
});
|
|
259
|
+
return decoded;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Replaces common WordPress HTML entities so they display as expected (e.g. en dash as hyphen, &/& as &).
|
|
263
|
+
* Use on block innerHTML or other WP-rendered HTML before passing to dangerouslySetInnerHTML.
|
|
264
|
+
*/
|
|
265
|
+
function normalizeWordPressHtmlEntities(html) {
|
|
266
|
+
if (!html)
|
|
267
|
+
return '';
|
|
268
|
+
return decodeHtmlEntities(html.replace(/–/g, '-'));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validates if a URL is safe for redirects
|
|
272
|
+
* @param url - The URL to validate
|
|
273
|
+
* @param allowedDomains - Array of allowed domains (optional)
|
|
274
|
+
* @returns boolean indicating if the URL is safe
|
|
275
|
+
*/
|
|
276
|
+
function isValidRedirectUrl(url, allowedDomains = []) {
|
|
277
|
+
if (!url)
|
|
278
|
+
return false;
|
|
279
|
+
try {
|
|
280
|
+
const urlObj = new URL(url);
|
|
281
|
+
// Check if it's a relative URL (safe)
|
|
282
|
+
if (!urlObj.protocol)
|
|
283
|
+
return true;
|
|
284
|
+
// Check if it's HTTP/HTTPS
|
|
285
|
+
if (!['http:', 'https:'].includes(urlObj.protocol))
|
|
286
|
+
return false;
|
|
287
|
+
// If allowed domains are specified, check against them
|
|
288
|
+
if (allowedDomains.length > 0) {
|
|
289
|
+
return allowedDomains.some(domain => urlObj.hostname === domain || urlObj.hostname.endsWith(`.${domain}`));
|
|
290
|
+
}
|
|
291
|
+
// Default: allow any HTTPS URL or same-origin HTTP
|
|
292
|
+
return urlObj.protocol === 'https:' || urlObj.hostname === window.location.hostname;
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Safely redirects to a URL after validation
|
|
300
|
+
* @param url - The URL to redirect to
|
|
301
|
+
* @param allowedDomains - Array of allowed domains (optional)
|
|
302
|
+
* @param fallbackUrl - Fallback URL if validation fails (optional)
|
|
303
|
+
*/
|
|
304
|
+
function safeRedirect(url, allowedDomains = [], fallbackUrl) {
|
|
305
|
+
if (isValidRedirectUrl(url, allowedDomains)) {
|
|
306
|
+
window.location.href = url;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
console.warn('Invalid redirect URL:', url);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// @ts-nocheck
|
|
314
|
+
// Minimal browser-safe static data loader to avoid importing Node-only code in the runtime bundle.
|
|
315
|
+
/** Recursively normalize WordPress HTML entities (e.g. – → "-") in block innerHTML. */
|
|
316
|
+
function normalizeBlocksHtml(blocks) {
|
|
317
|
+
if (!blocks)
|
|
318
|
+
return;
|
|
319
|
+
for (const block of blocks) {
|
|
320
|
+
if (block.innerHTML && typeof block.innerHTML === 'string') {
|
|
321
|
+
block.innerHTML = normalizeWordPressHtmlEntities(block.innerHTML);
|
|
322
|
+
}
|
|
323
|
+
if (block.innerBlocks?.length)
|
|
324
|
+
normalizeBlocksHtml(block.innerBlocks);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/** Recursively decode HTML entities in all string values of an event_meta-like object (mutate in place). */
|
|
328
|
+
function normalizeEventMetaStrings(meta) {
|
|
329
|
+
for (const key of Object.keys(meta)) {
|
|
330
|
+
const v = meta[key];
|
|
331
|
+
if (typeof v === 'string') {
|
|
332
|
+
meta[key] = decodeHtmlEntities(v);
|
|
333
|
+
}
|
|
334
|
+
else if (Array.isArray(v)) {
|
|
335
|
+
for (let i = 0; i < v.length; i++) {
|
|
336
|
+
const el = v[i];
|
|
337
|
+
if (typeof el === 'string') {
|
|
338
|
+
v[i] = decodeHtmlEntities(el);
|
|
339
|
+
}
|
|
340
|
+
else if (el && typeof el === 'object' && !Array.isArray(el)) {
|
|
341
|
+
normalizeEventMetaStrings(el);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** Normalize title.rendered, excerpt.rendered, content.rendered, content_cloudflare on a WP-like item (mutate in place). */
|
|
348
|
+
function normalizeRenderedStrings(item) {
|
|
349
|
+
if (!item || typeof item !== 'object')
|
|
350
|
+
return;
|
|
351
|
+
if (typeof item.title?.rendered === 'string') {
|
|
352
|
+
item.title.rendered = normalizeWordPressHtmlEntities(item.title.rendered);
|
|
353
|
+
}
|
|
354
|
+
if (typeof item.excerpt?.rendered === 'string') {
|
|
355
|
+
item.excerpt.rendered = normalizeWordPressHtmlEntities(item.excerpt.rendered);
|
|
356
|
+
}
|
|
357
|
+
if (typeof item.content?.rendered === 'string') {
|
|
358
|
+
item.content.rendered = normalizeWordPressHtmlEntities(item.content.rendered);
|
|
359
|
+
}
|
|
360
|
+
if (typeof item.content_cloudflare === 'string') {
|
|
361
|
+
item.content_cloudflare = normalizeWordPressHtmlEntities(item.content_cloudflare);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Mask email for privacy: first char of local + ***@****** + last char of domain name + TLD.
|
|
366
|
+
* Example: name@example.com.au → n***@******e.com.au
|
|
367
|
+
*/
|
|
368
|
+
function maskEmail(email) {
|
|
369
|
+
const s = typeof email === 'string' ? email.trim() : '';
|
|
370
|
+
if (!s || !s.includes('@'))
|
|
371
|
+
return '***@***.***';
|
|
372
|
+
const parts = s.split('@', 2);
|
|
373
|
+
const local = parts[0];
|
|
374
|
+
const domain = parts[1];
|
|
375
|
+
if (!local || !domain)
|
|
376
|
+
return '***@***.***';
|
|
377
|
+
const localFirst = local.slice(0, 1);
|
|
378
|
+
const domainBits = domain.split('.');
|
|
379
|
+
if (domainBits.length < 2)
|
|
380
|
+
return localFirst + '***@******.' + domain;
|
|
381
|
+
const domainName = domainBits.shift() ?? '';
|
|
382
|
+
const tld = domainBits.join('.');
|
|
383
|
+
const domainLast = domainName.slice(-1);
|
|
384
|
+
return localFirst + '***@******' + domainLast + '.' + tld;
|
|
385
|
+
}
|
|
386
|
+
/** Replace raw members_email with masked value on each member in member arrays. */
|
|
387
|
+
function maskMemberEmailsInStaticData(data) {
|
|
388
|
+
if (!data)
|
|
389
|
+
return;
|
|
390
|
+
const memberKeys = ['chapter_member', 'members', 'member'];
|
|
391
|
+
const dataRecord = data;
|
|
392
|
+
for (const key of memberKeys) {
|
|
393
|
+
const arr = dataRecord[key];
|
|
394
|
+
if (!Array.isArray(arr))
|
|
395
|
+
continue;
|
|
396
|
+
for (const item of arr) {
|
|
397
|
+
if (item && typeof item === 'object') {
|
|
398
|
+
const obj = item;
|
|
399
|
+
if (obj.member_meta && typeof obj.member_meta === 'object') {
|
|
400
|
+
const meta = obj.member_meta;
|
|
401
|
+
if (meta.members_email != null)
|
|
402
|
+
meta.members_email = maskEmail(meta.members_email);
|
|
403
|
+
}
|
|
404
|
+
if (obj.public_profile && typeof obj.public_profile === 'object') {
|
|
405
|
+
const profile = obj.public_profile;
|
|
406
|
+
if (profile.members_email != null)
|
|
407
|
+
profile.members_email = maskEmail(profile.members_email);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/** Walk static data and normalize block HTML and .rendered/content fields so entities like – display correctly. */
|
|
414
|
+
function normalizeWordPressHtmlInStaticData(data) {
|
|
415
|
+
if (!data)
|
|
416
|
+
return;
|
|
417
|
+
if (data.pages) {
|
|
418
|
+
for (const page of data.pages) {
|
|
419
|
+
normalizeRenderedStrings(page);
|
|
420
|
+
if (page.blocks)
|
|
421
|
+
normalizeBlocksHtml(page.blocks);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (data.posts) {
|
|
425
|
+
for (const post of data.posts) {
|
|
426
|
+
normalizeRenderedStrings(post);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const cptKeys = ['eventbrite_event', 'member', 'testimonial', 'chapter_member', 'members'];
|
|
430
|
+
const dataRecord = data;
|
|
431
|
+
for (const key of cptKeys) {
|
|
432
|
+
const arr = dataRecord[key];
|
|
433
|
+
if (Array.isArray(arr)) {
|
|
434
|
+
for (const item of arr) {
|
|
435
|
+
normalizeRenderedStrings(item);
|
|
436
|
+
if (key === 'eventbrite_event' && item?.event_meta && typeof item.event_meta === 'object' && !Array.isArray(item.event_meta)) {
|
|
437
|
+
normalizeEventMetaStrings(item.event_meta);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (data.site_settings && typeof data.site_settings === 'object') {
|
|
443
|
+
const settings = data.site_settings;
|
|
444
|
+
if (typeof settings.name === 'string')
|
|
445
|
+
settings.name = normalizeWordPressHtmlEntities(settings.name);
|
|
446
|
+
if (typeof settings.description === 'string')
|
|
447
|
+
settings.description = normalizeWordPressHtmlEntities(settings.description);
|
|
448
|
+
}
|
|
449
|
+
if (data.header?.blocks)
|
|
450
|
+
normalizeBlocksHtml(data.header.blocks);
|
|
451
|
+
if (data.footer?.blocks)
|
|
452
|
+
normalizeBlocksHtml(data.footer.blocks);
|
|
453
|
+
}
|
|
454
|
+
let wpStaticCache = null;
|
|
455
|
+
const loadWordPressData = async (path = '/wordpress-data.json') => {
|
|
456
|
+
if (wpStaticCache)
|
|
457
|
+
return;
|
|
458
|
+
const res = await fetch(path);
|
|
459
|
+
if (!res.ok)
|
|
460
|
+
throw new Error(`Failed to load WordPress static data: ${res.status}`);
|
|
461
|
+
wpStaticCache = (await res.json());
|
|
462
|
+
normalizeWordPressHtmlInStaticData(wpStaticCache);
|
|
463
|
+
maskMemberEmailsInStaticData(wpStaticCache);
|
|
464
|
+
// Expose cache globally for theme loading (temporary workaround)
|
|
465
|
+
if (typeof window !== 'undefined') {
|
|
466
|
+
window.__wpStaticCache = wpStaticCache;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
const getWordPressPages = () => wpStaticCache?.pages ?? [];
|
|
470
|
+
const getWordPressPageBySlug = (slug) => {
|
|
471
|
+
const pages = getWordPressPages();
|
|
472
|
+
return pages.find(page => page.slug === slug);
|
|
473
|
+
};
|
|
474
|
+
const getWordPressPageById = (id) => {
|
|
475
|
+
const pages = getWordPressPages();
|
|
476
|
+
return pages.find(page => page.id === id);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const TurnstileWidget = ({ siteKey, action, cData, onSuccess, onError, onExpire, theme = 'auto', size = 'normal', tabIndex, onWidgetId, }) => {
|
|
480
|
+
const widgetRef = react.useRef(null);
|
|
481
|
+
const widgetId = react.useRef(null);
|
|
482
|
+
const scriptLoaded = react.useRef(false);
|
|
483
|
+
react.useEffect(() => {
|
|
484
|
+
if (!widgetRef.current || !siteKey)
|
|
485
|
+
return;
|
|
486
|
+
const loadTurnstile = () => {
|
|
487
|
+
if (window.turnstile && widgetRef.current && !widgetId.current) {
|
|
488
|
+
try {
|
|
489
|
+
widgetId.current = window.turnstile.render(widgetRef.current, {
|
|
490
|
+
sitekey: siteKey,
|
|
491
|
+
callback: onSuccess,
|
|
492
|
+
'error-callback': onError,
|
|
493
|
+
'expired-callback': onExpire,
|
|
494
|
+
theme,
|
|
495
|
+
size,
|
|
496
|
+
tabindex: tabIndex,
|
|
497
|
+
});
|
|
498
|
+
// Notify parent component of widget ID for reset capability
|
|
499
|
+
if (widgetId.current && onWidgetId) {
|
|
500
|
+
onWidgetId(widgetId.current);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
console.error('Failed to render Turnstile widget:', error);
|
|
505
|
+
onError?.();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
// Check if Turnstile is already loaded
|
|
510
|
+
if (window.turnstile) {
|
|
511
|
+
loadTurnstile();
|
|
512
|
+
}
|
|
513
|
+
else if (!scriptLoaded.current) {
|
|
514
|
+
// Load Turnstile script
|
|
515
|
+
scriptLoaded.current = true;
|
|
516
|
+
const script = document.createElement('script');
|
|
517
|
+
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
|
|
518
|
+
script.async = true;
|
|
519
|
+
script.defer = true;
|
|
520
|
+
script.onload = loadTurnstile;
|
|
521
|
+
script.onerror = () => {
|
|
522
|
+
console.error('Failed to load Turnstile script');
|
|
523
|
+
onError?.();
|
|
524
|
+
};
|
|
525
|
+
document.head.appendChild(script);
|
|
526
|
+
}
|
|
527
|
+
return () => {
|
|
528
|
+
if (widgetId.current && window.turnstile) {
|
|
529
|
+
try {
|
|
530
|
+
window.turnstile.remove(widgetId.current);
|
|
531
|
+
}
|
|
532
|
+
catch (error) {
|
|
533
|
+
console.error('Failed to remove Turnstile widget:', error);
|
|
534
|
+
}
|
|
535
|
+
widgetId.current = null;
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}, [siteKey, onSuccess, onError, onExpire, theme, size, tabIndex]);
|
|
539
|
+
return jsxRuntime.jsx("div", { ref: widgetRef, className: "cf-turnstile" });
|
|
540
|
+
};
|
|
541
|
+
const StaticGravityForm = ({ formId, config, className = '', onSubmit, onError, }) => {
|
|
542
|
+
const navigate = reactRouterDom.useNavigate();
|
|
543
|
+
const [form, setForm] = react.useState(null);
|
|
544
|
+
const [loading, setLoading] = react.useState(true);
|
|
545
|
+
const [error, setError] = react.useState(null); // For loading/data errors only
|
|
546
|
+
const [submissionError, setSubmissionError] = react.useState(null); // For submission errors only
|
|
547
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
548
|
+
const [result, setResult] = react.useState(null);
|
|
549
|
+
const [submitted, setSubmitted] = react.useState(false); // Track if form was successfully submitted
|
|
550
|
+
const [formData, setFormData] = react.useState({});
|
|
551
|
+
const [turnstileToken, setTurnstileToken] = react.useState(null);
|
|
552
|
+
const [turnstileError, setTurnstileError] = react.useState(null);
|
|
553
|
+
const turnstileWidgetIdRef = react.useRef(null);
|
|
554
|
+
// Load form from static data
|
|
555
|
+
react.useEffect(() => {
|
|
556
|
+
const loadForm = async () => {
|
|
557
|
+
try {
|
|
558
|
+
setLoading(true);
|
|
559
|
+
setError(null);
|
|
560
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
561
|
+
console.log('🔍 StaticGravityForm: Loading form', { formId, formIdType: typeof formId });
|
|
562
|
+
}
|
|
563
|
+
// Ensure static data is loaded
|
|
564
|
+
const dataStore = await loadGravityFormsData();
|
|
565
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
566
|
+
console.log('🔍 StaticGravityForm: Data loaded', {
|
|
567
|
+
totalForms: dataStore?.total_forms,
|
|
568
|
+
formsCount: dataStore?.forms?.length,
|
|
569
|
+
availableFormIds: dataStore?.forms?.map(f => f.id),
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
// Get form from static store - try both string and number formats
|
|
573
|
+
const formIdStr = String(formId);
|
|
574
|
+
const formIdNum = typeof formId === 'number' ? formId : parseInt(formIdStr, 10);
|
|
575
|
+
let staticForm = getFormById$1(formIdStr);
|
|
576
|
+
// If not found with string, try with number as string
|
|
577
|
+
if (!staticForm && !isNaN(formIdNum)) {
|
|
578
|
+
staticForm = getFormById$1(String(formIdNum));
|
|
579
|
+
}
|
|
580
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
581
|
+
console.log('🔍 StaticGravityForm: Form lookup', {
|
|
582
|
+
formId,
|
|
583
|
+
formIdStr,
|
|
584
|
+
formIdNum,
|
|
585
|
+
found: !!staticForm,
|
|
586
|
+
formTitle: staticForm?.title,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
if (!staticForm) {
|
|
590
|
+
const errorMsg = `Form with ID ${formId} not found in static data. Please ensure the form exists in /gravity-forms-data.json and run 'npm run generate:gf' to regenerate static data.`;
|
|
591
|
+
console.error('❌ StaticGravityForm:', errorMsg);
|
|
592
|
+
setError(new Error(errorMsg));
|
|
593
|
+
setLoading(false);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
setForm(staticForm);
|
|
597
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
598
|
+
console.log('✅ StaticGravityForm: Form loaded successfully', {
|
|
599
|
+
formId: staticForm.id,
|
|
600
|
+
title: staticForm.title,
|
|
601
|
+
fieldsCount: staticForm.fields?.length,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
catch (err) {
|
|
606
|
+
const error = err instanceof Error ? err : new Error('Failed to load form from static data');
|
|
607
|
+
console.error('❌ StaticGravityForm error:', error);
|
|
608
|
+
setError(error);
|
|
609
|
+
if (onError) {
|
|
610
|
+
onError(error);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
finally {
|
|
614
|
+
setLoading(false);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
loadForm();
|
|
618
|
+
}, [formId, onError]);
|
|
619
|
+
// Get Turnstile site key from environment - memoized to prevent re-renders
|
|
620
|
+
const turnstilesiteKey = react.useMemo(() => {
|
|
621
|
+
try {
|
|
622
|
+
// @ts-ignore - import.meta is a Vite-specific global
|
|
623
|
+
if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('registry.cjs', document.baseURI).href)) }) !== 'undefined' && undefined) {
|
|
624
|
+
// @ts-ignore
|
|
625
|
+
const key = undefined.VITE_TURNSTILE_SITE_KEY;
|
|
626
|
+
// Ensure we return a string or undefined, not an object
|
|
627
|
+
return typeof key === 'string' ? key : undefined;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
catch (e) {
|
|
631
|
+
// import.meta not available
|
|
632
|
+
}
|
|
633
|
+
return undefined;
|
|
634
|
+
}, []); // Empty deps - site key doesn't change during runtime
|
|
635
|
+
// Memoize Turnstile callbacks to prevent widget re-renders
|
|
636
|
+
const handleTurnstileSuccess = react.useCallback((token) => {
|
|
637
|
+
setTurnstileToken(token);
|
|
638
|
+
setTurnstileError(null);
|
|
639
|
+
}, []);
|
|
640
|
+
const handleTurnstileError = react.useCallback(() => {
|
|
641
|
+
setTurnstileToken(null);
|
|
642
|
+
setTurnstileError('Verification failed. Please try again.');
|
|
643
|
+
}, []);
|
|
644
|
+
const handleTurnstileExpire = react.useCallback(() => {
|
|
645
|
+
setTurnstileToken(null);
|
|
646
|
+
setTurnstileError('Verification expired. Please verify again.');
|
|
647
|
+
}, []);
|
|
648
|
+
// Handle form submission using API
|
|
649
|
+
const handleSubmit = async (e) => {
|
|
650
|
+
e.preventDefault();
|
|
651
|
+
// Check Turnstile token if enabled
|
|
652
|
+
if (turnstilesiteKey && !turnstileToken) {
|
|
653
|
+
setTurnstileError('Please complete the verification');
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (!form) {
|
|
657
|
+
const error = new Error('Form data not available');
|
|
658
|
+
setError(error);
|
|
659
|
+
if (onError) {
|
|
660
|
+
onError(error);
|
|
661
|
+
}
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
console.log('🔍 StaticGravityForm.handleSubmit:', {
|
|
665
|
+
formId,
|
|
666
|
+
formData,
|
|
667
|
+
formDataKeys: Object.keys(formData),
|
|
668
|
+
turnstileToken,
|
|
669
|
+
});
|
|
670
|
+
try {
|
|
671
|
+
setSubmitting(true);
|
|
672
|
+
setSubmissionError(null);
|
|
673
|
+
setResult(null);
|
|
674
|
+
// Create GravityFormsClient for submission only
|
|
675
|
+
const client = new wadapter.GravityFormsClient({
|
|
676
|
+
...config,
|
|
677
|
+
proxyEndpoint: config.proxyEndpoint || withBasePath('/api/gravity-forms-submit'),
|
|
678
|
+
});
|
|
679
|
+
const submission = {
|
|
680
|
+
form_id: parseInt(String(formId), 10),
|
|
681
|
+
field_values: formData,
|
|
682
|
+
...(turnstileToken ? {
|
|
683
|
+
headers: {
|
|
684
|
+
'cf-turnstile-response': turnstileToken
|
|
685
|
+
}
|
|
686
|
+
} : {})
|
|
687
|
+
};
|
|
688
|
+
console.log('🔍 StaticGravityForm submission object:', submission);
|
|
689
|
+
// Submit form via API
|
|
690
|
+
const submissionResult = await client.submitForm(parseInt(String(formId), 10), submission);
|
|
691
|
+
setResult(submissionResult);
|
|
692
|
+
// Clear form data after successful submission
|
|
693
|
+
setFormData({});
|
|
694
|
+
// Reset Turnstile widget if present
|
|
695
|
+
if (turnstileWidgetIdRef.current && window.turnstile) {
|
|
696
|
+
try {
|
|
697
|
+
window.turnstile.reset(turnstileWidgetIdRef.current);
|
|
698
|
+
}
|
|
699
|
+
catch (e) {
|
|
700
|
+
console.warn('Failed to reset Turnstile widget:', e);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
setTurnstileToken(null);
|
|
704
|
+
setTurnstileError(null);
|
|
705
|
+
// Mark form as submitted
|
|
706
|
+
setSubmitted(true);
|
|
707
|
+
// Show success toast
|
|
708
|
+
notify('success', submissionResult.message || 'Form submitted successfully!');
|
|
709
|
+
// Handle confirmation types
|
|
710
|
+
const confirmation = submissionResult.confirmation;
|
|
711
|
+
if (confirmation) {
|
|
712
|
+
if (confirmation.type === 'redirect' && confirmation.url) {
|
|
713
|
+
// Redirect to URL
|
|
714
|
+
setTimeout(() => {
|
|
715
|
+
safeRedirect(confirmation.url);
|
|
716
|
+
}, 1500); // Small delay to show success message
|
|
717
|
+
}
|
|
718
|
+
else if (confirmation.type === 'page' && (confirmation.pageId || confirmation.page_id)) {
|
|
719
|
+
// Resolve page slug from ID and navigate
|
|
720
|
+
// Handle both camelCase (pageId) and snake_case (page_id) from API
|
|
721
|
+
const pageId = confirmation.pageId || confirmation.page_id;
|
|
722
|
+
// Load WordPress data if not already loaded, then resolve page
|
|
723
|
+
loadWordPressData().then(() => {
|
|
724
|
+
const page = getWordPressPageById(Number(pageId));
|
|
725
|
+
if (page?.slug) {
|
|
726
|
+
setTimeout(() => {
|
|
727
|
+
navigate(`/${page.slug}`);
|
|
728
|
+
}, 1500); // Small delay to show success message
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
console.warn(`Page with ID ${pageId} not found in static data`);
|
|
732
|
+
}
|
|
733
|
+
}).catch((err) => {
|
|
734
|
+
console.error('Failed to load WordPress data for page redirect:', err);
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
// For 'message' type, we'll show the confirmation message in the UI
|
|
738
|
+
}
|
|
739
|
+
if (onSubmit) {
|
|
740
|
+
onSubmit(submissionResult);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
catch (err) {
|
|
744
|
+
const error = err instanceof Error ? err : new Error('Unknown error');
|
|
745
|
+
console.error('❌ StaticGravityForm submission error:', error);
|
|
746
|
+
console.error('🔍 Error details:', {
|
|
747
|
+
message: error.message,
|
|
748
|
+
data: error.data,
|
|
749
|
+
validation_errors: error.validation_errors,
|
|
750
|
+
code: error.code,
|
|
751
|
+
});
|
|
752
|
+
// Determine error type and show appropriate message
|
|
753
|
+
let errorMessage = 'Form submission failed. Please try again.';
|
|
754
|
+
// Check if this is a CORS or network error
|
|
755
|
+
// Only show "Wrangler not running" if it's a clear network failure (ERR_FAILED, network error)
|
|
756
|
+
// Not for generic "Failed to fetch" which can happen for many reasons
|
|
757
|
+
const isNetworkError = error.message.includes('ERR_FAILED') ||
|
|
758
|
+
error.message.includes('NetworkError') ||
|
|
759
|
+
(error.message.includes('Failed to fetch') && !error.message.includes('403') && !error.message.includes('404') && !error.message.includes('500'));
|
|
760
|
+
const isCorsError = error.message.includes('CORS') || error.message.includes('Access-Control');
|
|
761
|
+
if (isNetworkError && config.proxyEndpoint?.includes('127.0.0.1:8788')) {
|
|
762
|
+
// Only show Wrangler error for clear network failures when using Wrangler proxy
|
|
763
|
+
errorMessage = 'Unable to connect to Wrangler development server. Please ensure it is running with `npm run pages:dev` on port 8788, or disable VITE_LOCAL_DEVELOPMENT to use the production proxy.';
|
|
764
|
+
}
|
|
765
|
+
else if (isCorsError) {
|
|
766
|
+
errorMessage = 'CORS error: The server is not allowing requests from this origin. Please check server configuration.';
|
|
767
|
+
}
|
|
768
|
+
else if (isNetworkError) {
|
|
769
|
+
errorMessage = 'Unable to connect to form server. Please check your connection or try again later.';
|
|
770
|
+
}
|
|
771
|
+
else if (error.message.includes('404') || error.message.includes('Not Found')) {
|
|
772
|
+
errorMessage = 'Form submission endpoint not found. Please contact support.';
|
|
773
|
+
}
|
|
774
|
+
else if (error.message.includes('403') || error.message.includes('Forbidden')) {
|
|
775
|
+
errorMessage = 'Form submission was denied. Please check your permissions.';
|
|
776
|
+
}
|
|
777
|
+
else if (error.message.includes('400') || error.message.includes('Bad Request')) {
|
|
778
|
+
// Try to extract validation errors from the error object
|
|
779
|
+
// WordPress REST API format: { code, message, data: { status, validation_errors } }
|
|
780
|
+
const errorData = error.data;
|
|
781
|
+
const validationErrors = error.validation_errors || errorData?.data?.validation_errors || errorData?.validation_errors;
|
|
782
|
+
if (validationErrors && typeof validationErrors === 'object') {
|
|
783
|
+
// Format validation errors for display
|
|
784
|
+
const errorList = Object.entries(validationErrors)
|
|
785
|
+
.map(([field, message]) => {
|
|
786
|
+
// Try to get field label from form if available
|
|
787
|
+
// For name fields, check if it's a sub-input (e.g., "1.3") and find parent field
|
|
788
|
+
let fieldObj = form?.fields?.find((f) => String(f.id) === String(field));
|
|
789
|
+
// If not found, it might be a sub-input of a name field
|
|
790
|
+
if (!fieldObj && field.includes('.')) {
|
|
791
|
+
const parentFieldId = field.split('.')[0];
|
|
792
|
+
fieldObj = form?.fields?.find((f) => String(f.id) === String(parentFieldId));
|
|
793
|
+
// If it's a name field, use the parent field's label
|
|
794
|
+
if (fieldObj?.type === 'name') {
|
|
795
|
+
return `${decodeHtmlEntities(fieldObj.label)}: ${message}`;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
const fieldLabel = fieldObj?.label ? decodeHtmlEntities(fieldObj.label) : `Field ${field}`;
|
|
799
|
+
return `${fieldLabel}: ${message}`;
|
|
800
|
+
})
|
|
801
|
+
.join('; ');
|
|
802
|
+
errorMessage = `Validation failed: ${errorList}`;
|
|
803
|
+
}
|
|
804
|
+
else if (error.message.includes('Validation failed')) {
|
|
805
|
+
errorMessage = 'Form validation failed. Please check all required fields are filled correctly.';
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
// Use the error message from WordPress
|
|
809
|
+
errorMessage = error.message || 'Form submission failed. Please try again.';
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
else if (error.message.includes('Network') || error.message.includes('network')) {
|
|
813
|
+
errorMessage = 'Network error. Please check your internet connection and try again.';
|
|
814
|
+
}
|
|
815
|
+
else if (error.message) {
|
|
816
|
+
// Use the error message if it's user-friendly
|
|
817
|
+
errorMessage = error.message;
|
|
818
|
+
}
|
|
819
|
+
setSubmissionError(error);
|
|
820
|
+
// Show error toast
|
|
821
|
+
notify('error', errorMessage);
|
|
822
|
+
if (onError) {
|
|
823
|
+
onError(error);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
finally {
|
|
827
|
+
setSubmitting(false);
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
const handleFieldChange = (fieldId, value) => {
|
|
831
|
+
setFormData(prev => ({
|
|
832
|
+
...prev,
|
|
833
|
+
[fieldId]: value,
|
|
834
|
+
}));
|
|
835
|
+
};
|
|
836
|
+
// Loading state
|
|
837
|
+
if (loading) {
|
|
838
|
+
return jsxRuntime.jsx("div", { className: `gravity-form loading ${className}`, children: "Loading form..." });
|
|
839
|
+
}
|
|
840
|
+
// Loading/Data error state (not submission errors)
|
|
841
|
+
if (error) {
|
|
842
|
+
return (jsxRuntime.jsxs("div", { className: `gravity-form error ${className}`, children: [jsxRuntime.jsxs("p", { children: ["Error: ", error.message] }), process.env.NODE_ENV !== 'production' && (jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-2", children: "Make sure the form exists in /gravity-forms-data.json and run `npm run generate:gf` to regenerate static data." }))] }));
|
|
843
|
+
}
|
|
844
|
+
// Form not found state
|
|
845
|
+
if (!form) {
|
|
846
|
+
return (jsxRuntime.jsxs("div", { className: `gravity-form not-found ${className}`, children: [jsxRuntime.jsx("p", { children: "Form not found" }), process.env.NODE_ENV !== 'production' && (jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: ["Form ID: ", formId, ". Check /gravity-forms-data.json or run `npm run generate:gf`."] }))] }));
|
|
847
|
+
}
|
|
848
|
+
// Show confirmation message if form was submitted and confirmation type is 'message'
|
|
849
|
+
const confirmation = result?.confirmation;
|
|
850
|
+
const showConfirmation = submitted && confirmation?.type === 'message';
|
|
851
|
+
return (jsxRuntime.jsx("div", { className: `gravity-form max-w-2xl mx-auto ${className}`, children: showConfirmation ? (
|
|
852
|
+
// Show confirmation message instead of form
|
|
853
|
+
jsxRuntime.jsxs("div", { className: "space-y-6 bg-white/95 backdrop-blur-sm p-6 md:p-8 rounded-lg shadow-lg", children: [form.title && (jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold mb-4 text-gray-900", children: form.title })), jsxRuntime.jsx("div", { className: "rounded-md bg-green-50 px-4 py-3 text-sm text-green-800 border border-green-200", children: confirmation?.message || result?.message || 'Thank you for your submission!' })] })) : (
|
|
854
|
+
// Show form
|
|
855
|
+
jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-6 bg-white/95 backdrop-blur-sm p-6 md:p-8 rounded-lg shadow-lg", children: [form.title && (jsxRuntime.jsx("h2", { className: "text-2xl md:text-3xl font-bold mb-4 text-gray-900", children: form.title })), form.description && (jsxRuntime.jsx("p", { className: "text-gray-600 mb-6", children: decodeHtmlEntities(form.description) })), form.fields.map(field => (jsxRuntime.jsxs("div", { className: `field field-${field.type} space-y-2`, children: [field.type !== 'html' && (jsxRuntime.jsxs("label", { htmlFor: `field_${field.id}`, className: "block text-sm font-medium text-gray-700", children: [decodeHtmlEntities(field.label), field.isRequired && jsxRuntime.jsx("span", { className: "text-red-500 ml-1", children: "*" })] })), field.type !== 'html' && field.description && (jsxRuntime.jsx("p", { className: "field-description text-sm text-gray-500", children: decodeHtmlEntities(field.description) })), field.type === 'html' && (jsxRuntime.jsx("div", { className: "gf-html-field prose max-w-none text-gray-700", dangerouslySetInnerHTML: { __html: decodeHtmlEntities(field.content || field.description || '') } })), field.type === 'text' && (jsxRuntime.jsx("input", { type: "text", id: `field_${field.id}`, name: field.label, required: field.isRequired, placeholder: field.placeholder ? decodeHtmlEntities(field.placeholder) : undefined, value: formData[field.id] || field.defaultValue || '', onChange: (e) => handleFieldChange(field.id, e.target.value), className: "w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white" })), field.type === 'email' && (jsxRuntime.jsx("input", { type: "email", id: `field_${field.id}`, name: field.label, required: field.isRequired, placeholder: field.placeholder ? decodeHtmlEntities(field.placeholder) : undefined, value: formData[field.id] || field.defaultValue || '', onChange: (e) => handleFieldChange(field.id, e.target.value), className: "w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white" })), field.type === 'textarea' && (jsxRuntime.jsx("textarea", { id: `field_${field.id}`, name: field.label, required: field.isRequired, placeholder: field.placeholder ? decodeHtmlEntities(field.placeholder) : undefined, value: formData[field.id] || field.defaultValue || '', onChange: (e) => handleFieldChange(field.id, e.target.value), rows: 4, className: "w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white resize-y" })), field.type === 'select' && field.choices && (jsxRuntime.jsxs("select", { id: `field_${field.id}`, name: field.label, required: field.isRequired, value: formData[field.id] || field.defaultValue || (field.choices.find(c => c.isSelected)?.value || ''), onChange: (e) => handleFieldChange(field.id, e.target.value), className: "w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white", children: [jsxRuntime.jsx("option", { value: "", children: "Select an option" }), field.choices.map((choice, index) => (jsxRuntime.jsx("option", { value: choice.value, children: decodeHtmlEntities(choice.text) }, index)))] })), field.type === 'name' && field.inputs && (jsxRuntime.jsx("div", { className: "name-field-inputs grid grid-cols-1 md:grid-cols-2 gap-4", children: field.inputs
|
|
856
|
+
.filter((input) => !input.isHidden)
|
|
857
|
+
.map((input) => (jsxRuntime.jsxs("div", { className: "name-input space-y-2", children: [jsxRuntime.jsxs("label", { htmlFor: `field_${input.id}`, className: "block text-sm font-medium text-gray-700", children: [input.label, field.isRequired && input.label.toLowerCase().includes('first') && (jsxRuntime.jsx("span", { className: "text-red-500 ml-1", children: "*" }))] }), input.choices ? (jsxRuntime.jsxs("select", { id: `field_${input.id}`, name: input.name || input.label, onChange: (e) => handleFieldChange(input.id, e.target.value), className: "w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white", children: [jsxRuntime.jsxs("option", { value: "", children: ["Select ", decodeHtmlEntities(input.label)] }), input.choices.map((choice, index) => (jsxRuntime.jsx("option", { value: choice.value, children: decodeHtmlEntities(choice.text) }, index)))] })) : (jsxRuntime.jsx("input", { type: "text", id: `field_${input.id}`, name: input.name || input.label, placeholder: input.placeholder ? decodeHtmlEntities(input.placeholder) : undefined, required: field.isRequired && input.label.toLowerCase().includes('first'), autoComplete: input.autocompleteAttribute, value: formData[input.id] || '', onChange: (e) => handleFieldChange(input.id, e.target.value), className: "w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white" }))] }, input.id))) }))] }, field.id))), turnstilesiteKey && (jsxRuntime.jsxs("div", { className: "form-turnstile flex justify-center py-4", children: [jsxRuntime.jsx(TurnstileWidget, { siteKey: turnstilesiteKey, onSuccess: handleTurnstileSuccess, onError: handleTurnstileError, onExpire: handleTurnstileExpire, onWidgetId: (id) => { turnstileWidgetIdRef.current = id; }, theme: "auto", size: "normal" }), turnstileError && (jsxRuntime.jsx("p", { className: "field-error turnstile-error text-red-600 text-sm mt-2", children: turnstileError }))] })), jsxRuntime.jsx("button", { type: "submit", disabled: submitting || (!!turnstilesiteKey && !turnstileToken), className: "w-full md:w-auto px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed text-white font-semibold rounded-md shadow-md transition-colors duration-200", children: submitting
|
|
858
|
+
? 'Submitting...'
|
|
859
|
+
: (turnstilesiteKey && !turnstileToken)
|
|
860
|
+
? 'Complete verification first'
|
|
861
|
+
: form.button?.text || 'Submit' }), result && !result.success && (jsxRuntime.jsx("div", { className: `result p-4 rounded-md bg-red-50 text-red-800 border border-red-200`, children: result.message }))] })) }));
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
var forms = [
|
|
865
|
+
{
|
|
866
|
+
id: 3,
|
|
867
|
+
name: "Eventbright Form",
|
|
868
|
+
alias: "eventbrigh",
|
|
869
|
+
description: null,
|
|
870
|
+
isPublished: true,
|
|
871
|
+
fields: [
|
|
872
|
+
{
|
|
873
|
+
id: 10,
|
|
874
|
+
label: "First Name",
|
|
875
|
+
showLabel: true,
|
|
876
|
+
alias: "first_name",
|
|
877
|
+
type: "text",
|
|
878
|
+
defaultValue: null,
|
|
879
|
+
isRequired: true,
|
|
880
|
+
validationMessage: null,
|
|
881
|
+
helpMessage: null,
|
|
882
|
+
order: 1,
|
|
883
|
+
properties: {
|
|
884
|
+
placeholder: null
|
|
885
|
+
},
|
|
886
|
+
validation: [
|
|
887
|
+
],
|
|
888
|
+
parent: null,
|
|
889
|
+
conditions: [
|
|
890
|
+
],
|
|
891
|
+
labelAttributes: null,
|
|
892
|
+
inputAttributes: null,
|
|
893
|
+
containerAttributes: null,
|
|
894
|
+
leadField: "firstname",
|
|
895
|
+
saveResult: true,
|
|
896
|
+
isAutoFill: true,
|
|
897
|
+
isReadOnly: false,
|
|
898
|
+
mappedObject: "contact",
|
|
899
|
+
mappedField: "firstname"
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
id: 11,
|
|
903
|
+
label: "Last Name",
|
|
904
|
+
showLabel: true,
|
|
905
|
+
alias: "last_name",
|
|
906
|
+
type: "text",
|
|
907
|
+
defaultValue: null,
|
|
908
|
+
isRequired: true,
|
|
909
|
+
validationMessage: null,
|
|
910
|
+
helpMessage: null,
|
|
911
|
+
order: 2,
|
|
912
|
+
properties: {
|
|
913
|
+
placeholder: null
|
|
914
|
+
},
|
|
915
|
+
validation: [
|
|
916
|
+
],
|
|
917
|
+
parent: null,
|
|
918
|
+
conditions: [
|
|
919
|
+
],
|
|
920
|
+
labelAttributes: null,
|
|
921
|
+
inputAttributes: null,
|
|
922
|
+
containerAttributes: null,
|
|
923
|
+
leadField: "lastname",
|
|
924
|
+
saveResult: true,
|
|
925
|
+
isAutoFill: true,
|
|
926
|
+
isReadOnly: false,
|
|
927
|
+
mappedObject: "contact",
|
|
928
|
+
mappedField: "lastname"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
id: 8,
|
|
932
|
+
label: "Email",
|
|
933
|
+
showLabel: true,
|
|
934
|
+
alias: "email",
|
|
935
|
+
type: "email",
|
|
936
|
+
defaultValue: null,
|
|
937
|
+
isRequired: true,
|
|
938
|
+
validationMessage: null,
|
|
939
|
+
helpMessage: null,
|
|
940
|
+
order: 3,
|
|
941
|
+
properties: {
|
|
942
|
+
placeholder: null
|
|
943
|
+
},
|
|
944
|
+
validation: [
|
|
945
|
+
],
|
|
946
|
+
parent: null,
|
|
947
|
+
conditions: [
|
|
948
|
+
],
|
|
949
|
+
labelAttributes: null,
|
|
950
|
+
inputAttributes: null,
|
|
951
|
+
containerAttributes: null,
|
|
952
|
+
leadField: "email",
|
|
953
|
+
saveResult: true,
|
|
954
|
+
isAutoFill: true,
|
|
955
|
+
isReadOnly: false,
|
|
956
|
+
mappedObject: "contact",
|
|
957
|
+
mappedField: "email"
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
id: 9,
|
|
961
|
+
label: "Submit",
|
|
962
|
+
showLabel: true,
|
|
963
|
+
alias: "submit",
|
|
964
|
+
type: "button",
|
|
965
|
+
defaultValue: null,
|
|
966
|
+
isRequired: false,
|
|
967
|
+
validationMessage: null,
|
|
968
|
+
helpMessage: null,
|
|
969
|
+
order: 4,
|
|
970
|
+
properties: [
|
|
971
|
+
],
|
|
972
|
+
validation: [
|
|
973
|
+
],
|
|
974
|
+
parent: null,
|
|
975
|
+
conditions: [
|
|
976
|
+
],
|
|
977
|
+
labelAttributes: null,
|
|
978
|
+
inputAttributes: "class=\"btn btn-ghost\"",
|
|
979
|
+
containerAttributes: null,
|
|
980
|
+
leadField: null,
|
|
981
|
+
saveResult: true,
|
|
982
|
+
isAutoFill: false,
|
|
983
|
+
isReadOnly: false,
|
|
984
|
+
mappedObject: null,
|
|
985
|
+
mappedField: null
|
|
986
|
+
}
|
|
987
|
+
],
|
|
988
|
+
actions: [
|
|
989
|
+
{
|
|
990
|
+
id: 1,
|
|
991
|
+
name: "Email",
|
|
992
|
+
description: null,
|
|
993
|
+
type: "lead.addutmtags",
|
|
994
|
+
order: 1,
|
|
995
|
+
properties: [
|
|
996
|
+
]
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
id: 2,
|
|
1000
|
+
name: "THank You Notif 1",
|
|
1001
|
+
description: null,
|
|
1002
|
+
type: "email.send.user",
|
|
1003
|
+
order: 2,
|
|
1004
|
+
properties: {
|
|
1005
|
+
useremail: {
|
|
1006
|
+
email: "2"
|
|
1007
|
+
},
|
|
1008
|
+
user_id: [
|
|
1009
|
+
1
|
|
1010
|
+
]
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
id: 3,
|
|
1015
|
+
name: "Registered +10 (HOT)",
|
|
1016
|
+
description: null,
|
|
1017
|
+
type: "lead.changetags",
|
|
1018
|
+
order: 3,
|
|
1019
|
+
properties: {
|
|
1020
|
+
add_tags: [
|
|
1021
|
+
"HOT"
|
|
1022
|
+
],
|
|
1023
|
+
remove_tags: [
|
|
1024
|
+
]
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
],
|
|
1028
|
+
postAction: "message",
|
|
1029
|
+
postActionProperty: "Thanks for your submission. An email will be sent to you shortly.",
|
|
1030
|
+
formType: "campaign"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
id: 2,
|
|
1034
|
+
name: "second form",
|
|
1035
|
+
alias: "second_for",
|
|
1036
|
+
description: null,
|
|
1037
|
+
isPublished: true,
|
|
1038
|
+
fields: [
|
|
1039
|
+
{
|
|
1040
|
+
id: 4,
|
|
1041
|
+
label: "Name",
|
|
1042
|
+
showLabel: false,
|
|
1043
|
+
alias: "f_name",
|
|
1044
|
+
type: "text",
|
|
1045
|
+
defaultValue: null,
|
|
1046
|
+
isRequired: true,
|
|
1047
|
+
validationMessage: "this field is required",
|
|
1048
|
+
helpMessage: null,
|
|
1049
|
+
order: 1,
|
|
1050
|
+
properties: {
|
|
1051
|
+
placeholder: "your first name"
|
|
1052
|
+
},
|
|
1053
|
+
validation: [
|
|
1054
|
+
],
|
|
1055
|
+
parent: null,
|
|
1056
|
+
conditions: [
|
|
1057
|
+
],
|
|
1058
|
+
labelAttributes: null,
|
|
1059
|
+
inputAttributes: null,
|
|
1060
|
+
containerAttributes: null,
|
|
1061
|
+
leadField: "firstname",
|
|
1062
|
+
saveResult: true,
|
|
1063
|
+
isAutoFill: false,
|
|
1064
|
+
isReadOnly: false,
|
|
1065
|
+
mappedObject: "contact",
|
|
1066
|
+
mappedField: "firstname"
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
id: 5,
|
|
1070
|
+
label: "Email",
|
|
1071
|
+
showLabel: true,
|
|
1072
|
+
alias: "email",
|
|
1073
|
+
type: "email",
|
|
1074
|
+
defaultValue: null,
|
|
1075
|
+
isRequired: true,
|
|
1076
|
+
validationMessage: "this is a required field",
|
|
1077
|
+
helpMessage: null,
|
|
1078
|
+
order: 2,
|
|
1079
|
+
properties: {
|
|
1080
|
+
placeholder: "email"
|
|
1081
|
+
},
|
|
1082
|
+
validation: [
|
|
1083
|
+
],
|
|
1084
|
+
parent: null,
|
|
1085
|
+
conditions: [
|
|
1086
|
+
],
|
|
1087
|
+
labelAttributes: null,
|
|
1088
|
+
inputAttributes: null,
|
|
1089
|
+
containerAttributes: null,
|
|
1090
|
+
leadField: "email",
|
|
1091
|
+
saveResult: true,
|
|
1092
|
+
isAutoFill: false,
|
|
1093
|
+
isReadOnly: false,
|
|
1094
|
+
mappedObject: "contact",
|
|
1095
|
+
mappedField: "email"
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
id: 6,
|
|
1099
|
+
label: "Message",
|
|
1100
|
+
showLabel: false,
|
|
1101
|
+
alias: "f_message",
|
|
1102
|
+
type: "textarea",
|
|
1103
|
+
defaultValue: null,
|
|
1104
|
+
isRequired: true,
|
|
1105
|
+
validationMessage: "we need your message",
|
|
1106
|
+
helpMessage: null,
|
|
1107
|
+
order: 3,
|
|
1108
|
+
properties: [
|
|
1109
|
+
],
|
|
1110
|
+
validation: [
|
|
1111
|
+
],
|
|
1112
|
+
parent: null,
|
|
1113
|
+
conditions: [
|
|
1114
|
+
],
|
|
1115
|
+
labelAttributes: null,
|
|
1116
|
+
inputAttributes: null,
|
|
1117
|
+
containerAttributes: null,
|
|
1118
|
+
leadField: "companyaddress1",
|
|
1119
|
+
saveResult: true,
|
|
1120
|
+
isAutoFill: false,
|
|
1121
|
+
isReadOnly: false,
|
|
1122
|
+
mappedObject: "company",
|
|
1123
|
+
mappedField: "companyaddress1"
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
id: 7,
|
|
1127
|
+
label: "Submit",
|
|
1128
|
+
showLabel: true,
|
|
1129
|
+
alias: "submit",
|
|
1130
|
+
type: "button",
|
|
1131
|
+
defaultValue: null,
|
|
1132
|
+
isRequired: false,
|
|
1133
|
+
validationMessage: null,
|
|
1134
|
+
helpMessage: null,
|
|
1135
|
+
order: 4,
|
|
1136
|
+
properties: [
|
|
1137
|
+
],
|
|
1138
|
+
validation: [
|
|
1139
|
+
],
|
|
1140
|
+
parent: null,
|
|
1141
|
+
conditions: [
|
|
1142
|
+
],
|
|
1143
|
+
labelAttributes: null,
|
|
1144
|
+
inputAttributes: "class=\"btn btn-ghost\"",
|
|
1145
|
+
containerAttributes: null,
|
|
1146
|
+
leadField: null,
|
|
1147
|
+
saveResult: true,
|
|
1148
|
+
isAutoFill: false,
|
|
1149
|
+
isReadOnly: false,
|
|
1150
|
+
mappedObject: null,
|
|
1151
|
+
mappedField: null
|
|
1152
|
+
}
|
|
1153
|
+
],
|
|
1154
|
+
actions: [
|
|
1155
|
+
],
|
|
1156
|
+
postAction: "message",
|
|
1157
|
+
postActionProperty: "Great, you have made it!",
|
|
1158
|
+
formType: "standalone"
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
id: 1,
|
|
1162
|
+
name: "Simple form",
|
|
1163
|
+
alias: "simple_for",
|
|
1164
|
+
description: null,
|
|
1165
|
+
isPublished: true,
|
|
1166
|
+
fields: [
|
|
1167
|
+
{
|
|
1168
|
+
id: 1,
|
|
1169
|
+
label: "Name",
|
|
1170
|
+
showLabel: false,
|
|
1171
|
+
alias: "f_name",
|
|
1172
|
+
type: "text",
|
|
1173
|
+
defaultValue: null,
|
|
1174
|
+
isRequired: true,
|
|
1175
|
+
validationMessage: "This field is required",
|
|
1176
|
+
helpMessage: null,
|
|
1177
|
+
order: 1,
|
|
1178
|
+
properties: {
|
|
1179
|
+
placeholder: "Your name"
|
|
1180
|
+
},
|
|
1181
|
+
validation: [
|
|
1182
|
+
],
|
|
1183
|
+
parent: null,
|
|
1184
|
+
conditions: [
|
|
1185
|
+
],
|
|
1186
|
+
labelAttributes: null,
|
|
1187
|
+
inputAttributes: null,
|
|
1188
|
+
containerAttributes: null,
|
|
1189
|
+
leadField: "firstname",
|
|
1190
|
+
saveResult: true,
|
|
1191
|
+
isAutoFill: false,
|
|
1192
|
+
isReadOnly: false,
|
|
1193
|
+
mappedObject: "contact",
|
|
1194
|
+
mappedField: "firstname"
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
id: 2,
|
|
1198
|
+
label: "EMail",
|
|
1199
|
+
showLabel: false,
|
|
1200
|
+
alias: "email",
|
|
1201
|
+
type: "email",
|
|
1202
|
+
defaultValue: null,
|
|
1203
|
+
isRequired: true,
|
|
1204
|
+
validationMessage: "This field is required",
|
|
1205
|
+
helpMessage: null,
|
|
1206
|
+
order: 2,
|
|
1207
|
+
properties: {
|
|
1208
|
+
placeholder: "Your email"
|
|
1209
|
+
},
|
|
1210
|
+
validation: [
|
|
1211
|
+
],
|
|
1212
|
+
parent: null,
|
|
1213
|
+
conditions: [
|
|
1214
|
+
],
|
|
1215
|
+
labelAttributes: null,
|
|
1216
|
+
inputAttributes: null,
|
|
1217
|
+
containerAttributes: null,
|
|
1218
|
+
leadField: "email",
|
|
1219
|
+
saveResult: true,
|
|
1220
|
+
isAutoFill: false,
|
|
1221
|
+
isReadOnly: false,
|
|
1222
|
+
mappedObject: "contact",
|
|
1223
|
+
mappedField: "email"
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
id: 3,
|
|
1227
|
+
label: "Submit",
|
|
1228
|
+
showLabel: true,
|
|
1229
|
+
alias: "submit",
|
|
1230
|
+
type: "button",
|
|
1231
|
+
defaultValue: null,
|
|
1232
|
+
isRequired: false,
|
|
1233
|
+
validationMessage: null,
|
|
1234
|
+
helpMessage: null,
|
|
1235
|
+
order: 3,
|
|
1236
|
+
properties: [
|
|
1237
|
+
],
|
|
1238
|
+
validation: [
|
|
1239
|
+
],
|
|
1240
|
+
parent: null,
|
|
1241
|
+
conditions: [
|
|
1242
|
+
],
|
|
1243
|
+
labelAttributes: null,
|
|
1244
|
+
inputAttributes: "class=\"btn btn-ghost\"",
|
|
1245
|
+
containerAttributes: null,
|
|
1246
|
+
leadField: null,
|
|
1247
|
+
saveResult: true,
|
|
1248
|
+
isAutoFill: false,
|
|
1249
|
+
isReadOnly: false,
|
|
1250
|
+
mappedObject: null,
|
|
1251
|
+
mappedField: null
|
|
1252
|
+
}
|
|
1253
|
+
],
|
|
1254
|
+
actions: [
|
|
1255
|
+
],
|
|
1256
|
+
postAction: "redirect",
|
|
1257
|
+
postActionProperty: "/mautic-test",
|
|
1258
|
+
formType: "standalone"
|
|
1259
|
+
}
|
|
1260
|
+
];
|
|
1261
|
+
var mauticFormsData = {
|
|
1262
|
+
forms: forms
|
|
1263
|
+
};
|
|
1264
|
+
|
|
1265
|
+
// @ts-nocheck
|
|
1266
|
+
// Import the generated Mautic forms data
|
|
1267
|
+
// Create the data store
|
|
1268
|
+
const mauticData = {
|
|
1269
|
+
forms: mauticFormsData.forms
|
|
1270
|
+
};
|
|
1271
|
+
const getFormById = (id) => mauticData.forms.find(form => form.id === id);
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* Integration Configuration
|
|
1275
|
+
* Centralized configuration for all third-party integrations
|
|
1276
|
+
*/
|
|
1277
|
+
function envBool(key, defaultValue) {
|
|
1278
|
+
const v = getEnv(key);
|
|
1279
|
+
if (v === undefined || v === '')
|
|
1280
|
+
return defaultValue;
|
|
1281
|
+
const lower = v.toLowerCase();
|
|
1282
|
+
if (lower === 'false' || lower === '0' || lower === 'no')
|
|
1283
|
+
return false;
|
|
1284
|
+
if (lower === 'true' || lower === '1' || lower === 'yes')
|
|
1285
|
+
return true;
|
|
1286
|
+
return defaultValue;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Get integration configuration with environment variable fallbacks
|
|
1290
|
+
*/
|
|
1291
|
+
function getIntegrationConfig() {
|
|
1292
|
+
isDevEnvironment();
|
|
1293
|
+
return {
|
|
1294
|
+
wordpress: {
|
|
1295
|
+
enabled: true,
|
|
1296
|
+
apiUrl: getEnv('VITE_WORDPRESS_API_URL') || '',
|
|
1297
|
+
authMode: getEnv('VITE_AUTH_MODE') || 'direct',
|
|
1298
|
+
},
|
|
1299
|
+
supabase: {
|
|
1300
|
+
enabled: getEnv('VITE_AUTH_MODE') === 'supabase_proxy',
|
|
1301
|
+
url: getEnv('VITE_SUPABASE_URL') || '',
|
|
1302
|
+
anonKey: getEnv('VITE_SUPABASE_ANON_KEY') || '',
|
|
1303
|
+
serviceRoleKey: getEnv('VITE_SUPABASE_SERVICE_ROLE_KEY'),
|
|
1304
|
+
},
|
|
1305
|
+
gravityForms: {
|
|
1306
|
+
enabled: !!(getEnv('VITE_CLOUDFLARE_WORKER_URL') || getEnv('VITE_WORDPRESS_API_URL')),
|
|
1307
|
+
url: getEnv('VITE_WORDPRESS_API_URL'),
|
|
1308
|
+
proxyUrl: getEnv('VITE_CLOUDFLARE_WORKER_URL'),
|
|
1309
|
+
consumerKey: getEnv('VITE_GF_CONSUMER_KEY'),
|
|
1310
|
+
consumerSecret: getEnv('VITE_GF_CONSUMER_SECRET'),
|
|
1311
|
+
forms: {
|
|
1312
|
+
'contact': {
|
|
1313
|
+
name: 'Contact Form',
|
|
1314
|
+
type: 'contact',
|
|
1315
|
+
successMessage: 'Thank you for your message. We\'ll get back to you soon!',
|
|
1316
|
+
},
|
|
1317
|
+
'newsletter': {
|
|
1318
|
+
name: 'Newsletter Signup',
|
|
1319
|
+
type: 'newsletter',
|
|
1320
|
+
successMessage: 'Thank you for subscribing to our newsletter!',
|
|
1321
|
+
},
|
|
1322
|
+
'support': {
|
|
1323
|
+
name: 'Support Request',
|
|
1324
|
+
type: 'support',
|
|
1325
|
+
successMessage: 'Your support request has been submitted successfully.',
|
|
1326
|
+
},
|
|
1327
|
+
},
|
|
1328
|
+
},
|
|
1329
|
+
mautic: {
|
|
1330
|
+
enabled: !!(getEnv('VITE_MAUTIC_PROXY_URL') || getEnv('VITE_MAUTIC_URL')),
|
|
1331
|
+
url: getEnv('VITE_MAUTIC_URL') || '',
|
|
1332
|
+
proxyUrl: getEnv('VITE_MAUTIC_PROXY_URL'),
|
|
1333
|
+
forms: {
|
|
1334
|
+
'1': {
|
|
1335
|
+
name: 'Newsletter Signup',
|
|
1336
|
+
type: 'newsletter',
|
|
1337
|
+
successMessage: 'Thank you for subscribing to our newsletter!',
|
|
1338
|
+
postAction: 'message',
|
|
1339
|
+
postActionProperty: 'Thank you for your submission!'
|
|
1340
|
+
},
|
|
1341
|
+
'2': {
|
|
1342
|
+
name: 'Contact Form',
|
|
1343
|
+
type: 'contact',
|
|
1344
|
+
successMessage: 'Thank you for contacting us! We\'ll get back to you soon.',
|
|
1345
|
+
postAction: 'message',
|
|
1346
|
+
postActionProperty: 'Thank you for your message!'
|
|
1347
|
+
},
|
|
1348
|
+
'3': {
|
|
1349
|
+
name: 'Lead Capture',
|
|
1350
|
+
type: 'lead',
|
|
1351
|
+
successMessage: 'Thank you for your interest! We\'ll be in touch.',
|
|
1352
|
+
postAction: 'message',
|
|
1353
|
+
postActionProperty: 'Thank you for your submission!'
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
},
|
|
1357
|
+
phplist: {
|
|
1358
|
+
enabled: !!(getEnv('VITE_PHPLIST_URL') || getEnv('PHPLIST_URL')),
|
|
1359
|
+
url: getEnv('VITE_PHPLIST_URL') || getEnv('PHPLIST_URL'),
|
|
1360
|
+
proxyUrl: getEnv('VITE_PHPLIST_PROXY_URL') || getEnv('PHPLIST_PROXY_URL'),
|
|
1361
|
+
listId: parseInt(getEnv('VITE_PHPLIST_LIST_ID') || getEnv('PHPLIST_LIST_ID') || '0', 10) || undefined,
|
|
1362
|
+
subscribePageIds: (getEnv('VITE_PHPLIST_SUBSCRIBE_PAGE_IDS') || getEnv('PHPLIST_SUBSCRIBE_PAGE_IDS') || '')
|
|
1363
|
+
.split(',')
|
|
1364
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
1365
|
+
.filter((n) => !Number.isNaN(n)),
|
|
1366
|
+
newsletterRouteBase: getEnv('VITE_PHPLIST_NEWSLETTER_ROUTE_BASE') || '/newsletters',
|
|
1367
|
+
},
|
|
1368
|
+
suiteCRM: {
|
|
1369
|
+
enabled: !!(getEnv('VITE_SUITECRM_URL') && getEnv('VITE_SUITECRM_CLIENT_ID') && getEnv('VITE_SUITECRM_CLIENT_SECRET')),
|
|
1370
|
+
url: getEnv('VITE_SUITECRM_URL'),
|
|
1371
|
+
proxyUrl: getEnv('VITE_SUITECRM_PROXY_URL'),
|
|
1372
|
+
appId: undefined,
|
|
1373
|
+
workerSecret: undefined,
|
|
1374
|
+
oauth2: {
|
|
1375
|
+
clientId: getEnv('VITE_SUITECRM_CLIENT_ID'),
|
|
1376
|
+
clientSecret: getEnv('VITE_SUITECRM_CLIENT_SECRET'),
|
|
1377
|
+
username: getEnv('VITE_SUITECRM_USERNAME'),
|
|
1378
|
+
password: getEnv('VITE_SUITECRM_PASSWORD'),
|
|
1379
|
+
},
|
|
1380
|
+
},
|
|
1381
|
+
chatwoot: {
|
|
1382
|
+
enabled: !!(getEnv('VITE_CHATWOOT_WEBSITE_TOKEN') && getEnv('VITE_CHATWOOT_BASE_URL')),
|
|
1383
|
+
websiteToken: getEnv('VITE_CHATWOOT_WEBSITE_TOKEN') || '',
|
|
1384
|
+
baseUrl: getEnv('VITE_CHATWOOT_BASE_URL') || '',
|
|
1385
|
+
locale: 'en',
|
|
1386
|
+
type: 'standard',
|
|
1387
|
+
launcherTitle: 'Chat',
|
|
1388
|
+
hideMessageBubble: false,
|
|
1389
|
+
position: 'right',
|
|
1390
|
+
showPopoutButton: false,
|
|
1391
|
+
},
|
|
1392
|
+
analytics: {
|
|
1393
|
+
googleAnalytics: { enabled: false },
|
|
1394
|
+
googleTagManager: { enabled: false },
|
|
1395
|
+
facebookPixel: { enabled: false },
|
|
1396
|
+
linkedinInsight: { enabled: false },
|
|
1397
|
+
},
|
|
1398
|
+
seo: (() => {
|
|
1399
|
+
const opt = (key) => {
|
|
1400
|
+
const v = getEnv(key);
|
|
1401
|
+
if (v === undefined || v === '')
|
|
1402
|
+
return undefined;
|
|
1403
|
+
const t = v.trim();
|
|
1404
|
+
return t || undefined;
|
|
1405
|
+
};
|
|
1406
|
+
const siteUrl = (getEnv('VITE_SEO_SITE_URL') || '').replace(/\/+$/, '').trim();
|
|
1407
|
+
const siteName = (getEnv('VITE_SEO_SITE_NAME') || '').trim();
|
|
1408
|
+
const defaultTitle = (getEnv('VITE_SEO_DEFAULT_TITLE') || '').trim() || siteName || '';
|
|
1409
|
+
return {
|
|
1410
|
+
defaultTitle,
|
|
1411
|
+
defaultDescription: (getEnv('VITE_SEO_DEFAULT_DESCRIPTION') || '').trim(),
|
|
1412
|
+
defaultKeywords: opt('VITE_SEO_DEFAULT_KEYWORDS'),
|
|
1413
|
+
defaultImage: (getEnv('VITE_SEO_DEFAULT_OG_IMAGE') || '').trim(),
|
|
1414
|
+
siteName,
|
|
1415
|
+
siteUrl,
|
|
1416
|
+
twitterHandle: opt('VITE_SEO_TWITTER_SITE'),
|
|
1417
|
+
facebookAppId: opt('VITE_SEO_FACEBOOK_APP_ID'),
|
|
1418
|
+
googleSiteVerification: opt('VITE_SEO_GOOGLE_SITE_VERIFICATION'),
|
|
1419
|
+
bingSiteVerification: opt('VITE_SEO_BING_SITE_VERIFICATION'),
|
|
1420
|
+
};
|
|
1421
|
+
})(),
|
|
1422
|
+
performance: {
|
|
1423
|
+
enableLazyLoading: envBool('VITE_PERF_LAZY_LOADING', true),
|
|
1424
|
+
enableImageOptimization: envBool('VITE_PERF_IMAGE_OPTIMIZATION', true),
|
|
1425
|
+
enableCodeSplitting: envBool('VITE_PERF_CODE_SPLITTING', true),
|
|
1426
|
+
enableServiceWorker: envBool('VITE_PERF_SERVICE_WORKER', false),
|
|
1427
|
+
enableCaching: envBool('VITE_PERF_CACHING', true),
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
const MauticForm = ({ formId, title, description, className = '' }) => {
|
|
1433
|
+
// Get form from static data
|
|
1434
|
+
const form = getFormById(parseInt(formId));
|
|
1435
|
+
// Get form configuration from integrations config
|
|
1436
|
+
const integrationConfig = getIntegrationConfig();
|
|
1437
|
+
const formConfig = integrationConfig.mautic.forms[formId];
|
|
1438
|
+
// Debug logging in development
|
|
1439
|
+
if (undefined.DEV && form) {
|
|
1440
|
+
console.log('MauticForm Debug:', {
|
|
1441
|
+
formId,
|
|
1442
|
+
formPostAction: form.postAction,
|
|
1443
|
+
formPostActionProperty: form.postActionProperty,
|
|
1444
|
+
configPostAction: formConfig?.postAction,
|
|
1445
|
+
configPostActionProperty: formConfig?.postActionProperty,
|
|
1446
|
+
finalPostAction: form.postAction || formConfig?.postAction || 'message',
|
|
1447
|
+
finalPostActionProperty: form.postActionProperty || formConfig?.postActionProperty || 'Thank you for your submission!'
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
// Use the package component
|
|
1451
|
+
return (jsxRuntime.jsx(madapter.MauticForm, { formId: formId, title: title, description: description, className: className, form: form, onSuccess: (data) => {
|
|
1452
|
+
console.log('Form submitted successfully:', data);
|
|
1453
|
+
}, onError: (error) => {
|
|
1454
|
+
console.error('Form submission error:', error);
|
|
1455
|
+
} }));
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
const { HelmetProvider } = helmetAsync;
|
|
1459
|
+
const gravityFormsConfig = () => ({
|
|
1460
|
+
apiUrl: undefined.VITE_WORDPRESS_API_URL,
|
|
1461
|
+
timeout: 30000,
|
|
1462
|
+
proxyEndpoint: withBasePath('/api/gravity-forms-submit'),
|
|
1463
|
+
});
|
|
1464
|
+
const InlineGravityForm = ({ formId }) => (jsxRuntime.jsx(StaticGravityForm, { formId: formId, config: gravityFormsConfig() }));
|
|
1465
|
+
const ContentPopup = ({ buttonText, title, children }) => {
|
|
1466
|
+
const [isOpen, setIsOpen] = react.useState(false);
|
|
1467
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { type: "button", onClick: () => setIsOpen(true), "data-digivalt-popup-trigger": true, children: buttonText }), isOpen && (jsxRuntime.jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": typeof title === 'string' ? title : undefined, "data-digivalt-popup": true, children: jsxRuntime.jsxs("div", { "data-digivalt-popup-content": true, children: [jsxRuntime.jsxs("header", { children: [jsxRuntime.jsx("h2", { children: title }), jsxRuntime.jsx("button", { type: "button", onClick: () => setIsOpen(false), "aria-label": "Close", children: "\u00D7" })] }), children] }) }))] }));
|
|
1468
|
+
};
|
|
1469
|
+
/**
|
|
1470
|
+
* Registry for WordPress page-body blocks and DigiValt integration shortcodes.
|
|
1471
|
+
* Header and footer template parts deliberately remain app-owned.
|
|
1472
|
+
*/
|
|
1473
|
+
function createContentRegistry(themePalette) {
|
|
1474
|
+
const colorMapper = themePalette
|
|
1475
|
+
? (colorName) => colorName ? themePalette[colorName] || null : null
|
|
1476
|
+
: undefined;
|
|
1477
|
+
const registry = wparser.createEnhancedRegistry([], undefined, colorMapper);
|
|
1478
|
+
registry.renderers['gravityforms/form'] = ({ block }) => {
|
|
1479
|
+
const attrs = block?.attributes || {};
|
|
1480
|
+
const formId = attrs.formId || attrs.id || attrs.form_id || '1';
|
|
1481
|
+
return jsxRuntime.jsx(InlineGravityForm, { formId: formId });
|
|
1482
|
+
};
|
|
1483
|
+
registry.shortcodes = {
|
|
1484
|
+
...registry.shortcodes,
|
|
1485
|
+
...madapter.getMauticShortcodeHandlers({
|
|
1486
|
+
renderForm: ({ formId, title, description }) => (jsxRuntime.jsx(MauticForm, { formId: formId, title: title, description: description })),
|
|
1487
|
+
}),
|
|
1488
|
+
...plister.getPhplistShortcodeHandlers(),
|
|
1489
|
+
gf_embed: (attrs) => (jsxRuntime.jsx(InlineGravityForm, { formId: attrs.id || attrs.formId || '1' })),
|
|
1490
|
+
gform: (attrs) => (jsxRuntime.jsx(InlineGravityForm, { formId: attrs.form_id || attrs.id || attrs.formId || '1' })),
|
|
1491
|
+
popupbtn_gf: (attrs) => (jsxRuntime.jsx(ContentPopup, { buttonText: attrs.text || attrs.buttonText || 'Submit', title: attrs.title || 'Contact Form', children: jsxRuntime.jsx(InlineGravityForm, { formId: attrs.id || attrs.formId || '1' }) })),
|
|
1492
|
+
popupgform: (attrs) => (jsxRuntime.jsx(ContentPopup, { buttonText: attrs.text || attrs.buttonText || 'Submit', title: attrs.title || 'Contact Form', children: jsxRuntime.jsx(InlineGravityForm, { formId: attrs.form_id || attrs.id || attrs.formId || '1' }) })),
|
|
1493
|
+
popupbtn_mautic: (attrs) => {
|
|
1494
|
+
const formId = attrs.id || attrs.formId || '1';
|
|
1495
|
+
return (jsxRuntime.jsx(ContentPopup, { buttonText: attrs.text || attrs.buttonText || 'Submit', title: attrs.title || attrs.popupTitle || 'Contact Form', children: jsxRuntime.jsx(MauticForm, { formId: String(formId) }) }));
|
|
1496
|
+
},
|
|
1497
|
+
};
|
|
1498
|
+
return registry;
|
|
1499
|
+
}
|
|
1500
|
+
/** Load and render one WordPress page body. */
|
|
1501
|
+
const DigiValtWpPage = ({ slug, className, dataPath = '/wordpress-data.json', themePalette, }) => {
|
|
1502
|
+
const [page, setPage] = react.useState(() => getWordPressPageBySlug(slug));
|
|
1503
|
+
const registry = react.useMemo(() => createContentRegistry(themePalette), [themePalette]);
|
|
1504
|
+
react.useEffect(() => {
|
|
1505
|
+
let active = true;
|
|
1506
|
+
loadWordPressData(dataPath).then(() => {
|
|
1507
|
+
if (active)
|
|
1508
|
+
setPage(getWordPressPageBySlug(slug));
|
|
1509
|
+
});
|
|
1510
|
+
return () => {
|
|
1511
|
+
active = false;
|
|
1512
|
+
};
|
|
1513
|
+
}, [dataPath, slug]);
|
|
1514
|
+
return page && Array.isArray(page.blocks)
|
|
1515
|
+
? jsxRuntime.jsx(wparser.WPPage, { page: page, registry: registry, className: className })
|
|
1516
|
+
: null;
|
|
1517
|
+
};
|
|
1518
|
+
const DigiValtProviders = ({ children, queryClient }) => {
|
|
1519
|
+
const content = queryClient
|
|
1520
|
+
? jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: queryClient, children: children })
|
|
1521
|
+
: children;
|
|
1522
|
+
return jsxRuntime.jsx(HelmetProvider, { children: content });
|
|
1523
|
+
};
|
|
1524
|
+
|
|
1525
|
+
exports.DigiValtProviders = DigiValtProviders;
|
|
1526
|
+
exports.DigiValtWpPage = DigiValtWpPage;
|
|
1527
|
+
exports.createContentRegistry = createContentRegistry;
|
|
1528
|
+
//# sourceMappingURL=registry.cjs.map
|