@marvalt/madapter 1.0.14 → 2.1.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.
- package/dist/client/mautic-client.d.ts +1 -8
- package/dist/client/mautic-client.d.ts.map +1 -1
- package/dist/generators/mautic-generator.d.ts +5 -0
- package/dist/generators/mautic-generator.d.ts.map +1 -1
- package/dist/index.cjs +436 -507
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +25 -21
- package/dist/index.esm.js +436 -506
- package/dist/index.esm.js.map +1 -1
- package/dist/react/components/MauticTracking.d.ts.map +1 -1
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/mautic-proxy.d.ts +28 -0
- package/dist/server/mautic-proxy.d.ts.map +1 -0
- package/dist/server.cjs +156 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.esm.js +154 -0
- package/dist/server.esm.js.map +1 -0
- package/dist/service/mautic-service.d.ts +1 -1
- package/dist/service/mautic-service.d.ts.map +1 -1
- package/dist/types/config.d.ts +16 -8
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/config.d.ts +3 -5
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/package.json +9 -2
- package/scripts/postinstall.cjs +114 -0
- package/scripts/postinstall.js +114 -0
- package/templates/mautic-submit.ts +19 -0
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var reactQuery = require('@tanstack/react-query');
|
|
4
3
|
var fs = require('fs');
|
|
5
4
|
var path = require('path');
|
|
6
5
|
var React = require('react');
|
|
6
|
+
var reactQuery = require('@tanstack/react-query');
|
|
7
7
|
|
|
8
8
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
9
9
|
/**
|
|
@@ -25,68 +25,16 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
|
|
|
25
25
|
class MauticClient {
|
|
26
26
|
constructor(config) {
|
|
27
27
|
this.config = config;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.proxyUrl = config.cloudflareWorkerUrl;
|
|
31
|
-
this.appId = config.appId;
|
|
32
|
-
this.workerSecret = config.workerSecret;
|
|
33
|
-
this.useProxy = true;
|
|
34
|
-
this.isConfigured = true;
|
|
35
|
-
}
|
|
36
|
-
else if (config.authMode === 'direct' && config.apiUrl) {
|
|
37
|
-
// Direct Mautic API access (legacy - for local development only)
|
|
38
|
-
this.baseUrl = config.apiUrl;
|
|
39
|
-
this.useProxy = false;
|
|
40
|
-
this.isConfigured = true;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
// Fallback or error state
|
|
44
|
-
this.useProxy = false;
|
|
45
|
-
this.isConfigured = false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
validateConfiguration() {
|
|
49
|
-
if (!this.isConfigured) {
|
|
50
|
-
throw new Error('Mautic service not properly configured. Check your configuration.');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
getAuthHeaders() {
|
|
54
|
-
if (this.useProxy) {
|
|
55
|
-
return {
|
|
56
|
-
'x-app-id': this.appId || '',
|
|
57
|
-
'x-app-secret': this.workerSecret || '',
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
28
|
+
// Convention-based: Always use /api/mautic-submit unless explicitly overridden
|
|
29
|
+
this.proxyEndpoint = config.proxyEndpoint || '/api/mautic-submit';
|
|
63
30
|
}
|
|
64
31
|
async makeRequest(endpoint, options = {}) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Use Cloudflare Worker proxy
|
|
69
|
-
const proxyEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
70
|
-
url = `${this.proxyUrl}?endpoint=${encodeURIComponent(proxyEndpoint)}`;
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
// Use direct Mautic API
|
|
74
|
-
// Determine if this is a form submission endpoint (should not use /api prefix)
|
|
75
|
-
const isFormSubmission = endpoint.startsWith('/form/submit') ||
|
|
76
|
-
(endpoint.startsWith('/forms/') && endpoint.includes('/submit'));
|
|
77
|
-
if (isFormSubmission) {
|
|
78
|
-
// Form submissions go directly to the form endpoint, not the API
|
|
79
|
-
url = `${this.baseUrl}${endpoint}`;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
// API calls use the /api prefix
|
|
83
|
-
url = `${this.baseUrl}/api${endpoint}`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
32
|
+
// Always use proxy endpoint (convention-based)
|
|
33
|
+
const proxyEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
|
|
34
|
+
const url = `${this.proxyEndpoint}?endpoint=${encodeURIComponent(proxyEndpoint)}`;
|
|
86
35
|
const isFormSubmission = endpoint.startsWith('/form/submit') ||
|
|
87
36
|
(endpoint.startsWith('/forms/') && endpoint.includes('/submit'));
|
|
88
37
|
const headers = {
|
|
89
|
-
...this.getAuthHeaders(),
|
|
90
38
|
...options.headers,
|
|
91
39
|
};
|
|
92
40
|
// Default JSON only for non-form submissions when a body exists and no explicit content-type provided
|
|
@@ -290,6 +238,7 @@ class MauticClient {
|
|
|
290
238
|
*/
|
|
291
239
|
/**
|
|
292
240
|
* Create Mautic configuration from environment variables
|
|
241
|
+
* Convention-based: Uses /api/mautic-submit by default
|
|
293
242
|
*/
|
|
294
243
|
const createMauticConfig = (overrides = {}) => {
|
|
295
244
|
// Use import.meta.env for Vite compatibility in browser
|
|
@@ -316,32 +265,12 @@ const createMauticConfig = (overrides = {}) => {
|
|
|
316
265
|
return undefined;
|
|
317
266
|
};
|
|
318
267
|
const merged = {
|
|
319
|
-
authMode: getEnvVar('VITE_AUTH_MODE') || getEnvVar('VITE_MAUTIC_AUTH_MODE') || 'cloudflare_proxy',
|
|
320
268
|
apiUrl: getEnvVar('VITE_MAUTIC_URL'),
|
|
321
|
-
|
|
322
|
-
appId: getEnvVar('VITE_FRONTEND_ID'),
|
|
323
|
-
workerSecret: getEnvVar('VITE_FRONTEND_SECRET'),
|
|
269
|
+
proxyEndpoint: getEnvVar('VITE_MAUTIC_PROXY_ENDPOINT') || '/api/mautic-submit',
|
|
324
270
|
timeout: parseInt(getEnvVar('VITE_MAUTIC_TIMEOUT') || '30000'),
|
|
325
271
|
retries: parseInt(getEnvVar('VITE_MAUTIC_RETRIES') || '3'),
|
|
326
272
|
...overrides,
|
|
327
273
|
};
|
|
328
|
-
// Enforce required fields depending on authMode
|
|
329
|
-
const errors = [];
|
|
330
|
-
if (merged.authMode === 'cloudflare_proxy') {
|
|
331
|
-
if (!merged.cloudflareWorkerUrl)
|
|
332
|
-
errors.push('cloudflareWorkerUrl is required for cloudflare_proxy mode');
|
|
333
|
-
if (!merged.appId)
|
|
334
|
-
errors.push('appId is required for cloudflare_proxy mode');
|
|
335
|
-
if (!merged.workerSecret)
|
|
336
|
-
errors.push('workerSecret is required for cloudflare_proxy mode');
|
|
337
|
-
}
|
|
338
|
-
if (merged.authMode === 'direct') {
|
|
339
|
-
if (!merged.apiUrl)
|
|
340
|
-
errors.push('apiUrl is required for direct mode');
|
|
341
|
-
}
|
|
342
|
-
if (errors.length) {
|
|
343
|
-
throw new Error(`Invalid Mautic configuration: ${errors.join('; ')}`);
|
|
344
|
-
}
|
|
345
274
|
return merged;
|
|
346
275
|
};
|
|
347
276
|
/**
|
|
@@ -349,25 +278,6 @@ const createMauticConfig = (overrides = {}) => {
|
|
|
349
278
|
*/
|
|
350
279
|
const validateMauticConfig = (config) => {
|
|
351
280
|
const errors = [];
|
|
352
|
-
if (!config.authMode) {
|
|
353
|
-
errors.push('authMode is required');
|
|
354
|
-
}
|
|
355
|
-
if (config.authMode === 'cloudflare_proxy') {
|
|
356
|
-
if (!config.cloudflareWorkerUrl) {
|
|
357
|
-
errors.push('cloudflareWorkerUrl is required for cloudflare_proxy mode');
|
|
358
|
-
}
|
|
359
|
-
if (!config.appId) {
|
|
360
|
-
errors.push('appId is required for cloudflare_proxy mode');
|
|
361
|
-
}
|
|
362
|
-
if (!config.workerSecret) {
|
|
363
|
-
errors.push('workerSecret is required for cloudflare_proxy mode');
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
else if (config.authMode === 'direct') {
|
|
367
|
-
if (!config.apiUrl) {
|
|
368
|
-
errors.push('apiUrl is required for direct mode');
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
281
|
if (config.timeout && config.timeout < 1000) {
|
|
372
282
|
errors.push('timeout must be at least 1000ms');
|
|
373
283
|
}
|
|
@@ -384,7 +294,7 @@ const validateMauticConfig = (config) => {
|
|
|
384
294
|
*/
|
|
385
295
|
const getDefaultMauticConfig = () => {
|
|
386
296
|
return {
|
|
387
|
-
|
|
297
|
+
proxyEndpoint: '/api/mautic-submit',
|
|
388
298
|
timeout: 30000,
|
|
389
299
|
retries: 3,
|
|
390
300
|
};
|
|
@@ -402,339 +312,21 @@ const mergeMauticConfig = (base, overrides) => {
|
|
|
402
312
|
* Check if Mautic is enabled based on configuration
|
|
403
313
|
*/
|
|
404
314
|
const isMauticEnabled = (config) => {
|
|
405
|
-
|
|
406
|
-
return !!(config.cloudflareWorkerUrl && config.appId && config.workerSecret);
|
|
407
|
-
}
|
|
408
|
-
else if (config.authMode === 'direct') {
|
|
409
|
-
return !!config.apiUrl;
|
|
410
|
-
}
|
|
411
|
-
return false;
|
|
315
|
+
return !!config.apiUrl;
|
|
412
316
|
};
|
|
413
317
|
/**
|
|
414
318
|
* Get Mautic configuration summary (for debugging)
|
|
415
319
|
*/
|
|
416
320
|
const getMauticConfigSummary = (config) => {
|
|
417
321
|
return {
|
|
418
|
-
authMode: config.authMode,
|
|
419
322
|
hasApiUrl: !!config.apiUrl,
|
|
420
|
-
|
|
421
|
-
hasAppId: !!config.appId,
|
|
422
|
-
hasWorkerSecret: !!config.workerSecret,
|
|
323
|
+
proxyEndpoint: config.proxyEndpoint || '/api/mautic-submit',
|
|
423
324
|
timeout: config.timeout,
|
|
424
325
|
retries: config.retries,
|
|
425
326
|
isEnabled: isMauticEnabled(config),
|
|
426
327
|
};
|
|
427
328
|
};
|
|
428
329
|
|
|
429
|
-
/**
|
|
430
|
-
* @license GPL-3.0-or-later
|
|
431
|
-
*
|
|
432
|
-
* This file is part of the MarVAlt Open SDK.
|
|
433
|
-
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
434
|
-
*
|
|
435
|
-
* This program is free software: you can redistribute it and/or modify
|
|
436
|
-
* it under the terms of the GNU General Public License as published by
|
|
437
|
-
* the Free Software Foundation, either version 3 of the License, or
|
|
438
|
-
* (at your option) any later version.
|
|
439
|
-
*
|
|
440
|
-
* This program is distributed in the hope that it will be useful,
|
|
441
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
442
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
443
|
-
* See the GNU General Public License for more details.
|
|
444
|
-
*/
|
|
445
|
-
// Create a default client instance (will be overridden by provider)
|
|
446
|
-
let defaultClient = null;
|
|
447
|
-
/**
|
|
448
|
-
* Set the default Mautic client for hooks
|
|
449
|
-
*/
|
|
450
|
-
const setMauticClient = (client) => {
|
|
451
|
-
defaultClient = client;
|
|
452
|
-
};
|
|
453
|
-
/**
|
|
454
|
-
* Get the current Mautic client
|
|
455
|
-
*/
|
|
456
|
-
const getMauticClient = () => {
|
|
457
|
-
if (!defaultClient) {
|
|
458
|
-
// Lazy initialize the client from environment if not set by provider/service
|
|
459
|
-
const config = createMauticConfig();
|
|
460
|
-
defaultClient = new MauticClient(config);
|
|
461
|
-
}
|
|
462
|
-
return defaultClient;
|
|
463
|
-
};
|
|
464
|
-
/**
|
|
465
|
-
* Custom hook for submitting forms to Mautic
|
|
466
|
-
*/
|
|
467
|
-
const useMauticFormSubmission = () => {
|
|
468
|
-
const queryClient = reactQuery.useQueryClient();
|
|
469
|
-
return reactQuery.useMutation({
|
|
470
|
-
mutationFn: (submission) => {
|
|
471
|
-
const client = getMauticClient();
|
|
472
|
-
return client.submitForm(submission.formId, submission);
|
|
473
|
-
},
|
|
474
|
-
onSuccess: (data, variables) => {
|
|
475
|
-
if (undefined?.DEV) {
|
|
476
|
-
console.log('Mautic form submission successful:', data);
|
|
477
|
-
}
|
|
478
|
-
// Invalidate related queries
|
|
479
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
|
|
480
|
-
// Track the submission event
|
|
481
|
-
if (variables.contact?.email) {
|
|
482
|
-
const client = getMauticClient();
|
|
483
|
-
client.trackEvent('form_submission', {
|
|
484
|
-
formId: variables.formId,
|
|
485
|
-
email: variables.contact.email
|
|
486
|
-
}).catch(console.error);
|
|
487
|
-
}
|
|
488
|
-
},
|
|
489
|
-
onError: (error) => {
|
|
490
|
-
console.error('Mautic form submission failed:', error);
|
|
491
|
-
},
|
|
492
|
-
});
|
|
493
|
-
};
|
|
494
|
-
/**
|
|
495
|
-
* Custom hook for creating Mautic contacts
|
|
496
|
-
*/
|
|
497
|
-
const useMauticCreateContact = () => {
|
|
498
|
-
const queryClient = reactQuery.useQueryClient();
|
|
499
|
-
return reactQuery.useMutation({
|
|
500
|
-
mutationFn: (contact) => {
|
|
501
|
-
const client = getMauticClient();
|
|
502
|
-
return client.createContact(contact);
|
|
503
|
-
},
|
|
504
|
-
onSuccess: (data, variables) => {
|
|
505
|
-
if (undefined?.DEV) {
|
|
506
|
-
console.log('Mautic contact created:', data);
|
|
507
|
-
}
|
|
508
|
-
// Invalidate contacts queries
|
|
509
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
|
|
510
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.email] });
|
|
511
|
-
},
|
|
512
|
-
onError: (error) => {
|
|
513
|
-
console.error('Failed to create Mautic contact:', error);
|
|
514
|
-
},
|
|
515
|
-
});
|
|
516
|
-
};
|
|
517
|
-
/**
|
|
518
|
-
* Custom hook for updating Mautic contacts
|
|
519
|
-
*/
|
|
520
|
-
const useMauticUpdateContact = () => {
|
|
521
|
-
const queryClient = reactQuery.useQueryClient();
|
|
522
|
-
return reactQuery.useMutation({
|
|
523
|
-
mutationFn: ({ contactId, contact }) => {
|
|
524
|
-
const client = getMauticClient();
|
|
525
|
-
return client.updateContact(contactId, contact);
|
|
526
|
-
},
|
|
527
|
-
onSuccess: (data, variables) => {
|
|
528
|
-
if (undefined?.DEV) {
|
|
529
|
-
console.log('Mautic contact updated:', data);
|
|
530
|
-
}
|
|
531
|
-
// Invalidate related queries
|
|
532
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
|
|
533
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
534
|
-
},
|
|
535
|
-
onError: (error) => {
|
|
536
|
-
console.error('Failed to update Mautic contact:', error);
|
|
537
|
-
},
|
|
538
|
-
});
|
|
539
|
-
};
|
|
540
|
-
/**
|
|
541
|
-
* Custom hook for fetching Mautic contact by email
|
|
542
|
-
*/
|
|
543
|
-
const useMauticContactByEmail = (email) => {
|
|
544
|
-
return reactQuery.useQuery({
|
|
545
|
-
queryKey: ['mautic-contact', email],
|
|
546
|
-
queryFn: () => {
|
|
547
|
-
const client = getMauticClient();
|
|
548
|
-
return client.getContactByEmail(email);
|
|
549
|
-
},
|
|
550
|
-
enabled: !!email && email.includes('@'),
|
|
551
|
-
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
552
|
-
gcTime: 10 * 60 * 1000,
|
|
553
|
-
});
|
|
554
|
-
};
|
|
555
|
-
/**
|
|
556
|
-
* Custom hook for fetching Mautic forms
|
|
557
|
-
*/
|
|
558
|
-
const useMauticForms = () => {
|
|
559
|
-
return reactQuery.useQuery({
|
|
560
|
-
queryKey: ['mautic-forms'],
|
|
561
|
-
queryFn: () => {
|
|
562
|
-
const client = getMauticClient();
|
|
563
|
-
return client.getForms();
|
|
564
|
-
},
|
|
565
|
-
staleTime: 30 * 60 * 1000, // Forms don't change often
|
|
566
|
-
gcTime: 60 * 60 * 1000,
|
|
567
|
-
});
|
|
568
|
-
};
|
|
569
|
-
/**
|
|
570
|
-
* Custom hook for fetching a specific Mautic form
|
|
571
|
-
*/
|
|
572
|
-
const useMauticForm = (formId) => {
|
|
573
|
-
return reactQuery.useQuery({
|
|
574
|
-
queryKey: ['mautic-form', formId],
|
|
575
|
-
queryFn: () => {
|
|
576
|
-
const client = getMauticClient();
|
|
577
|
-
return client.getForm(formId);
|
|
578
|
-
},
|
|
579
|
-
enabled: !!formId,
|
|
580
|
-
staleTime: 30 * 60 * 1000,
|
|
581
|
-
gcTime: 60 * 60 * 1000,
|
|
582
|
-
});
|
|
583
|
-
};
|
|
584
|
-
/**
|
|
585
|
-
* Custom hook for tracking Mautic events
|
|
586
|
-
*/
|
|
587
|
-
const useMauticEventTracking = () => {
|
|
588
|
-
return reactQuery.useMutation({
|
|
589
|
-
mutationFn: ({ email, eventName, eventData }) => {
|
|
590
|
-
const client = getMauticClient();
|
|
591
|
-
return client.trackEvent(eventName, {
|
|
592
|
-
email,
|
|
593
|
-
...(eventData || {})
|
|
594
|
-
});
|
|
595
|
-
},
|
|
596
|
-
onSuccess: (data, variables) => {
|
|
597
|
-
if (undefined?.DEV) {
|
|
598
|
-
console.log('Mautic event tracked successfully:', variables.eventName);
|
|
599
|
-
}
|
|
600
|
-
},
|
|
601
|
-
onError: (error, variables) => {
|
|
602
|
-
console.error('Failed to track Mautic event:', variables.eventName, error);
|
|
603
|
-
},
|
|
604
|
-
});
|
|
605
|
-
};
|
|
606
|
-
/**
|
|
607
|
-
* Custom hook for adding tags to contacts
|
|
608
|
-
*/
|
|
609
|
-
const useMauticAddTags = () => {
|
|
610
|
-
const queryClient = reactQuery.useQueryClient();
|
|
611
|
-
return reactQuery.useMutation({
|
|
612
|
-
mutationFn: ({ contactId, tags }) => {
|
|
613
|
-
const client = getMauticClient();
|
|
614
|
-
return client.addTagToContact(contactId, tags);
|
|
615
|
-
},
|
|
616
|
-
onSuccess: (data, variables) => {
|
|
617
|
-
if (undefined?.DEV) {
|
|
618
|
-
console.log('Tags added to Mautic contact:', variables.tags);
|
|
619
|
-
}
|
|
620
|
-
// Invalidate contact queries
|
|
621
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
622
|
-
},
|
|
623
|
-
onError: (error) => {
|
|
624
|
-
console.error('Failed to add tags to Mautic contact:', error);
|
|
625
|
-
},
|
|
626
|
-
});
|
|
627
|
-
};
|
|
628
|
-
/**
|
|
629
|
-
* Custom hook for removing tags from contacts
|
|
630
|
-
*/
|
|
631
|
-
const useMauticRemoveTags = () => {
|
|
632
|
-
const queryClient = reactQuery.useQueryClient();
|
|
633
|
-
return reactQuery.useMutation({
|
|
634
|
-
mutationFn: ({ contactId, tags }) => {
|
|
635
|
-
const client = getMauticClient();
|
|
636
|
-
return client.removeTagFromContact(contactId, tags);
|
|
637
|
-
},
|
|
638
|
-
onSuccess: (data, variables) => {
|
|
639
|
-
if (undefined?.DEV) {
|
|
640
|
-
console.log('Tags removed from Mautic contact:', variables.tags);
|
|
641
|
-
}
|
|
642
|
-
// Invalidate contact queries
|
|
643
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
644
|
-
},
|
|
645
|
-
onError: (error) => {
|
|
646
|
-
console.error('Failed to remove tags from Mautic contact:', error);
|
|
647
|
-
},
|
|
648
|
-
});
|
|
649
|
-
};
|
|
650
|
-
/**
|
|
651
|
-
* Custom hook for getting contact tags
|
|
652
|
-
*/
|
|
653
|
-
const useMauticContactTags = (contactId) => {
|
|
654
|
-
return reactQuery.useQuery({
|
|
655
|
-
queryKey: ['mautic-contact-tags', contactId],
|
|
656
|
-
queryFn: () => {
|
|
657
|
-
const client = getMauticClient();
|
|
658
|
-
return client.getContactTags(contactId);
|
|
659
|
-
},
|
|
660
|
-
enabled: !!contactId,
|
|
661
|
-
staleTime: 5 * 60 * 1000,
|
|
662
|
-
gcTime: 10 * 60 * 1000,
|
|
663
|
-
});
|
|
664
|
-
};
|
|
665
|
-
/**
|
|
666
|
-
* Custom hook for getting all tags
|
|
667
|
-
*/
|
|
668
|
-
const useMauticTags = () => {
|
|
669
|
-
return reactQuery.useQuery({
|
|
670
|
-
queryKey: ['mautic-tags'],
|
|
671
|
-
queryFn: () => {
|
|
672
|
-
const client = getMauticClient();
|
|
673
|
-
return client.getTags();
|
|
674
|
-
},
|
|
675
|
-
staleTime: 30 * 60 * 1000,
|
|
676
|
-
gcTime: 60 * 60 * 1000,
|
|
677
|
-
});
|
|
678
|
-
};
|
|
679
|
-
/**
|
|
680
|
-
* Custom hook for getting segments
|
|
681
|
-
*/
|
|
682
|
-
const useMauticSegments = () => {
|
|
683
|
-
return reactQuery.useQuery({
|
|
684
|
-
queryKey: ['mautic-segments'],
|
|
685
|
-
queryFn: () => {
|
|
686
|
-
const client = getMauticClient();
|
|
687
|
-
return client.getSegments();
|
|
688
|
-
},
|
|
689
|
-
staleTime: 30 * 60 * 1000,
|
|
690
|
-
gcTime: 60 * 60 * 1000,
|
|
691
|
-
});
|
|
692
|
-
};
|
|
693
|
-
/**
|
|
694
|
-
* Custom hook for adding contact to segment
|
|
695
|
-
*/
|
|
696
|
-
const useMauticAddToSegment = () => {
|
|
697
|
-
const queryClient = reactQuery.useQueryClient();
|
|
698
|
-
return reactQuery.useMutation({
|
|
699
|
-
mutationFn: ({ contactId, segmentId }) => {
|
|
700
|
-
const client = getMauticClient();
|
|
701
|
-
return client.addContactToSegment(contactId, segmentId);
|
|
702
|
-
},
|
|
703
|
-
onSuccess: (data, variables) => {
|
|
704
|
-
if (undefined?.DEV) {
|
|
705
|
-
console.log('Contact added to segment:', variables.segmentId);
|
|
706
|
-
}
|
|
707
|
-
// Invalidate contact queries
|
|
708
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
709
|
-
},
|
|
710
|
-
onError: (error) => {
|
|
711
|
-
console.error('Failed to add contact to segment:', error);
|
|
712
|
-
},
|
|
713
|
-
});
|
|
714
|
-
};
|
|
715
|
-
/**
|
|
716
|
-
* Custom hook for removing contact from segment
|
|
717
|
-
*/
|
|
718
|
-
const useMauticRemoveFromSegment = () => {
|
|
719
|
-
const queryClient = reactQuery.useQueryClient();
|
|
720
|
-
return reactQuery.useMutation({
|
|
721
|
-
mutationFn: ({ contactId, segmentId }) => {
|
|
722
|
-
const client = getMauticClient();
|
|
723
|
-
return client.removeContactFromSegment(contactId, segmentId);
|
|
724
|
-
},
|
|
725
|
-
onSuccess: (data, variables) => {
|
|
726
|
-
if (undefined?.DEV) {
|
|
727
|
-
console.log('Contact removed from segment:', variables.segmentId);
|
|
728
|
-
}
|
|
729
|
-
// Invalidate contact queries
|
|
730
|
-
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
731
|
-
},
|
|
732
|
-
onError: (error) => {
|
|
733
|
-
console.error('Failed to remove contact from segment:', error);
|
|
734
|
-
},
|
|
735
|
-
});
|
|
736
|
-
};
|
|
737
|
-
|
|
738
330
|
/**
|
|
739
331
|
* @license GPL-3.0-or-later
|
|
740
332
|
*
|
|
@@ -836,9 +428,19 @@ class MauticService {
|
|
|
836
428
|
return this.client.trackEvent(eventName, eventData);
|
|
837
429
|
}
|
|
838
430
|
}
|
|
839
|
-
// Export configured instance
|
|
840
|
-
|
|
841
|
-
|
|
431
|
+
// Export configured instance (guarded to avoid throwing at import time)
|
|
432
|
+
exports.mauticService = null;
|
|
433
|
+
try {
|
|
434
|
+
exports.mauticService = new MauticService();
|
|
435
|
+
// Initialize the default client for hooks
|
|
436
|
+
// Lazy import to avoid circular/side-effect issues in some environments
|
|
437
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
438
|
+
const { setMauticClient } = require('../react/hooks/useMautic');
|
|
439
|
+
setMauticClient(exports.mauticService['client']);
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
// Silently skip initialization when environment is not configured
|
|
443
|
+
}
|
|
842
444
|
|
|
843
445
|
/**
|
|
844
446
|
* @license GPL-3.0-or-later
|
|
@@ -858,17 +460,54 @@ setMauticClient(mauticService['client']);
|
|
|
858
460
|
*/
|
|
859
461
|
class MauticGenerator {
|
|
860
462
|
constructor(config) {
|
|
463
|
+
this.cachedToken = null;
|
|
861
464
|
this.config = config;
|
|
465
|
+
// Generator uses direct OAuth2 calls, not the client
|
|
466
|
+
// Client is only used for proxy mode (cloudflare_proxy)
|
|
862
467
|
this.client = new MauticClient({
|
|
863
|
-
authMode: config.authMode,
|
|
864
468
|
apiUrl: config.apiUrl,
|
|
865
|
-
|
|
866
|
-
appId: config.appId,
|
|
867
|
-
workerSecret: config.workerSecret,
|
|
469
|
+
proxyEndpoint: config.cloudflareWorkerUrl,
|
|
868
470
|
timeout: config.timeout,
|
|
869
471
|
retries: config.retries,
|
|
870
472
|
});
|
|
871
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Get OAuth2 token for direct mode API calls
|
|
476
|
+
*/
|
|
477
|
+
async getOAuth2Token() {
|
|
478
|
+
if (this.cachedToken && this.cachedToken.expires_at > Date.now() + 300000) {
|
|
479
|
+
console.log('🔑 Using cached OAuth2 token');
|
|
480
|
+
return this.cachedToken.access_token;
|
|
481
|
+
}
|
|
482
|
+
if (!this.config.clientId || !this.config.clientSecret) {
|
|
483
|
+
throw new Error('OAuth2 credentials (clientId, clientSecret) required for direct mode');
|
|
484
|
+
}
|
|
485
|
+
console.log('🔑 Fetching new OAuth2 token...');
|
|
486
|
+
const tokenUrl = `${this.config.apiUrl}/oauth/v2/token`;
|
|
487
|
+
const body = new URLSearchParams({
|
|
488
|
+
grant_type: 'client_credentials',
|
|
489
|
+
client_id: this.config.clientId,
|
|
490
|
+
client_secret: this.config.clientSecret,
|
|
491
|
+
});
|
|
492
|
+
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
|
493
|
+
if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
|
|
494
|
+
headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
|
|
495
|
+
headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
|
|
496
|
+
console.log('🔐 Added CF Access headers to OAuth2 token request');
|
|
497
|
+
}
|
|
498
|
+
const resp = await fetch(tokenUrl, { method: 'POST', headers, body: body.toString() });
|
|
499
|
+
if (!resp.ok) {
|
|
500
|
+
const errText = await resp.text();
|
|
501
|
+
throw new Error(`OAuth2 token failed: ${resp.status} ${errText}`);
|
|
502
|
+
}
|
|
503
|
+
const data = await resp.json();
|
|
504
|
+
this.cachedToken = {
|
|
505
|
+
access_token: data.access_token,
|
|
506
|
+
expires_at: Date.now() + (data.expires_in * 1000),
|
|
507
|
+
};
|
|
508
|
+
console.log('✅ OAuth2 token cached');
|
|
509
|
+
return this.cachedToken.access_token;
|
|
510
|
+
}
|
|
872
511
|
/**
|
|
873
512
|
* Generate static data for Mautic forms
|
|
874
513
|
*/
|
|
@@ -902,7 +541,7 @@ class MauticGenerator {
|
|
|
902
541
|
// Include other important form properties
|
|
903
542
|
formType: formDetails.formType || 'standalone',
|
|
904
543
|
// Extract fields with validation and properties
|
|
905
|
-
fields: (formDetails.fields || []).map(field => ({
|
|
544
|
+
fields: (formDetails.fields || []).map((field) => ({
|
|
906
545
|
id: field.id,
|
|
907
546
|
label: field.label,
|
|
908
547
|
alias: field.alias,
|
|
@@ -921,7 +560,7 @@ class MauticGenerator {
|
|
|
921
560
|
}
|
|
922
561
|
})),
|
|
923
562
|
// Extract actions
|
|
924
|
-
actions: (formDetails.actions || []).map(action => ({
|
|
563
|
+
actions: (formDetails.actions || []).map((action) => ({
|
|
925
564
|
id: action.id,
|
|
926
565
|
name: action.name,
|
|
927
566
|
type: action.type,
|
|
@@ -945,10 +584,10 @@ class MauticGenerator {
|
|
|
945
584
|
const staticData = {
|
|
946
585
|
generated_at: new Date().toISOString(),
|
|
947
586
|
total_forms: forms.length,
|
|
948
|
-
forms: forms.map(form => ({
|
|
587
|
+
forms: forms.map((form) => ({
|
|
949
588
|
id: parseInt(form.id),
|
|
950
589
|
name: form.name,
|
|
951
|
-
alias: form.alias
|
|
590
|
+
alias: String(form.alias ?? (form.name || '').toLowerCase().replace(/\s+/g, '-')),
|
|
952
591
|
description: form.description,
|
|
953
592
|
isPublished: form.isPublished,
|
|
954
593
|
fields: form.fields,
|
|
@@ -995,49 +634,44 @@ class MauticGenerator {
|
|
|
995
634
|
* Fetch list of forms from Mautic
|
|
996
635
|
*/
|
|
997
636
|
async fetchMauticFormsList() {
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
const url = `${baseUrl}?endpoint=/forms`;
|
|
1002
|
-
const headers = {
|
|
1003
|
-
'Content-Type': 'application/json',
|
|
1004
|
-
};
|
|
1005
|
-
if (this.config.appId) {
|
|
1006
|
-
headers['x-app-id'] = this.config.appId;
|
|
1007
|
-
}
|
|
1008
|
-
if (this.config.workerSecret) {
|
|
1009
|
-
headers['x-app-secret'] = this.config.workerSecret;
|
|
637
|
+
if (this.config.authMode === 'cloudflare_proxy') {
|
|
638
|
+
const forms = await this.client.getForms();
|
|
639
|
+
return Array.isArray(forms) ? forms : [];
|
|
1010
640
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
641
|
+
// Direct mode with OAuth2
|
|
642
|
+
const token = await this.getOAuth2Token();
|
|
643
|
+
const url = `${this.config.apiUrl}/api/forms`;
|
|
644
|
+
const headers = { 'Authorization': `Bearer ${token}` };
|
|
645
|
+
if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
|
|
646
|
+
headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
|
|
647
|
+
headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
|
|
1014
648
|
}
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
649
|
+
const resp = await fetch(url, { headers });
|
|
650
|
+
if (!resp.ok)
|
|
651
|
+
throw new Error(`Failed to fetch forms: ${resp.status}`);
|
|
652
|
+
const data = await resp.json();
|
|
653
|
+
return Array.isArray(data.forms) ? data.forms : [];
|
|
1018
654
|
}
|
|
1019
655
|
/**
|
|
1020
656
|
* Fetch individual form details from Mautic
|
|
1021
657
|
*/
|
|
1022
658
|
async fetchMauticForm(formId) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
throw new Error('VITE_MAUTIC_PROXY_URL not configured');
|
|
1026
|
-
const url = `${baseUrl}?endpoint=/forms/${formId}`;
|
|
1027
|
-
const headers = {
|
|
1028
|
-
'Content-Type': 'application/json',
|
|
1029
|
-
};
|
|
1030
|
-
if (this.config.appId) {
|
|
1031
|
-
headers['x-app-id'] = this.config.appId;
|
|
1032
|
-
}
|
|
1033
|
-
if (this.config.workerSecret) {
|
|
1034
|
-
headers['x-app-secret'] = this.config.workerSecret;
|
|
659
|
+
if (this.config.authMode === 'cloudflare_proxy') {
|
|
660
|
+
return this.client.getForm(parseInt(formId, 10));
|
|
1035
661
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
662
|
+
// Direct mode with OAuth2
|
|
663
|
+
const token = await this.getOAuth2Token();
|
|
664
|
+
const url = `${this.config.apiUrl}/api/forms/${formId}`;
|
|
665
|
+
const headers = { 'Authorization': `Bearer ${token}` };
|
|
666
|
+
if (this.config.cfAccessClientId && this.config.cfAccessClientSecret) {
|
|
667
|
+
headers['CF-Access-Client-Id'] = this.config.cfAccessClientId;
|
|
668
|
+
headers['CF-Access-Client-Secret'] = this.config.cfAccessClientSecret;
|
|
1039
669
|
}
|
|
1040
|
-
|
|
670
|
+
const resp = await fetch(url, { headers });
|
|
671
|
+
if (!resp.ok)
|
|
672
|
+
throw new Error(`Failed to fetch form ${formId}: ${resp.status}`);
|
|
673
|
+
const data = await resp.json();
|
|
674
|
+
return data.form || data;
|
|
1041
675
|
}
|
|
1042
676
|
}
|
|
1043
677
|
/**
|
|
@@ -2403,6 +2037,315 @@ if (process.env.NODE_ENV === 'production') {
|
|
|
2403
2037
|
|
|
2404
2038
|
var jsxRuntimeExports = jsxRuntime.exports;
|
|
2405
2039
|
|
|
2040
|
+
/**
|
|
2041
|
+
* @license GPL-3.0-or-later
|
|
2042
|
+
*
|
|
2043
|
+
* This file is part of the MarVAlt Open SDK.
|
|
2044
|
+
* Copyright (c) 2025 Vibune Pty Ltd.
|
|
2045
|
+
*
|
|
2046
|
+
* This program is free software: you can redistribute it and/or modify
|
|
2047
|
+
* it under the terms of the GNU General Public License as published by
|
|
2048
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
2049
|
+
* (at your option) any later version.
|
|
2050
|
+
*
|
|
2051
|
+
* This program is distributed in the hope that it will be useful,
|
|
2052
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
2053
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
2054
|
+
* See the GNU General Public License for more details.
|
|
2055
|
+
*/
|
|
2056
|
+
// Create a default client instance (will be overridden by provider)
|
|
2057
|
+
let defaultClient = null;
|
|
2058
|
+
/**
|
|
2059
|
+
* Set the default Mautic client for hooks
|
|
2060
|
+
*/
|
|
2061
|
+
const setMauticClient = (client) => {
|
|
2062
|
+
defaultClient = client;
|
|
2063
|
+
};
|
|
2064
|
+
/**
|
|
2065
|
+
* Get the current Mautic client
|
|
2066
|
+
*/
|
|
2067
|
+
const getMauticClient = () => {
|
|
2068
|
+
if (!defaultClient) {
|
|
2069
|
+
// Lazy initialize the client from environment if not set by provider/service
|
|
2070
|
+
const config = createMauticConfig();
|
|
2071
|
+
defaultClient = new MauticClient(config);
|
|
2072
|
+
}
|
|
2073
|
+
return defaultClient;
|
|
2074
|
+
};
|
|
2075
|
+
/**
|
|
2076
|
+
* Custom hook for submitting forms to Mautic
|
|
2077
|
+
*/
|
|
2078
|
+
const useMauticFormSubmission = () => {
|
|
2079
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2080
|
+
return reactQuery.useMutation({
|
|
2081
|
+
mutationFn: (submission) => {
|
|
2082
|
+
const client = getMauticClient();
|
|
2083
|
+
return client.submitForm(submission.formId, submission);
|
|
2084
|
+
},
|
|
2085
|
+
onSuccess: (data, variables) => {
|
|
2086
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2087
|
+
console.log('Mautic form submission successful:', data);
|
|
2088
|
+
}
|
|
2089
|
+
// Invalidate related queries
|
|
2090
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
|
|
2091
|
+
// Track the submission event
|
|
2092
|
+
if (variables.contact?.email) {
|
|
2093
|
+
const client = getMauticClient();
|
|
2094
|
+
client.trackEvent('form_submission', {
|
|
2095
|
+
formId: variables.formId,
|
|
2096
|
+
email: variables.contact.email
|
|
2097
|
+
}).catch(console.error);
|
|
2098
|
+
}
|
|
2099
|
+
},
|
|
2100
|
+
onError: (error) => {
|
|
2101
|
+
console.error('Mautic form submission failed:', error);
|
|
2102
|
+
},
|
|
2103
|
+
});
|
|
2104
|
+
};
|
|
2105
|
+
/**
|
|
2106
|
+
* Custom hook for creating Mautic contacts
|
|
2107
|
+
*/
|
|
2108
|
+
const useMauticCreateContact = () => {
|
|
2109
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2110
|
+
return reactQuery.useMutation({
|
|
2111
|
+
mutationFn: (contact) => {
|
|
2112
|
+
const client = getMauticClient();
|
|
2113
|
+
return client.createContact(contact);
|
|
2114
|
+
},
|
|
2115
|
+
onSuccess: (data, variables) => {
|
|
2116
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2117
|
+
console.log('Mautic contact created:', data);
|
|
2118
|
+
}
|
|
2119
|
+
// Invalidate contacts queries
|
|
2120
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
|
|
2121
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.email] });
|
|
2122
|
+
},
|
|
2123
|
+
onError: (error) => {
|
|
2124
|
+
console.error('Failed to create Mautic contact:', error);
|
|
2125
|
+
},
|
|
2126
|
+
});
|
|
2127
|
+
};
|
|
2128
|
+
/**
|
|
2129
|
+
* Custom hook for updating Mautic contacts
|
|
2130
|
+
*/
|
|
2131
|
+
const useMauticUpdateContact = () => {
|
|
2132
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2133
|
+
return reactQuery.useMutation({
|
|
2134
|
+
mutationFn: ({ contactId, contact }) => {
|
|
2135
|
+
const client = getMauticClient();
|
|
2136
|
+
return client.updateContact(contactId, contact);
|
|
2137
|
+
},
|
|
2138
|
+
onSuccess: (data, variables) => {
|
|
2139
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2140
|
+
console.log('Mautic contact updated:', data);
|
|
2141
|
+
}
|
|
2142
|
+
// Invalidate related queries
|
|
2143
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contacts'] });
|
|
2144
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
2145
|
+
},
|
|
2146
|
+
onError: (error) => {
|
|
2147
|
+
console.error('Failed to update Mautic contact:', error);
|
|
2148
|
+
},
|
|
2149
|
+
});
|
|
2150
|
+
};
|
|
2151
|
+
/**
|
|
2152
|
+
* Custom hook for fetching Mautic contact by email
|
|
2153
|
+
*/
|
|
2154
|
+
const useMauticContactByEmail = (email) => {
|
|
2155
|
+
return reactQuery.useQuery({
|
|
2156
|
+
queryKey: ['mautic-contact', email],
|
|
2157
|
+
queryFn: () => {
|
|
2158
|
+
const client = getMauticClient();
|
|
2159
|
+
return client.getContactByEmail(email);
|
|
2160
|
+
},
|
|
2161
|
+
enabled: !!email && email.includes('@'),
|
|
2162
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
2163
|
+
gcTime: 10 * 60 * 1000,
|
|
2164
|
+
});
|
|
2165
|
+
};
|
|
2166
|
+
/**
|
|
2167
|
+
* Custom hook for fetching Mautic forms
|
|
2168
|
+
*/
|
|
2169
|
+
const useMauticForms = () => {
|
|
2170
|
+
return reactQuery.useQuery({
|
|
2171
|
+
queryKey: ['mautic-forms'],
|
|
2172
|
+
queryFn: () => {
|
|
2173
|
+
const client = getMauticClient();
|
|
2174
|
+
return client.getForms();
|
|
2175
|
+
},
|
|
2176
|
+
staleTime: 30 * 60 * 1000, // Forms don't change often
|
|
2177
|
+
gcTime: 60 * 60 * 1000,
|
|
2178
|
+
});
|
|
2179
|
+
};
|
|
2180
|
+
/**
|
|
2181
|
+
* Custom hook for fetching a specific Mautic form
|
|
2182
|
+
*/
|
|
2183
|
+
const useMauticForm = (formId) => {
|
|
2184
|
+
return reactQuery.useQuery({
|
|
2185
|
+
queryKey: ['mautic-form', formId],
|
|
2186
|
+
queryFn: () => {
|
|
2187
|
+
const client = getMauticClient();
|
|
2188
|
+
return client.getForm(formId);
|
|
2189
|
+
},
|
|
2190
|
+
enabled: !!formId,
|
|
2191
|
+
staleTime: 30 * 60 * 1000,
|
|
2192
|
+
gcTime: 60 * 60 * 1000,
|
|
2193
|
+
});
|
|
2194
|
+
};
|
|
2195
|
+
/**
|
|
2196
|
+
* Custom hook for tracking Mautic events
|
|
2197
|
+
*/
|
|
2198
|
+
const useMauticEventTracking = () => {
|
|
2199
|
+
return reactQuery.useMutation({
|
|
2200
|
+
mutationFn: ({ email, eventName, eventData }) => {
|
|
2201
|
+
const client = getMauticClient();
|
|
2202
|
+
return client.trackEvent(eventName, {
|
|
2203
|
+
email,
|
|
2204
|
+
...(eventData || {})
|
|
2205
|
+
});
|
|
2206
|
+
},
|
|
2207
|
+
onSuccess: (data, variables) => {
|
|
2208
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2209
|
+
console.log('Mautic event tracked successfully:', variables.eventName);
|
|
2210
|
+
}
|
|
2211
|
+
},
|
|
2212
|
+
onError: (error, variables) => {
|
|
2213
|
+
console.error('Failed to track Mautic event:', variables.eventName, error);
|
|
2214
|
+
},
|
|
2215
|
+
});
|
|
2216
|
+
};
|
|
2217
|
+
/**
|
|
2218
|
+
* Custom hook for adding tags to contacts
|
|
2219
|
+
*/
|
|
2220
|
+
const useMauticAddTags = () => {
|
|
2221
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2222
|
+
return reactQuery.useMutation({
|
|
2223
|
+
mutationFn: ({ contactId, tags }) => {
|
|
2224
|
+
const client = getMauticClient();
|
|
2225
|
+
return client.addTagToContact(contactId, tags);
|
|
2226
|
+
},
|
|
2227
|
+
onSuccess: (data, variables) => {
|
|
2228
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2229
|
+
console.log('Tags added to Mautic contact:', variables.tags);
|
|
2230
|
+
}
|
|
2231
|
+
// Invalidate contact queries
|
|
2232
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
2233
|
+
},
|
|
2234
|
+
onError: (error) => {
|
|
2235
|
+
console.error('Failed to add tags to Mautic contact:', error);
|
|
2236
|
+
},
|
|
2237
|
+
});
|
|
2238
|
+
};
|
|
2239
|
+
/**
|
|
2240
|
+
* Custom hook for removing tags from contacts
|
|
2241
|
+
*/
|
|
2242
|
+
const useMauticRemoveTags = () => {
|
|
2243
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2244
|
+
return reactQuery.useMutation({
|
|
2245
|
+
mutationFn: ({ contactId, tags }) => {
|
|
2246
|
+
const client = getMauticClient();
|
|
2247
|
+
return client.removeTagFromContact(contactId, tags);
|
|
2248
|
+
},
|
|
2249
|
+
onSuccess: (data, variables) => {
|
|
2250
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2251
|
+
console.log('Tags removed from Mautic contact:', variables.tags);
|
|
2252
|
+
}
|
|
2253
|
+
// Invalidate contact queries
|
|
2254
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
2255
|
+
},
|
|
2256
|
+
onError: (error) => {
|
|
2257
|
+
console.error('Failed to remove tags from Mautic contact:', error);
|
|
2258
|
+
},
|
|
2259
|
+
});
|
|
2260
|
+
};
|
|
2261
|
+
/**
|
|
2262
|
+
* Custom hook for getting contact tags
|
|
2263
|
+
*/
|
|
2264
|
+
const useMauticContactTags = (contactId) => {
|
|
2265
|
+
return reactQuery.useQuery({
|
|
2266
|
+
queryKey: ['mautic-contact-tags', contactId],
|
|
2267
|
+
queryFn: () => {
|
|
2268
|
+
const client = getMauticClient();
|
|
2269
|
+
return client.getContactTags(contactId);
|
|
2270
|
+
},
|
|
2271
|
+
enabled: !!contactId,
|
|
2272
|
+
staleTime: 5 * 60 * 1000,
|
|
2273
|
+
gcTime: 10 * 60 * 1000,
|
|
2274
|
+
});
|
|
2275
|
+
};
|
|
2276
|
+
/**
|
|
2277
|
+
* Custom hook for getting all tags
|
|
2278
|
+
*/
|
|
2279
|
+
const useMauticTags = () => {
|
|
2280
|
+
return reactQuery.useQuery({
|
|
2281
|
+
queryKey: ['mautic-tags'],
|
|
2282
|
+
queryFn: () => {
|
|
2283
|
+
const client = getMauticClient();
|
|
2284
|
+
return client.getTags();
|
|
2285
|
+
},
|
|
2286
|
+
staleTime: 30 * 60 * 1000,
|
|
2287
|
+
gcTime: 60 * 60 * 1000,
|
|
2288
|
+
});
|
|
2289
|
+
};
|
|
2290
|
+
/**
|
|
2291
|
+
* Custom hook for getting segments
|
|
2292
|
+
*/
|
|
2293
|
+
const useMauticSegments = () => {
|
|
2294
|
+
return reactQuery.useQuery({
|
|
2295
|
+
queryKey: ['mautic-segments'],
|
|
2296
|
+
queryFn: () => {
|
|
2297
|
+
const client = getMauticClient();
|
|
2298
|
+
return client.getSegments();
|
|
2299
|
+
},
|
|
2300
|
+
staleTime: 30 * 60 * 1000,
|
|
2301
|
+
gcTime: 60 * 60 * 1000,
|
|
2302
|
+
});
|
|
2303
|
+
};
|
|
2304
|
+
/**
|
|
2305
|
+
* Custom hook for adding contact to segment
|
|
2306
|
+
*/
|
|
2307
|
+
const useMauticAddToSegment = () => {
|
|
2308
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2309
|
+
return reactQuery.useMutation({
|
|
2310
|
+
mutationFn: ({ contactId, segmentId }) => {
|
|
2311
|
+
const client = getMauticClient();
|
|
2312
|
+
return client.addContactToSegment(contactId, segmentId);
|
|
2313
|
+
},
|
|
2314
|
+
onSuccess: (data, variables) => {
|
|
2315
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2316
|
+
console.log('Contact added to segment:', variables.segmentId);
|
|
2317
|
+
}
|
|
2318
|
+
// Invalidate contact queries
|
|
2319
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
2320
|
+
},
|
|
2321
|
+
onError: (error) => {
|
|
2322
|
+
console.error('Failed to add contact to segment:', error);
|
|
2323
|
+
},
|
|
2324
|
+
});
|
|
2325
|
+
};
|
|
2326
|
+
/**
|
|
2327
|
+
* Custom hook for removing contact from segment
|
|
2328
|
+
*/
|
|
2329
|
+
const useMauticRemoveFromSegment = () => {
|
|
2330
|
+
const queryClient = reactQuery.useQueryClient();
|
|
2331
|
+
return reactQuery.useMutation({
|
|
2332
|
+
mutationFn: ({ contactId, segmentId }) => {
|
|
2333
|
+
const client = getMauticClient();
|
|
2334
|
+
return client.removeContactFromSegment(contactId, segmentId);
|
|
2335
|
+
},
|
|
2336
|
+
onSuccess: (data, variables) => {
|
|
2337
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2338
|
+
console.log('Contact removed from segment:', variables.segmentId);
|
|
2339
|
+
}
|
|
2340
|
+
// Invalidate contact queries
|
|
2341
|
+
queryClient.invalidateQueries({ queryKey: ['mautic-contact', variables.contactId] });
|
|
2342
|
+
},
|
|
2343
|
+
onError: (error) => {
|
|
2344
|
+
console.error('Failed to remove contact from segment:', error);
|
|
2345
|
+
},
|
|
2346
|
+
});
|
|
2347
|
+
};
|
|
2348
|
+
|
|
2406
2349
|
const MauticForm = ({ formId, title, description, className = '', form, onSubmit, onSuccess, onError, }) => {
|
|
2407
2350
|
const [formData, setFormData] = React.useState({});
|
|
2408
2351
|
const [errors, setErrors] = React.useState({});
|
|
@@ -2575,21 +2518,19 @@ const MauticForm = ({ formId, title, description, className = '', form, onSubmit
|
|
|
2575
2518
|
.map((field) => (jsxRuntimeExports.jsxs("div", { className: `form-field form-field-${field.type}`, children: [field.showLabel !== false && (jsxRuntimeExports.jsxs("label", { htmlFor: field.alias, className: "field-label", children: [field.label, field.isRequired && jsxRuntimeExports.jsx("span", { className: "required", children: "*" })] })), renderField(field), field.properties?.helpText && (jsxRuntimeExports.jsx("p", { className: "field-help", children: field.properties.helpText })), errors[field.alias] && (jsxRuntimeExports.jsx("p", { className: "field-error", children: errors[field.alias] }))] }, field.id))) }), jsxRuntimeExports.jsx("div", { className: "form-actions", children: jsxRuntimeExports.jsx("button", { type: "submit", disabled: isSubmitting, className: "submit-button", children: isSubmitting ? 'Submitting...' : 'Submit' }) }), submitMutation.error && (jsxRuntimeExports.jsx("div", { className: "form-error", children: jsxRuntimeExports.jsx("p", { children: "There was an error submitting the form. Please try again." }) }))] }));
|
|
2576
2519
|
};
|
|
2577
2520
|
|
|
2578
|
-
const MauticTracking = ({ enabled, mauticUrl, proxyUrl,
|
|
2521
|
+
const MauticTracking = ({ enabled, mauticUrl, proxyUrl, children }) => {
|
|
2579
2522
|
React.useEffect(() => {
|
|
2580
2523
|
// Determine if tracking is enabled
|
|
2581
|
-
const isEnabled = enabled ??
|
|
2524
|
+
const isEnabled = enabled ?? !!proxyUrl;
|
|
2582
2525
|
const trackingProxyUrl = proxyUrl;
|
|
2583
2526
|
// Debug logging to understand configuration
|
|
2584
|
-
if (
|
|
2527
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2585
2528
|
console.log('🔍 Mautic Tracking Debug:', {
|
|
2586
2529
|
isEnabled,
|
|
2587
2530
|
mauticUrl,
|
|
2588
2531
|
trackingProxyUrl,
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
isDev: undefined?.DEV,
|
|
2592
|
-
isProd: undefined?.PROD
|
|
2532
|
+
isDev: true,
|
|
2533
|
+
isProd: false
|
|
2593
2534
|
});
|
|
2594
2535
|
}
|
|
2595
2536
|
// Check if tracking is enabled
|
|
@@ -2598,7 +2539,7 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
|
|
|
2598
2539
|
return;
|
|
2599
2540
|
}
|
|
2600
2541
|
// Check if we're in development mode - skip tracking in dev
|
|
2601
|
-
if (
|
|
2542
|
+
if (process.env.NODE_ENV === 'development') {
|
|
2602
2543
|
console.log('Mautic tracking disabled in development mode');
|
|
2603
2544
|
return;
|
|
2604
2545
|
}
|
|
@@ -2609,6 +2550,8 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
|
|
|
2609
2550
|
}
|
|
2610
2551
|
console.log('✅ Mautic tracking enabled for production');
|
|
2611
2552
|
// Initialize Mautic tracking with error handling
|
|
2553
|
+
// Capture original fetch for cleanup
|
|
2554
|
+
let originalFetch;
|
|
2612
2555
|
try {
|
|
2613
2556
|
// Pre-initialize tracking object and queue (standard Mautic snippet behavior)
|
|
2614
2557
|
window.MauticTrackingObject = 'mt';
|
|
@@ -2616,20 +2559,12 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
|
|
|
2616
2559
|
(window.mt.q = window.mt.q || []).push(arguments);
|
|
2617
2560
|
};
|
|
2618
2561
|
// Intercept Mautic tracking API calls to use our proxy (generic /mtc/ matcher)
|
|
2619
|
-
|
|
2562
|
+
originalFetch = window.fetch.bind(window);
|
|
2620
2563
|
const interceptFetch = function (url, options) {
|
|
2621
2564
|
if (typeof url === 'string' && /\/mtc\//.test(url)) {
|
|
2622
2565
|
try {
|
|
2623
2566
|
const proxyUrl = url.replace(/https?:\/\/[^/]+\/mtc\//, `${trackingProxyUrl}?endpoint=/mtc/`);
|
|
2624
|
-
const proxyOptions = {
|
|
2625
|
-
...options,
|
|
2626
|
-
headers: {
|
|
2627
|
-
...options?.headers,
|
|
2628
|
-
'Content-Type': 'application/json',
|
|
2629
|
-
'x-app-id': appId || '',
|
|
2630
|
-
...(workerSecret ? { 'x-app-secret': workerSecret } : {})
|
|
2631
|
-
}
|
|
2632
|
-
};
|
|
2567
|
+
const proxyOptions = { ...options };
|
|
2633
2568
|
return originalFetch(proxyUrl, proxyOptions);
|
|
2634
2569
|
}
|
|
2635
2570
|
catch (e) {
|
|
@@ -2639,16 +2574,9 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
|
|
|
2639
2574
|
return originalFetch(url, options);
|
|
2640
2575
|
};
|
|
2641
2576
|
window.fetch = interceptFetch;
|
|
2642
|
-
// Fetch the tracking script through the proxy
|
|
2577
|
+
// Fetch the tracking script through the proxy
|
|
2643
2578
|
const trackingScriptUrl = `${trackingProxyUrl}?endpoint=/mtc.js`;
|
|
2644
|
-
fetch(trackingScriptUrl, {
|
|
2645
|
-
method: 'GET',
|
|
2646
|
-
headers: {
|
|
2647
|
-
'Content-Type': 'application/javascript',
|
|
2648
|
-
'x-app-id': appId || '',
|
|
2649
|
-
...(workerSecret ? { 'x-app-secret': workerSecret } : {})
|
|
2650
|
-
}
|
|
2651
|
-
})
|
|
2579
|
+
fetch(trackingScriptUrl, { method: 'GET' })
|
|
2652
2580
|
.then(response => {
|
|
2653
2581
|
if (!response.ok) {
|
|
2654
2582
|
throw new Error(`Failed to load Mautic tracking script: ${response.status}`);
|
|
@@ -2676,12 +2604,11 @@ const MauticTracking = ({ enabled, mauticUrl, proxyUrl, appId, workerSecret, chi
|
|
|
2676
2604
|
}
|
|
2677
2605
|
// Cleanup function
|
|
2678
2606
|
return () => {
|
|
2679
|
-
|
|
2680
|
-
if (window.fetch !== originalFetch) {
|
|
2607
|
+
if (originalFetch && window.fetch !== originalFetch) {
|
|
2681
2608
|
window.fetch = originalFetch;
|
|
2682
2609
|
}
|
|
2683
2610
|
};
|
|
2684
|
-
}, [enabled, mauticUrl, proxyUrl
|
|
2611
|
+
}, [enabled, mauticUrl, proxyUrl]);
|
|
2685
2612
|
// Render children if provided, otherwise render nothing
|
|
2686
2613
|
return children ? jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: children }) : null;
|
|
2687
2614
|
};
|
|
@@ -3056,30 +2983,33 @@ const validateField = (field, value) => {
|
|
|
3056
2983
|
const validateCustomRule = (rule, value, field) => {
|
|
3057
2984
|
switch (rule) {
|
|
3058
2985
|
case 'min_length':
|
|
3059
|
-
if (typeof value === 'string' && value.length < (field.properties?.minLength
|
|
3060
|
-
return `Minimum length is ${field.properties?.minLength} characters`;
|
|
2986
|
+
if (typeof value === 'string' && value.length < Number(field.properties?.minLength ?? 0)) {
|
|
2987
|
+
return `Minimum length is ${Number(field.properties?.minLength ?? 0)} characters`;
|
|
3061
2988
|
}
|
|
3062
2989
|
break;
|
|
3063
2990
|
case 'max_length':
|
|
3064
|
-
if (typeof value === 'string' && value.length > (field.properties?.maxLength
|
|
3065
|
-
return `Maximum length is ${field.properties?.maxLength} characters`;
|
|
2991
|
+
if (typeof value === 'string' && value.length > Number(field.properties?.maxLength ?? 1000)) {
|
|
2992
|
+
return `Maximum length is ${Number(field.properties?.maxLength ?? 1000)} characters`;
|
|
3066
2993
|
}
|
|
3067
2994
|
break;
|
|
3068
2995
|
case 'min_value':
|
|
3069
|
-
if (typeof value === 'number' && value < (field.properties?.minValue
|
|
3070
|
-
return `Minimum value is ${field.properties?.minValue}`;
|
|
2996
|
+
if (typeof value === 'number' && value < Number(field.properties?.minValue ?? 0)) {
|
|
2997
|
+
return `Minimum value is ${Number(field.properties?.minValue ?? 0)}`;
|
|
3071
2998
|
}
|
|
3072
2999
|
break;
|
|
3073
3000
|
case 'max_value':
|
|
3074
|
-
if (typeof value === 'number' && value > (field.properties?.maxValue
|
|
3075
|
-
return `Maximum value is ${field.properties?.maxValue}`;
|
|
3001
|
+
if (typeof value === 'number' && value > Number(field.properties?.maxValue ?? 1000000)) {
|
|
3002
|
+
return `Maximum value is ${Number(field.properties?.maxValue ?? 1000000)}`;
|
|
3076
3003
|
}
|
|
3077
3004
|
break;
|
|
3078
3005
|
case 'pattern':
|
|
3079
|
-
|
|
3080
|
-
const
|
|
3081
|
-
if (
|
|
3082
|
-
|
|
3006
|
+
{
|
|
3007
|
+
const pattern = field.properties?.pattern;
|
|
3008
|
+
if (pattern) {
|
|
3009
|
+
const regex = new RegExp(String(pattern));
|
|
3010
|
+
if (!regex.test(value)) {
|
|
3011
|
+
return 'Invalid format';
|
|
3012
|
+
}
|
|
3083
3013
|
}
|
|
3084
3014
|
}
|
|
3085
3015
|
break;
|
|
@@ -3169,7 +3099,6 @@ exports.isValidEmail = isValidEmail;
|
|
|
3169
3099
|
exports.isValidPhone = isValidPhone;
|
|
3170
3100
|
exports.isValidUrl = isValidUrl;
|
|
3171
3101
|
exports.loadMauticData = loadMauticData;
|
|
3172
|
-
exports.mauticService = mauticService;
|
|
3173
3102
|
exports.mergeMauticConfig = mergeMauticConfig;
|
|
3174
3103
|
exports.sanitizeFormData = sanitizeFormData;
|
|
3175
3104
|
exports.searchForms = searchForms;
|