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