@clianta/sdk 1.0.0 → 1.1.1
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/README.md +352 -205
- package/dist/clianta.cjs.js +828 -48
- package/dist/clianta.cjs.js.map +1 -1
- package/dist/clianta.esm.js +828 -49
- package/dist/clianta.esm.js.map +1 -1
- package/dist/clianta.umd.js +828 -48
- package/dist/clianta.umd.js.map +1 -1
- package/dist/clianta.umd.min.js +2 -2
- package/dist/clianta.umd.min.js.map +1 -1
- package/dist/index.d.ts +149 -16
- package/dist/react.cjs.js +2661 -0
- package/dist/react.cjs.js.map +1 -0
- package/dist/react.d.ts +141 -0
- package/dist/react.esm.js +2657 -0
- package/dist/react.esm.js.map +1 -0
- package/package.json +18 -2
package/dist/clianta.esm.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Clianta SDK v1.
|
|
2
|
+
* Clianta SDK v1.1.1
|
|
3
3
|
* (c) 2026 Clianta
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Clianta SDK - Configuration
|
|
8
|
-
* @
|
|
8
|
+
* @see SDK_VERSION in core/config.ts
|
|
9
9
|
*/
|
|
10
10
|
/** SDK Version */
|
|
11
|
-
const SDK_VERSION = '1.
|
|
11
|
+
const SDK_VERSION = '1.1.0';
|
|
12
12
|
/** Default API endpoint based on environment */
|
|
13
13
|
const getDefaultApiEndpoint = () => {
|
|
14
14
|
if (typeof window === 'undefined')
|
|
@@ -28,10 +28,13 @@ const DEFAULT_PLUGINS = [
|
|
|
28
28
|
'engagement',
|
|
29
29
|
'downloads',
|
|
30
30
|
'exitIntent',
|
|
31
|
+
'popupForms',
|
|
31
32
|
];
|
|
32
33
|
/** Default configuration values */
|
|
33
34
|
const DEFAULT_CONFIG = {
|
|
35
|
+
projectId: '',
|
|
34
36
|
apiEndpoint: getDefaultApiEndpoint(),
|
|
37
|
+
authToken: '',
|
|
35
38
|
debug: false,
|
|
36
39
|
autoPageView: true,
|
|
37
40
|
plugins: DEFAULT_PLUGINS,
|
|
@@ -42,9 +45,11 @@ const DEFAULT_CONFIG = {
|
|
|
42
45
|
defaultConsent: { analytics: true, marketing: false, personalization: false },
|
|
43
46
|
waitForConsent: false,
|
|
44
47
|
storageKey: 'mb_consent',
|
|
48
|
+
anonymousMode: false,
|
|
45
49
|
},
|
|
46
50
|
cookieDomain: '',
|
|
47
51
|
useCookies: false,
|
|
52
|
+
cookielessMode: false,
|
|
48
53
|
};
|
|
49
54
|
/** Storage keys */
|
|
50
55
|
const STORAGE_KEYS = {
|
|
@@ -78,8 +83,8 @@ function mergeConfig(userConfig = {}) {
|
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
/**
|
|
81
|
-
*
|
|
82
|
-
* @
|
|
86
|
+
* Clianta SDK - Debug Logger
|
|
87
|
+
* @see SDK_VERSION in core/config.ts
|
|
83
88
|
*/
|
|
84
89
|
const LOG_PREFIX = '[Clianta]';
|
|
85
90
|
const LOG_STYLES = {
|
|
@@ -149,9 +154,9 @@ function createLogger(enabled = false) {
|
|
|
149
154
|
const logger = createLogger(false);
|
|
150
155
|
|
|
151
156
|
/**
|
|
152
|
-
*
|
|
157
|
+
* Clianta SDK - Transport Layer
|
|
153
158
|
* Handles sending events to the backend with retry logic
|
|
154
|
-
* @
|
|
159
|
+
* @see SDK_VERSION in core/config.ts
|
|
155
160
|
*/
|
|
156
161
|
const DEFAULT_TIMEOUT = 10000; // 10 seconds
|
|
157
162
|
const DEFAULT_MAX_RETRIES = 3;
|
|
@@ -275,8 +280,8 @@ class Transport {
|
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
/**
|
|
278
|
-
*
|
|
279
|
-
* @
|
|
283
|
+
* Clianta SDK - Utility Functions
|
|
284
|
+
* @see SDK_VERSION in core/config.ts
|
|
280
285
|
*/
|
|
281
286
|
// ============================================
|
|
282
287
|
// UUID GENERATION
|
|
@@ -562,9 +567,9 @@ function getDeviceInfo() {
|
|
|
562
567
|
}
|
|
563
568
|
|
|
564
569
|
/**
|
|
565
|
-
*
|
|
570
|
+
* Clianta SDK - Event Queue
|
|
566
571
|
* Handles batching and flushing of events
|
|
567
|
-
* @
|
|
572
|
+
* @see SDK_VERSION in core/config.ts
|
|
568
573
|
*/
|
|
569
574
|
const MAX_QUEUE_SIZE = 1000;
|
|
570
575
|
/**
|
|
@@ -739,8 +744,8 @@ class EventQueue {
|
|
|
739
744
|
}
|
|
740
745
|
|
|
741
746
|
/**
|
|
742
|
-
*
|
|
743
|
-
* @
|
|
747
|
+
* Clianta SDK - Plugin Base
|
|
748
|
+
* @see SDK_VERSION in core/config.ts
|
|
744
749
|
*/
|
|
745
750
|
/**
|
|
746
751
|
* Base class for plugins
|
|
@@ -763,8 +768,8 @@ class BasePlugin {
|
|
|
763
768
|
}
|
|
764
769
|
|
|
765
770
|
/**
|
|
766
|
-
*
|
|
767
|
-
* @
|
|
771
|
+
* Clianta SDK - Page View Plugin
|
|
772
|
+
* @see SDK_VERSION in core/config.ts
|
|
768
773
|
*/
|
|
769
774
|
/**
|
|
770
775
|
* Page View Plugin - Tracks page views
|
|
@@ -812,8 +817,8 @@ class PageViewPlugin extends BasePlugin {
|
|
|
812
817
|
}
|
|
813
818
|
|
|
814
819
|
/**
|
|
815
|
-
*
|
|
816
|
-
* @
|
|
820
|
+
* Clianta SDK - Scroll Depth Plugin
|
|
821
|
+
* @see SDK_VERSION in core/config.ts
|
|
817
822
|
*/
|
|
818
823
|
/**
|
|
819
824
|
* Scroll Depth Plugin - Tracks scroll milestones
|
|
@@ -880,8 +885,8 @@ class ScrollPlugin extends BasePlugin {
|
|
|
880
885
|
}
|
|
881
886
|
|
|
882
887
|
/**
|
|
883
|
-
*
|
|
884
|
-
* @
|
|
888
|
+
* Clianta SDK - Form Tracking Plugin
|
|
889
|
+
* @see SDK_VERSION in core/config.ts
|
|
885
890
|
*/
|
|
886
891
|
/**
|
|
887
892
|
* Form Tracking Plugin - Auto-tracks form views, interactions, and submissions
|
|
@@ -988,8 +993,8 @@ class FormsPlugin extends BasePlugin {
|
|
|
988
993
|
}
|
|
989
994
|
|
|
990
995
|
/**
|
|
991
|
-
*
|
|
992
|
-
* @
|
|
996
|
+
* Clianta SDK - Click Tracking Plugin
|
|
997
|
+
* @see SDK_VERSION in core/config.ts
|
|
993
998
|
*/
|
|
994
999
|
/**
|
|
995
1000
|
* Click Tracking Plugin - Tracks button and CTA clicks
|
|
@@ -1030,8 +1035,8 @@ class ClicksPlugin extends BasePlugin {
|
|
|
1030
1035
|
}
|
|
1031
1036
|
|
|
1032
1037
|
/**
|
|
1033
|
-
*
|
|
1034
|
-
* @
|
|
1038
|
+
* Clianta SDK - Engagement Plugin
|
|
1039
|
+
* @see SDK_VERSION in core/config.ts
|
|
1035
1040
|
*/
|
|
1036
1041
|
/**
|
|
1037
1042
|
* Engagement Plugin - Tracks user engagement and time on page
|
|
@@ -1112,8 +1117,8 @@ class EngagementPlugin extends BasePlugin {
|
|
|
1112
1117
|
}
|
|
1113
1118
|
|
|
1114
1119
|
/**
|
|
1115
|
-
*
|
|
1116
|
-
* @
|
|
1120
|
+
* Clianta SDK - Downloads Plugin
|
|
1121
|
+
* @see SDK_VERSION in core/config.ts
|
|
1117
1122
|
*/
|
|
1118
1123
|
/**
|
|
1119
1124
|
* Downloads Plugin - Tracks file downloads
|
|
@@ -1160,8 +1165,8 @@ class DownloadsPlugin extends BasePlugin {
|
|
|
1160
1165
|
}
|
|
1161
1166
|
|
|
1162
1167
|
/**
|
|
1163
|
-
*
|
|
1164
|
-
* @
|
|
1168
|
+
* Clianta SDK - Exit Intent Plugin
|
|
1169
|
+
* @see SDK_VERSION in core/config.ts
|
|
1165
1170
|
*/
|
|
1166
1171
|
/**
|
|
1167
1172
|
* Exit Intent Plugin - Detects when user intends to leave the page
|
|
@@ -1203,8 +1208,8 @@ class ExitIntentPlugin extends BasePlugin {
|
|
|
1203
1208
|
}
|
|
1204
1209
|
|
|
1205
1210
|
/**
|
|
1206
|
-
*
|
|
1207
|
-
* @
|
|
1211
|
+
* Clianta SDK - Error Tracking Plugin
|
|
1212
|
+
* @see SDK_VERSION in core/config.ts
|
|
1208
1213
|
*/
|
|
1209
1214
|
/**
|
|
1210
1215
|
* Error Tracking Plugin - Tracks JavaScript errors
|
|
@@ -1253,8 +1258,8 @@ class ErrorsPlugin extends BasePlugin {
|
|
|
1253
1258
|
}
|
|
1254
1259
|
|
|
1255
1260
|
/**
|
|
1256
|
-
*
|
|
1257
|
-
* @
|
|
1261
|
+
* Clianta SDK - Performance Plugin
|
|
1262
|
+
* @see SDK_VERSION in core/config.ts
|
|
1258
1263
|
*/
|
|
1259
1264
|
/**
|
|
1260
1265
|
* Performance Plugin - Tracks page performance and Web Vitals
|
|
@@ -1360,8 +1365,420 @@ class PerformancePlugin extends BasePlugin {
|
|
|
1360
1365
|
}
|
|
1361
1366
|
|
|
1362
1367
|
/**
|
|
1363
|
-
*
|
|
1364
|
-
* @
|
|
1368
|
+
* Clianta Tracking SDK - Popup Forms Plugin
|
|
1369
|
+
* @see SDK_VERSION in core/config.ts
|
|
1370
|
+
*
|
|
1371
|
+
* Auto-loads and displays lead capture popups based on triggers
|
|
1372
|
+
*/
|
|
1373
|
+
/**
|
|
1374
|
+
* Popup Forms Plugin - Fetches and displays lead capture forms
|
|
1375
|
+
*/
|
|
1376
|
+
class PopupFormsPlugin extends BasePlugin {
|
|
1377
|
+
constructor() {
|
|
1378
|
+
super(...arguments);
|
|
1379
|
+
this.name = 'popupForms';
|
|
1380
|
+
this.forms = [];
|
|
1381
|
+
this.shownForms = new Set();
|
|
1382
|
+
this.scrollHandler = null;
|
|
1383
|
+
this.exitHandler = null;
|
|
1384
|
+
}
|
|
1385
|
+
async init(tracker) {
|
|
1386
|
+
super.init(tracker);
|
|
1387
|
+
if (typeof window === 'undefined')
|
|
1388
|
+
return;
|
|
1389
|
+
// Load shown forms from storage
|
|
1390
|
+
this.loadShownForms();
|
|
1391
|
+
// Fetch active forms
|
|
1392
|
+
await this.fetchForms();
|
|
1393
|
+
// Setup triggers
|
|
1394
|
+
this.setupTriggers();
|
|
1395
|
+
}
|
|
1396
|
+
destroy() {
|
|
1397
|
+
this.removeTriggers();
|
|
1398
|
+
super.destroy();
|
|
1399
|
+
}
|
|
1400
|
+
loadShownForms() {
|
|
1401
|
+
try {
|
|
1402
|
+
const stored = localStorage.getItem('clianta_shown_forms');
|
|
1403
|
+
if (stored) {
|
|
1404
|
+
const data = JSON.parse(stored);
|
|
1405
|
+
this.shownForms = new Set(data.forms || []);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
catch (e) {
|
|
1409
|
+
// Ignore storage errors
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
saveShownForms() {
|
|
1413
|
+
try {
|
|
1414
|
+
localStorage.setItem('clianta_shown_forms', JSON.stringify({
|
|
1415
|
+
forms: Array.from(this.shownForms),
|
|
1416
|
+
timestamp: Date.now(),
|
|
1417
|
+
}));
|
|
1418
|
+
}
|
|
1419
|
+
catch (e) {
|
|
1420
|
+
// Ignore storage errors
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
async fetchForms() {
|
|
1424
|
+
if (!this.tracker)
|
|
1425
|
+
return;
|
|
1426
|
+
const config = this.tracker.getConfig();
|
|
1427
|
+
const workspaceId = this.tracker.getWorkspaceId();
|
|
1428
|
+
const apiEndpoint = config.apiEndpoint || 'https://api.clianta.online';
|
|
1429
|
+
try {
|
|
1430
|
+
const url = encodeURIComponent(window.location.href);
|
|
1431
|
+
const response = await fetch(`${apiEndpoint}/api/public/lead-forms/${workspaceId}?url=${url}`);
|
|
1432
|
+
if (!response.ok)
|
|
1433
|
+
return;
|
|
1434
|
+
const data = await response.json();
|
|
1435
|
+
if (data.success && Array.isArray(data.data)) {
|
|
1436
|
+
this.forms = data.data.filter((form) => this.shouldShowForm(form));
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
catch (error) {
|
|
1440
|
+
console.error('[Clianta] Failed to fetch forms:', error);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
shouldShowForm(form) {
|
|
1444
|
+
// Check show frequency
|
|
1445
|
+
if (form.showFrequency === 'once_per_visitor') {
|
|
1446
|
+
if (this.shownForms.has(form._id))
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
else if (form.showFrequency === 'once_per_session') {
|
|
1450
|
+
const sessionKey = `clianta_form_${form._id}_shown`;
|
|
1451
|
+
if (sessionStorage.getItem(sessionKey))
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
return true;
|
|
1455
|
+
}
|
|
1456
|
+
setupTriggers() {
|
|
1457
|
+
this.forms.forEach(form => {
|
|
1458
|
+
switch (form.trigger.type) {
|
|
1459
|
+
case 'delay':
|
|
1460
|
+
setTimeout(() => this.showForm(form), (form.trigger.value || 5) * 1000);
|
|
1461
|
+
break;
|
|
1462
|
+
case 'scroll':
|
|
1463
|
+
this.setupScrollTrigger(form);
|
|
1464
|
+
break;
|
|
1465
|
+
case 'exit_intent':
|
|
1466
|
+
this.setupExitIntentTrigger(form);
|
|
1467
|
+
break;
|
|
1468
|
+
case 'click':
|
|
1469
|
+
this.setupClickTrigger(form);
|
|
1470
|
+
break;
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
setupScrollTrigger(form) {
|
|
1475
|
+
const threshold = form.trigger.value || 50;
|
|
1476
|
+
this.scrollHandler = () => {
|
|
1477
|
+
const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
|
|
1478
|
+
if (scrollPercent >= threshold) {
|
|
1479
|
+
this.showForm(form);
|
|
1480
|
+
if (this.scrollHandler) {
|
|
1481
|
+
window.removeEventListener('scroll', this.scrollHandler);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
window.addEventListener('scroll', this.scrollHandler, { passive: true });
|
|
1486
|
+
}
|
|
1487
|
+
setupExitIntentTrigger(form) {
|
|
1488
|
+
this.exitHandler = (e) => {
|
|
1489
|
+
if (e.clientY <= 0) {
|
|
1490
|
+
this.showForm(form);
|
|
1491
|
+
if (this.exitHandler) {
|
|
1492
|
+
document.removeEventListener('mouseout', this.exitHandler);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
document.addEventListener('mouseout', this.exitHandler);
|
|
1497
|
+
}
|
|
1498
|
+
setupClickTrigger(form) {
|
|
1499
|
+
if (!form.trigger.selector)
|
|
1500
|
+
return;
|
|
1501
|
+
const elements = document.querySelectorAll(form.trigger.selector);
|
|
1502
|
+
elements.forEach(el => {
|
|
1503
|
+
el.addEventListener('click', () => this.showForm(form));
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
removeTriggers() {
|
|
1507
|
+
if (this.scrollHandler) {
|
|
1508
|
+
window.removeEventListener('scroll', this.scrollHandler);
|
|
1509
|
+
}
|
|
1510
|
+
if (this.exitHandler) {
|
|
1511
|
+
document.removeEventListener('mouseout', this.exitHandler);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async showForm(form) {
|
|
1515
|
+
// Check if already shown in this session
|
|
1516
|
+
if (!this.shouldShowForm(form))
|
|
1517
|
+
return;
|
|
1518
|
+
// Mark as shown
|
|
1519
|
+
this.shownForms.add(form._id);
|
|
1520
|
+
this.saveShownForms();
|
|
1521
|
+
sessionStorage.setItem(`clianta_form_${form._id}_shown`, 'true');
|
|
1522
|
+
// Track view
|
|
1523
|
+
await this.trackFormView(form._id);
|
|
1524
|
+
// Render form
|
|
1525
|
+
this.renderForm(form);
|
|
1526
|
+
}
|
|
1527
|
+
async trackFormView(formId) {
|
|
1528
|
+
if (!this.tracker)
|
|
1529
|
+
return;
|
|
1530
|
+
const config = this.tracker.getConfig();
|
|
1531
|
+
const apiEndpoint = config.apiEndpoint || 'https://api.clianta.online';
|
|
1532
|
+
try {
|
|
1533
|
+
await fetch(`${apiEndpoint}/api/public/lead-forms/${formId}/view`, {
|
|
1534
|
+
method: 'POST',
|
|
1535
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
catch (e) {
|
|
1539
|
+
// Ignore tracking errors
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
renderForm(form) {
|
|
1543
|
+
// Create overlay
|
|
1544
|
+
const overlay = document.createElement('div');
|
|
1545
|
+
overlay.id = `clianta-form-overlay-${form._id}`;
|
|
1546
|
+
overlay.style.cssText = `
|
|
1547
|
+
position: fixed;
|
|
1548
|
+
top: 0;
|
|
1549
|
+
left: 0;
|
|
1550
|
+
right: 0;
|
|
1551
|
+
bottom: 0;
|
|
1552
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1553
|
+
z-index: 999998;
|
|
1554
|
+
display: flex;
|
|
1555
|
+
align-items: center;
|
|
1556
|
+
justify-content: center;
|
|
1557
|
+
opacity: 0;
|
|
1558
|
+
transition: opacity 0.3s ease;
|
|
1559
|
+
`;
|
|
1560
|
+
// Create form container
|
|
1561
|
+
const container = document.createElement('div');
|
|
1562
|
+
container.id = `clianta-form-${form._id}`;
|
|
1563
|
+
const style = form.style || {};
|
|
1564
|
+
container.style.cssText = `
|
|
1565
|
+
background: ${style.backgroundColor || '#FFFFFF'};
|
|
1566
|
+
border-radius: ${style.borderRadius || 12}px;
|
|
1567
|
+
padding: 24px;
|
|
1568
|
+
max-width: 400px;
|
|
1569
|
+
width: 90%;
|
|
1570
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
1571
|
+
transform: translateY(20px);
|
|
1572
|
+
opacity: 0;
|
|
1573
|
+
transition: all 0.3s ease;
|
|
1574
|
+
`;
|
|
1575
|
+
// Build form HTML
|
|
1576
|
+
container.innerHTML = this.buildFormHTML(form);
|
|
1577
|
+
overlay.appendChild(container);
|
|
1578
|
+
document.body.appendChild(overlay);
|
|
1579
|
+
// Animate in
|
|
1580
|
+
requestAnimationFrame(() => {
|
|
1581
|
+
overlay.style.opacity = '1';
|
|
1582
|
+
container.style.transform = 'translateY(0)';
|
|
1583
|
+
container.style.opacity = '1';
|
|
1584
|
+
});
|
|
1585
|
+
// Setup event listeners
|
|
1586
|
+
this.setupFormEvents(form, overlay, container);
|
|
1587
|
+
}
|
|
1588
|
+
buildFormHTML(form) {
|
|
1589
|
+
const style = form.style || {};
|
|
1590
|
+
const primaryColor = style.primaryColor || '#10B981';
|
|
1591
|
+
const textColor = style.textColor || '#18181B';
|
|
1592
|
+
let fieldsHTML = form.fields.map(field => {
|
|
1593
|
+
const requiredMark = field.required ? '<span style="color: #EF4444;">*</span>' : '';
|
|
1594
|
+
if (field.type === 'textarea') {
|
|
1595
|
+
return `
|
|
1596
|
+
<div style="margin-bottom: 12px;">
|
|
1597
|
+
<label style="display: block; font-size: 14px; font-weight: 500; margin-bottom: 4px; color: ${textColor};">
|
|
1598
|
+
${field.label} ${requiredMark}
|
|
1599
|
+
</label>
|
|
1600
|
+
<textarea
|
|
1601
|
+
name="${field.name}"
|
|
1602
|
+
placeholder="${field.placeholder || ''}"
|
|
1603
|
+
${field.required ? 'required' : ''}
|
|
1604
|
+
style="width: 100%; padding: 8px 12px; border: 1px solid #E4E4E7; border-radius: 6px; font-size: 14px; resize: vertical; min-height: 80px;"
|
|
1605
|
+
></textarea>
|
|
1606
|
+
</div>
|
|
1607
|
+
`;
|
|
1608
|
+
}
|
|
1609
|
+
else if (field.type === 'checkbox') {
|
|
1610
|
+
return `
|
|
1611
|
+
<div style="margin-bottom: 12px;">
|
|
1612
|
+
<label style="display: flex; align-items: center; gap: 8px; font-size: 14px; color: ${textColor}; cursor: pointer;">
|
|
1613
|
+
<input
|
|
1614
|
+
type="checkbox"
|
|
1615
|
+
name="${field.name}"
|
|
1616
|
+
${field.required ? 'required' : ''}
|
|
1617
|
+
style="width: 16px; height: 16px;"
|
|
1618
|
+
/>
|
|
1619
|
+
${field.label} ${requiredMark}
|
|
1620
|
+
</label>
|
|
1621
|
+
</div>
|
|
1622
|
+
`;
|
|
1623
|
+
}
|
|
1624
|
+
else {
|
|
1625
|
+
return `
|
|
1626
|
+
<div style="margin-bottom: 12px;">
|
|
1627
|
+
<label style="display: block; font-size: 14px; font-weight: 500; margin-bottom: 4px; color: ${textColor};">
|
|
1628
|
+
${field.label} ${requiredMark}
|
|
1629
|
+
</label>
|
|
1630
|
+
<input
|
|
1631
|
+
type="${field.type}"
|
|
1632
|
+
name="${field.name}"
|
|
1633
|
+
placeholder="${field.placeholder || ''}"
|
|
1634
|
+
${field.required ? 'required' : ''}
|
|
1635
|
+
style="width: 100%; padding: 8px 12px; border: 1px solid #E4E4E7; border-radius: 6px; font-size: 14px; box-sizing: border-box;"
|
|
1636
|
+
/>
|
|
1637
|
+
</div>
|
|
1638
|
+
`;
|
|
1639
|
+
}
|
|
1640
|
+
}).join('');
|
|
1641
|
+
return `
|
|
1642
|
+
<button id="clianta-form-close" style="
|
|
1643
|
+
position: absolute;
|
|
1644
|
+
top: 12px;
|
|
1645
|
+
right: 12px;
|
|
1646
|
+
background: none;
|
|
1647
|
+
border: none;
|
|
1648
|
+
font-size: 20px;
|
|
1649
|
+
cursor: pointer;
|
|
1650
|
+
color: #71717A;
|
|
1651
|
+
padding: 4px;
|
|
1652
|
+
">×</button>
|
|
1653
|
+
<h2 style="font-size: 20px; font-weight: 700; margin-bottom: 8px; color: ${textColor};">
|
|
1654
|
+
${form.headline || 'Stay in touch'}
|
|
1655
|
+
</h2>
|
|
1656
|
+
<p style="font-size: 14px; color: #71717A; margin-bottom: 16px;">
|
|
1657
|
+
${form.subheadline || 'Get the latest updates'}
|
|
1658
|
+
</p>
|
|
1659
|
+
<form id="clianta-form-element">
|
|
1660
|
+
${fieldsHTML}
|
|
1661
|
+
<button type="submit" style="
|
|
1662
|
+
width: 100%;
|
|
1663
|
+
padding: 10px 16px;
|
|
1664
|
+
background: ${primaryColor};
|
|
1665
|
+
color: white;
|
|
1666
|
+
border: none;
|
|
1667
|
+
border-radius: 6px;
|
|
1668
|
+
font-size: 14px;
|
|
1669
|
+
font-weight: 500;
|
|
1670
|
+
cursor: pointer;
|
|
1671
|
+
margin-top: 8px;
|
|
1672
|
+
">
|
|
1673
|
+
${form.submitButtonText || 'Subscribe'}
|
|
1674
|
+
</button>
|
|
1675
|
+
</form>
|
|
1676
|
+
`;
|
|
1677
|
+
}
|
|
1678
|
+
setupFormEvents(form, overlay, container) {
|
|
1679
|
+
// Close button
|
|
1680
|
+
const closeBtn = container.querySelector('#clianta-form-close');
|
|
1681
|
+
if (closeBtn) {
|
|
1682
|
+
closeBtn.addEventListener('click', () => this.closeForm(form._id, overlay, container));
|
|
1683
|
+
}
|
|
1684
|
+
// Overlay click
|
|
1685
|
+
overlay.addEventListener('click', (e) => {
|
|
1686
|
+
if (e.target === overlay) {
|
|
1687
|
+
this.closeForm(form._id, overlay, container);
|
|
1688
|
+
}
|
|
1689
|
+
});
|
|
1690
|
+
// Form submit
|
|
1691
|
+
const formElement = container.querySelector('#clianta-form-element');
|
|
1692
|
+
if (formElement) {
|
|
1693
|
+
formElement.addEventListener('submit', async (e) => {
|
|
1694
|
+
e.preventDefault();
|
|
1695
|
+
await this.handleSubmit(form, formElement, container);
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
closeForm(formId, overlay, container) {
|
|
1700
|
+
container.style.transform = 'translateY(20px)';
|
|
1701
|
+
container.style.opacity = '0';
|
|
1702
|
+
overlay.style.opacity = '0';
|
|
1703
|
+
setTimeout(() => {
|
|
1704
|
+
overlay.remove();
|
|
1705
|
+
}, 300);
|
|
1706
|
+
}
|
|
1707
|
+
async handleSubmit(form, formElement, container) {
|
|
1708
|
+
if (!this.tracker)
|
|
1709
|
+
return;
|
|
1710
|
+
const config = this.tracker.getConfig();
|
|
1711
|
+
const apiEndpoint = config.apiEndpoint || 'https://api.clianta.online';
|
|
1712
|
+
const visitorId = this.tracker.getVisitorId();
|
|
1713
|
+
// Collect form data
|
|
1714
|
+
const formData = new FormData(formElement);
|
|
1715
|
+
const data = {};
|
|
1716
|
+
formData.forEach((value, key) => {
|
|
1717
|
+
data[key] = value;
|
|
1718
|
+
});
|
|
1719
|
+
// Disable submit button
|
|
1720
|
+
const submitBtn = formElement.querySelector('button[type="submit"]');
|
|
1721
|
+
if (submitBtn) {
|
|
1722
|
+
submitBtn.disabled = true;
|
|
1723
|
+
submitBtn.innerHTML = 'Submitting...';
|
|
1724
|
+
}
|
|
1725
|
+
try {
|
|
1726
|
+
const response = await fetch(`${apiEndpoint}/api/public/lead-forms/${form._id}/submit`, {
|
|
1727
|
+
method: 'POST',
|
|
1728
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1729
|
+
body: JSON.stringify({
|
|
1730
|
+
visitorId,
|
|
1731
|
+
data,
|
|
1732
|
+
url: window.location.href,
|
|
1733
|
+
}),
|
|
1734
|
+
});
|
|
1735
|
+
const result = await response.json();
|
|
1736
|
+
if (result.success) {
|
|
1737
|
+
// Show success message
|
|
1738
|
+
container.innerHTML = `
|
|
1739
|
+
<div style="text-align: center; padding: 20px;">
|
|
1740
|
+
<div style="width: 48px; height: 48px; background: #10B981; border-radius: 50%; margin: 0 auto 16px; display: flex; align-items: center; justify-content: center;">
|
|
1741
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
|
|
1742
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
1743
|
+
</svg>
|
|
1744
|
+
</div>
|
|
1745
|
+
<p style="font-size: 16px; font-weight: 500; color: #18181B;">
|
|
1746
|
+
${form.successMessage || 'Thank you!'}
|
|
1747
|
+
</p>
|
|
1748
|
+
</div>
|
|
1749
|
+
`;
|
|
1750
|
+
// Track identify
|
|
1751
|
+
if (data.email) {
|
|
1752
|
+
this.tracker?.identify(data.email, data);
|
|
1753
|
+
}
|
|
1754
|
+
// Redirect if configured
|
|
1755
|
+
if (form.redirectUrl) {
|
|
1756
|
+
setTimeout(() => {
|
|
1757
|
+
window.location.href = form.redirectUrl;
|
|
1758
|
+
}, 1500);
|
|
1759
|
+
}
|
|
1760
|
+
// Close after delay
|
|
1761
|
+
setTimeout(() => {
|
|
1762
|
+
const overlay = document.getElementById(`clianta-form-overlay-${form._id}`);
|
|
1763
|
+
if (overlay) {
|
|
1764
|
+
this.closeForm(form._id, overlay, container);
|
|
1765
|
+
}
|
|
1766
|
+
}, 2000);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
catch (error) {
|
|
1770
|
+
console.error('[Clianta] Form submit error:', error);
|
|
1771
|
+
if (submitBtn) {
|
|
1772
|
+
submitBtn.disabled = false;
|
|
1773
|
+
submitBtn.innerHTML = form.submitButtonText || 'Subscribe';
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
/**
|
|
1780
|
+
* Clianta SDK - Plugins Index
|
|
1781
|
+
* Version is defined in core/config.ts as SDK_VERSION
|
|
1365
1782
|
*/
|
|
1366
1783
|
/**
|
|
1367
1784
|
* Get plugin instance by name
|
|
@@ -1386,17 +1803,256 @@ function getPlugin(name) {
|
|
|
1386
1803
|
return new ErrorsPlugin();
|
|
1387
1804
|
case 'performance':
|
|
1388
1805
|
return new PerformancePlugin();
|
|
1806
|
+
case 'popupForms':
|
|
1807
|
+
return new PopupFormsPlugin();
|
|
1389
1808
|
default:
|
|
1390
1809
|
throw new Error(`Unknown plugin: ${name}`);
|
|
1391
1810
|
}
|
|
1392
1811
|
}
|
|
1393
1812
|
|
|
1394
1813
|
/**
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1814
|
+
* Clianta SDK - Consent Storage
|
|
1815
|
+
* Handles persistence of consent state
|
|
1816
|
+
* @see SDK_VERSION in core/config.ts
|
|
1817
|
+
*/
|
|
1818
|
+
const CONSENT_VERSION = 1;
|
|
1819
|
+
/**
|
|
1820
|
+
* Save consent state to storage
|
|
1821
|
+
*/
|
|
1822
|
+
function saveConsent(state) {
|
|
1823
|
+
try {
|
|
1824
|
+
if (typeof localStorage === 'undefined')
|
|
1825
|
+
return false;
|
|
1826
|
+
const stored = {
|
|
1827
|
+
state,
|
|
1828
|
+
timestamp: Date.now(),
|
|
1829
|
+
version: CONSENT_VERSION,
|
|
1830
|
+
};
|
|
1831
|
+
localStorage.setItem(STORAGE_KEYS.CONSENT, JSON.stringify(stored));
|
|
1832
|
+
return true;
|
|
1833
|
+
}
|
|
1834
|
+
catch {
|
|
1835
|
+
return false;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Load consent state from storage
|
|
1840
|
+
*/
|
|
1841
|
+
function loadConsent() {
|
|
1842
|
+
try {
|
|
1843
|
+
if (typeof localStorage === 'undefined')
|
|
1844
|
+
return null;
|
|
1845
|
+
const stored = localStorage.getItem(STORAGE_KEYS.CONSENT);
|
|
1846
|
+
if (!stored)
|
|
1847
|
+
return null;
|
|
1848
|
+
const parsed = JSON.parse(stored);
|
|
1849
|
+
// Validate version
|
|
1850
|
+
if (parsed.version !== CONSENT_VERSION) {
|
|
1851
|
+
clearConsent();
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
return parsed;
|
|
1855
|
+
}
|
|
1856
|
+
catch {
|
|
1857
|
+
return null;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* Clear consent state from storage
|
|
1862
|
+
*/
|
|
1863
|
+
function clearConsent() {
|
|
1864
|
+
try {
|
|
1865
|
+
if (typeof localStorage === 'undefined')
|
|
1866
|
+
return false;
|
|
1867
|
+
localStorage.removeItem(STORAGE_KEYS.CONSENT);
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
catch {
|
|
1871
|
+
return false;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
/**
|
|
1875
|
+
* Check if consent has been explicitly set
|
|
1876
|
+
*/
|
|
1877
|
+
function hasStoredConsent() {
|
|
1878
|
+
return loadConsent() !== null;
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* Clianta SDK - Consent Manager
|
|
1883
|
+
* Manages consent state and event buffering for GDPR/CCPA compliance
|
|
1884
|
+
* @see SDK_VERSION in core/config.ts
|
|
1397
1885
|
*/
|
|
1398
1886
|
/**
|
|
1399
|
-
*
|
|
1887
|
+
* Manages user consent state for tracking
|
|
1888
|
+
*/
|
|
1889
|
+
class ConsentManager {
|
|
1890
|
+
constructor(config = {}) {
|
|
1891
|
+
this.eventBuffer = [];
|
|
1892
|
+
this.callbacks = [];
|
|
1893
|
+
this.hasExplicitConsent = false;
|
|
1894
|
+
this.config = {
|
|
1895
|
+
defaultConsent: { analytics: true, marketing: false, personalization: false },
|
|
1896
|
+
waitForConsent: false,
|
|
1897
|
+
storageKey: 'mb_consent',
|
|
1898
|
+
...config,
|
|
1899
|
+
};
|
|
1900
|
+
// Load stored consent or use default
|
|
1901
|
+
const stored = loadConsent();
|
|
1902
|
+
if (stored) {
|
|
1903
|
+
this.state = stored.state;
|
|
1904
|
+
this.hasExplicitConsent = true;
|
|
1905
|
+
logger.debug('Loaded stored consent:', this.state);
|
|
1906
|
+
}
|
|
1907
|
+
else {
|
|
1908
|
+
this.state = this.config.defaultConsent || { analytics: true };
|
|
1909
|
+
this.hasExplicitConsent = false;
|
|
1910
|
+
logger.debug('Using default consent:', this.state);
|
|
1911
|
+
}
|
|
1912
|
+
// Register callback if provided
|
|
1913
|
+
if (config.onConsentChange) {
|
|
1914
|
+
this.callbacks.push(config.onConsentChange);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Grant consent for specified categories
|
|
1919
|
+
*/
|
|
1920
|
+
grant(categories) {
|
|
1921
|
+
const previous = { ...this.state };
|
|
1922
|
+
this.state = { ...this.state, ...categories };
|
|
1923
|
+
this.hasExplicitConsent = true;
|
|
1924
|
+
saveConsent(this.state);
|
|
1925
|
+
logger.info('Consent granted:', categories);
|
|
1926
|
+
this.notifyChange(previous);
|
|
1927
|
+
}
|
|
1928
|
+
/**
|
|
1929
|
+
* Revoke consent for specified categories
|
|
1930
|
+
*/
|
|
1931
|
+
revoke(categories) {
|
|
1932
|
+
const previous = { ...this.state };
|
|
1933
|
+
for (const category of categories) {
|
|
1934
|
+
this.state[category] = false;
|
|
1935
|
+
}
|
|
1936
|
+
this.hasExplicitConsent = true;
|
|
1937
|
+
saveConsent(this.state);
|
|
1938
|
+
logger.info('Consent revoked:', categories);
|
|
1939
|
+
this.notifyChange(previous);
|
|
1940
|
+
}
|
|
1941
|
+
/**
|
|
1942
|
+
* Update entire consent state
|
|
1943
|
+
*/
|
|
1944
|
+
update(state) {
|
|
1945
|
+
const previous = { ...this.state };
|
|
1946
|
+
this.state = { ...state };
|
|
1947
|
+
this.hasExplicitConsent = true;
|
|
1948
|
+
saveConsent(this.state);
|
|
1949
|
+
logger.info('Consent updated:', this.state);
|
|
1950
|
+
this.notifyChange(previous);
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Reset consent to default (clear stored consent)
|
|
1954
|
+
*/
|
|
1955
|
+
reset() {
|
|
1956
|
+
const previous = { ...this.state };
|
|
1957
|
+
this.state = this.config.defaultConsent || { analytics: true };
|
|
1958
|
+
this.hasExplicitConsent = false;
|
|
1959
|
+
this.eventBuffer = [];
|
|
1960
|
+
clearConsent();
|
|
1961
|
+
logger.info('Consent reset to defaults');
|
|
1962
|
+
this.notifyChange(previous);
|
|
1963
|
+
}
|
|
1964
|
+
/**
|
|
1965
|
+
* Get current consent state
|
|
1966
|
+
*/
|
|
1967
|
+
getState() {
|
|
1968
|
+
return { ...this.state };
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Check if a specific consent category is granted
|
|
1972
|
+
*/
|
|
1973
|
+
hasConsent(category) {
|
|
1974
|
+
return this.state[category] === true;
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Check if analytics consent is granted (most common check)
|
|
1978
|
+
*/
|
|
1979
|
+
canTrack() {
|
|
1980
|
+
// If waiting for consent and no explicit consent given, cannot track
|
|
1981
|
+
if (this.config.waitForConsent && !this.hasExplicitConsent) {
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
return this.state.analytics === true;
|
|
1985
|
+
}
|
|
1986
|
+
/**
|
|
1987
|
+
* Check if explicit consent has been given
|
|
1988
|
+
*/
|
|
1989
|
+
hasExplicit() {
|
|
1990
|
+
return this.hasExplicitConsent;
|
|
1991
|
+
}
|
|
1992
|
+
/**
|
|
1993
|
+
* Check if there's stored consent
|
|
1994
|
+
*/
|
|
1995
|
+
hasStored() {
|
|
1996
|
+
return hasStoredConsent();
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Buffer an event (for waitForConsent mode)
|
|
2000
|
+
*/
|
|
2001
|
+
bufferEvent(event) {
|
|
2002
|
+
this.eventBuffer.push(event);
|
|
2003
|
+
logger.debug('Event buffered (waiting for consent):', event.eventName);
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Get and clear buffered events
|
|
2007
|
+
*/
|
|
2008
|
+
flushBuffer() {
|
|
2009
|
+
const events = [...this.eventBuffer];
|
|
2010
|
+
this.eventBuffer = [];
|
|
2011
|
+
if (events.length > 0) {
|
|
2012
|
+
logger.debug(`Flushing ${events.length} buffered events`);
|
|
2013
|
+
}
|
|
2014
|
+
return events;
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Get buffered event count
|
|
2018
|
+
*/
|
|
2019
|
+
getBufferSize() {
|
|
2020
|
+
return this.eventBuffer.length;
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Register a consent change callback
|
|
2024
|
+
*/
|
|
2025
|
+
onChange(callback) {
|
|
2026
|
+
this.callbacks.push(callback);
|
|
2027
|
+
// Return unsubscribe function
|
|
2028
|
+
return () => {
|
|
2029
|
+
const index = this.callbacks.indexOf(callback);
|
|
2030
|
+
if (index > -1) {
|
|
2031
|
+
this.callbacks.splice(index, 1);
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
/**
|
|
2036
|
+
* Notify all callbacks of consent change
|
|
2037
|
+
*/
|
|
2038
|
+
notifyChange(previous) {
|
|
2039
|
+
for (const callback of this.callbacks) {
|
|
2040
|
+
try {
|
|
2041
|
+
callback(this.state, previous);
|
|
2042
|
+
}
|
|
2043
|
+
catch (error) {
|
|
2044
|
+
logger.error('Consent change callback error:', error);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
/**
|
|
2051
|
+
* Clianta SDK - Main Tracker Class
|
|
2052
|
+
* @see SDK_VERSION in core/config.ts
|
|
2053
|
+
*/
|
|
2054
|
+
/**
|
|
2055
|
+
* Main Clianta Tracker Class
|
|
1400
2056
|
*/
|
|
1401
2057
|
class Tracker {
|
|
1402
2058
|
constructor(workspaceId, userConfig = {}) {
|
|
@@ -1410,21 +2066,81 @@ class Tracker {
|
|
|
1410
2066
|
// Setup debug mode
|
|
1411
2067
|
logger.enabled = this.config.debug;
|
|
1412
2068
|
logger.info(`Initializing SDK v${SDK_VERSION}`, { workspaceId });
|
|
2069
|
+
// Initialize consent manager
|
|
2070
|
+
this.consentManager = new ConsentManager({
|
|
2071
|
+
...this.config.consent,
|
|
2072
|
+
onConsentChange: (state, previous) => {
|
|
2073
|
+
this.onConsentChange(state, previous);
|
|
2074
|
+
},
|
|
2075
|
+
});
|
|
1413
2076
|
// Initialize transport and queue
|
|
1414
2077
|
this.transport = new Transport({ apiEndpoint: this.config.apiEndpoint });
|
|
1415
2078
|
this.queue = new EventQueue(this.transport, {
|
|
1416
2079
|
batchSize: this.config.batchSize,
|
|
1417
2080
|
flushInterval: this.config.flushInterval,
|
|
1418
2081
|
});
|
|
1419
|
-
// Get or create visitor and session IDs
|
|
1420
|
-
this.visitorId =
|
|
1421
|
-
this.sessionId =
|
|
2082
|
+
// Get or create visitor and session IDs based on mode
|
|
2083
|
+
this.visitorId = this.createVisitorId();
|
|
2084
|
+
this.sessionId = this.createSessionId();
|
|
1422
2085
|
logger.debug('IDs created', { visitorId: this.visitorId, sessionId: this.sessionId });
|
|
1423
2086
|
// Initialize plugins
|
|
1424
2087
|
this.initPlugins();
|
|
1425
2088
|
this.isInitialized = true;
|
|
1426
2089
|
logger.info('SDK initialized successfully');
|
|
1427
2090
|
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Create visitor ID based on storage mode
|
|
2093
|
+
*/
|
|
2094
|
+
createVisitorId() {
|
|
2095
|
+
// Anonymous mode: use temporary ID until consent
|
|
2096
|
+
if (this.config.consent.anonymousMode && !this.consentManager.hasExplicit()) {
|
|
2097
|
+
const key = STORAGE_KEYS.VISITOR_ID + '_anon';
|
|
2098
|
+
let anonId = getSessionStorage(key);
|
|
2099
|
+
if (!anonId) {
|
|
2100
|
+
anonId = 'anon_' + generateUUID();
|
|
2101
|
+
setSessionStorage(key, anonId);
|
|
2102
|
+
}
|
|
2103
|
+
return anonId;
|
|
2104
|
+
}
|
|
2105
|
+
// Cookie-less mode: use sessionStorage only
|
|
2106
|
+
if (this.config.cookielessMode) {
|
|
2107
|
+
let visitorId = getSessionStorage(STORAGE_KEYS.VISITOR_ID);
|
|
2108
|
+
if (!visitorId) {
|
|
2109
|
+
visitorId = generateUUID();
|
|
2110
|
+
setSessionStorage(STORAGE_KEYS.VISITOR_ID, visitorId);
|
|
2111
|
+
}
|
|
2112
|
+
return visitorId;
|
|
2113
|
+
}
|
|
2114
|
+
// Normal mode
|
|
2115
|
+
return getOrCreateVisitorId(this.config.useCookies);
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* Create session ID
|
|
2119
|
+
*/
|
|
2120
|
+
createSessionId() {
|
|
2121
|
+
return getOrCreateSessionId(this.config.sessionTimeout);
|
|
2122
|
+
}
|
|
2123
|
+
/**
|
|
2124
|
+
* Handle consent state changes
|
|
2125
|
+
*/
|
|
2126
|
+
onConsentChange(state, previous) {
|
|
2127
|
+
logger.debug('Consent changed:', { from: previous, to: state });
|
|
2128
|
+
// If analytics consent was just granted
|
|
2129
|
+
if (state.analytics && !previous.analytics) {
|
|
2130
|
+
// Upgrade from anonymous ID to persistent ID
|
|
2131
|
+
if (this.config.consent.anonymousMode) {
|
|
2132
|
+
this.visitorId = getOrCreateVisitorId(this.config.useCookies);
|
|
2133
|
+
logger.info('Upgraded from anonymous to persistent visitor ID');
|
|
2134
|
+
}
|
|
2135
|
+
// Flush buffered events
|
|
2136
|
+
const buffered = this.consentManager.flushBuffer();
|
|
2137
|
+
for (const event of buffered) {
|
|
2138
|
+
// Update event with new visitor ID
|
|
2139
|
+
event.visitorId = this.visitorId;
|
|
2140
|
+
this.queue.push(event);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
1428
2144
|
/**
|
|
1429
2145
|
* Initialize enabled plugins
|
|
1430
2146
|
*/
|
|
@@ -1468,6 +2184,17 @@ class Tracker {
|
|
|
1468
2184
|
timestamp: new Date().toISOString(),
|
|
1469
2185
|
sdkVersion: SDK_VERSION,
|
|
1470
2186
|
};
|
|
2187
|
+
// Check consent before tracking
|
|
2188
|
+
if (!this.consentManager.canTrack()) {
|
|
2189
|
+
// Buffer event for later if waitForConsent is enabled
|
|
2190
|
+
if (this.config.consent.waitForConsent) {
|
|
2191
|
+
this.consentManager.bufferEvent(event);
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
// Otherwise drop the event
|
|
2195
|
+
logger.debug('Event dropped (no consent):', eventName);
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
1471
2198
|
this.queue.push(event);
|
|
1472
2199
|
logger.debug('Event tracked:', eventName, properties);
|
|
1473
2200
|
}
|
|
@@ -1507,11 +2234,13 @@ class Tracker {
|
|
|
1507
2234
|
* Update consent state
|
|
1508
2235
|
*/
|
|
1509
2236
|
consent(state) {
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
2237
|
+
this.consentManager.update(state);
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Get current consent state
|
|
2241
|
+
*/
|
|
2242
|
+
getConsentState() {
|
|
2243
|
+
return this.consentManager.getState();
|
|
1515
2244
|
}
|
|
1516
2245
|
/**
|
|
1517
2246
|
* Toggle debug mode
|
|
@@ -1556,10 +2285,49 @@ class Tracker {
|
|
|
1556
2285
|
reset() {
|
|
1557
2286
|
logger.info('Resetting visitor data');
|
|
1558
2287
|
resetIds(this.config.useCookies);
|
|
1559
|
-
this.visitorId =
|
|
1560
|
-
this.sessionId =
|
|
2288
|
+
this.visitorId = this.createVisitorId();
|
|
2289
|
+
this.sessionId = this.createSessionId();
|
|
1561
2290
|
this.queue.clear();
|
|
1562
2291
|
}
|
|
2292
|
+
/**
|
|
2293
|
+
* Delete all stored user data (GDPR right-to-erasure)
|
|
2294
|
+
*/
|
|
2295
|
+
deleteData() {
|
|
2296
|
+
logger.info('Deleting all user data (GDPR request)');
|
|
2297
|
+
// Clear queue
|
|
2298
|
+
this.queue.clear();
|
|
2299
|
+
// Reset consent
|
|
2300
|
+
this.consentManager.reset();
|
|
2301
|
+
// Clear all stored IDs
|
|
2302
|
+
resetIds(this.config.useCookies);
|
|
2303
|
+
// Clear session storage items
|
|
2304
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
2305
|
+
try {
|
|
2306
|
+
sessionStorage.removeItem(STORAGE_KEYS.VISITOR_ID);
|
|
2307
|
+
sessionStorage.removeItem(STORAGE_KEYS.VISITOR_ID + '_anon');
|
|
2308
|
+
sessionStorage.removeItem(STORAGE_KEYS.SESSION_ID);
|
|
2309
|
+
sessionStorage.removeItem(STORAGE_KEYS.SESSION_TIMESTAMP);
|
|
2310
|
+
}
|
|
2311
|
+
catch {
|
|
2312
|
+
// Ignore errors
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
// Clear localStorage items
|
|
2316
|
+
if (typeof localStorage !== 'undefined') {
|
|
2317
|
+
try {
|
|
2318
|
+
localStorage.removeItem(STORAGE_KEYS.VISITOR_ID);
|
|
2319
|
+
localStorage.removeItem(STORAGE_KEYS.CONSENT);
|
|
2320
|
+
localStorage.removeItem(STORAGE_KEYS.EVENT_QUEUE);
|
|
2321
|
+
}
|
|
2322
|
+
catch {
|
|
2323
|
+
// Ignore errors
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
// Generate new IDs
|
|
2327
|
+
this.visitorId = this.createVisitorId();
|
|
2328
|
+
this.sessionId = this.createSessionId();
|
|
2329
|
+
logger.info('All user data deleted');
|
|
2330
|
+
}
|
|
1563
2331
|
/**
|
|
1564
2332
|
* Destroy tracker and cleanup
|
|
1565
2333
|
*/
|
|
@@ -1582,7 +2350,7 @@ class Tracker {
|
|
|
1582
2350
|
|
|
1583
2351
|
/**
|
|
1584
2352
|
* Clianta SDK - CRM API Client
|
|
1585
|
-
* @
|
|
2353
|
+
* @see SDK_VERSION in core/config.ts
|
|
1586
2354
|
*/
|
|
1587
2355
|
/**
|
|
1588
2356
|
* CRM API Client for managing contacts and opportunities
|
|
@@ -1756,7 +2524,7 @@ class CRMClient {
|
|
|
1756
2524
|
/**
|
|
1757
2525
|
* Clianta SDK
|
|
1758
2526
|
* Professional CRM and tracking SDK for lead generation
|
|
1759
|
-
* @
|
|
2527
|
+
* @see SDK_VERSION in core/config.ts
|
|
1760
2528
|
*/
|
|
1761
2529
|
// Global instance cache
|
|
1762
2530
|
let globalInstance = null;
|
|
@@ -1773,6 +2541,16 @@ let globalInstance = null;
|
|
|
1773
2541
|
* debug: true,
|
|
1774
2542
|
* plugins: ['pageView', 'forms', 'scroll'],
|
|
1775
2543
|
* });
|
|
2544
|
+
*
|
|
2545
|
+
* @example
|
|
2546
|
+
* // With consent configuration
|
|
2547
|
+
* const tracker = clianta('your-workspace-id', {
|
|
2548
|
+
* consent: {
|
|
2549
|
+
* waitForConsent: true,
|
|
2550
|
+
* anonymousMode: true,
|
|
2551
|
+
* },
|
|
2552
|
+
* cookielessMode: true, // GDPR-friendly mode
|
|
2553
|
+
* });
|
|
1776
2554
|
*/
|
|
1777
2555
|
function clianta(workspaceId, config) {
|
|
1778
2556
|
// Return existing instance if same workspace
|
|
@@ -1794,8 +2572,9 @@ if (typeof window !== 'undefined') {
|
|
|
1794
2572
|
clianta,
|
|
1795
2573
|
Tracker,
|
|
1796
2574
|
CRMClient,
|
|
2575
|
+
ConsentManager,
|
|
1797
2576
|
};
|
|
1798
2577
|
}
|
|
1799
2578
|
|
|
1800
|
-
export { CRMClient, SDK_VERSION, Tracker, clianta, clianta as default };
|
|
2579
|
+
export { CRMClient, ConsentManager, SDK_VERSION, Tracker, clianta, clianta as default };
|
|
1801
2580
|
//# sourceMappingURL=clianta.esm.js.map
|