@clianta/sdk 1.6.0 → 1.6.2
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 +86 -470
- package/dist/angular.cjs.js +39 -1337
- package/dist/angular.cjs.js.map +1 -1
- package/dist/angular.d.ts +2 -125
- package/dist/angular.esm.js +39 -1337
- package/dist/angular.esm.js.map +1 -1
- package/dist/clianta.cjs.js +39 -1336
- package/dist/clianta.cjs.js.map +1 -1
- package/dist/clianta.esm.js +40 -1335
- package/dist/clianta.esm.js.map +1 -1
- package/dist/clianta.umd.js +39 -1336
- 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 +13 -1033
- package/dist/react.cjs.js +58 -1385
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +17 -161
- package/dist/react.esm.js +58 -1385
- package/dist/react.esm.js.map +1 -1
- package/dist/svelte.cjs.js +39 -1335
- package/dist/svelte.cjs.js.map +1 -1
- package/dist/svelte.d.ts +2 -123
- package/dist/svelte.esm.js +39 -1335
- package/dist/svelte.esm.js.map +1 -1
- package/dist/vue.cjs.js +39 -1336
- package/dist/vue.cjs.js.map +1 -1
- package/dist/vue.d.ts +2 -124
- package/dist/vue.esm.js +39 -1336
- package/dist/vue.esm.js.map +1 -1
- package/package.json +2 -2
package/dist/react.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Clianta SDK v1.6.
|
|
2
|
+
* Clianta SDK v1.6.2
|
|
3
3
|
* (c) 2026 Clianta
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -13,7 +13,7 @@ var react = require('react');
|
|
|
13
13
|
* @see SDK_VERSION in core/config.ts
|
|
14
14
|
*/
|
|
15
15
|
/** SDK Version */
|
|
16
|
-
const SDK_VERSION = '1.6.
|
|
16
|
+
const SDK_VERSION = '1.6.2';
|
|
17
17
|
/** Default API endpoint — reads from env or falls back to localhost */
|
|
18
18
|
const getDefaultApiEndpoint = () => {
|
|
19
19
|
// Build-time env var (works with Next.js, Vite, CRA, etc.)
|
|
@@ -31,7 +31,7 @@ const getDefaultApiEndpoint = () => {
|
|
|
31
31
|
}
|
|
32
32
|
return 'http://localhost:5000';
|
|
33
33
|
};
|
|
34
|
-
/** Core plugins enabled by default */
|
|
34
|
+
/** Core plugins enabled by default — all auto-track with zero config */
|
|
35
35
|
const DEFAULT_PLUGINS = [
|
|
36
36
|
'pageView',
|
|
37
37
|
'forms',
|
|
@@ -40,13 +40,13 @@ const DEFAULT_PLUGINS = [
|
|
|
40
40
|
'engagement',
|
|
41
41
|
'downloads',
|
|
42
42
|
'exitIntent',
|
|
43
|
+
'errors',
|
|
44
|
+
'performance',
|
|
43
45
|
];
|
|
44
46
|
/** Default configuration values */
|
|
45
47
|
const DEFAULT_CONFIG = {
|
|
46
48
|
projectId: '',
|
|
47
49
|
apiEndpoint: getDefaultApiEndpoint(),
|
|
48
|
-
authToken: '',
|
|
49
|
-
apiKey: '',
|
|
50
50
|
debug: false,
|
|
51
51
|
autoPageView: true,
|
|
52
52
|
plugins: DEFAULT_PLUGINS,
|
|
@@ -2512,1268 +2512,6 @@ class ConsentManager {
|
|
|
2512
2512
|
}
|
|
2513
2513
|
}
|
|
2514
2514
|
|
|
2515
|
-
/**
|
|
2516
|
-
* Clianta SDK - Event Triggers Manager
|
|
2517
|
-
* Manages event-driven automation and email notifications
|
|
2518
|
-
*/
|
|
2519
|
-
/**
|
|
2520
|
-
* Event Triggers Manager
|
|
2521
|
-
* Handles event-driven automation based on CRM actions
|
|
2522
|
-
*
|
|
2523
|
-
* Similar to:
|
|
2524
|
-
* - Salesforce: Process Builder, Flow Automation
|
|
2525
|
-
* - HubSpot: Workflows, Email Sequences
|
|
2526
|
-
* - Pipedrive: Workflow Automation
|
|
2527
|
-
*/
|
|
2528
|
-
class EventTriggersManager {
|
|
2529
|
-
constructor(apiEndpoint, workspaceId, authToken) {
|
|
2530
|
-
this.triggers = new Map();
|
|
2531
|
-
this.listeners = new Map();
|
|
2532
|
-
this.apiEndpoint = apiEndpoint;
|
|
2533
|
-
this.workspaceId = workspaceId;
|
|
2534
|
-
this.authToken = authToken;
|
|
2535
|
-
}
|
|
2536
|
-
/**
|
|
2537
|
-
* Set authentication token
|
|
2538
|
-
*/
|
|
2539
|
-
setAuthToken(token) {
|
|
2540
|
-
this.authToken = token;
|
|
2541
|
-
}
|
|
2542
|
-
/**
|
|
2543
|
-
* Make authenticated API request
|
|
2544
|
-
*/
|
|
2545
|
-
async request(endpoint, options = {}) {
|
|
2546
|
-
const url = `${this.apiEndpoint}${endpoint}`;
|
|
2547
|
-
const headers = {
|
|
2548
|
-
'Content-Type': 'application/json',
|
|
2549
|
-
...(options.headers || {}),
|
|
2550
|
-
};
|
|
2551
|
-
if (this.authToken) {
|
|
2552
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
2553
|
-
}
|
|
2554
|
-
try {
|
|
2555
|
-
const response = await fetch(url, {
|
|
2556
|
-
...options,
|
|
2557
|
-
headers,
|
|
2558
|
-
});
|
|
2559
|
-
const data = await response.json();
|
|
2560
|
-
if (!response.ok) {
|
|
2561
|
-
return {
|
|
2562
|
-
success: false,
|
|
2563
|
-
error: data.message || 'Request failed',
|
|
2564
|
-
status: response.status,
|
|
2565
|
-
};
|
|
2566
|
-
}
|
|
2567
|
-
return {
|
|
2568
|
-
success: true,
|
|
2569
|
-
data: data.data || data,
|
|
2570
|
-
status: response.status,
|
|
2571
|
-
};
|
|
2572
|
-
}
|
|
2573
|
-
catch (error) {
|
|
2574
|
-
return {
|
|
2575
|
-
success: false,
|
|
2576
|
-
error: error instanceof Error ? error.message : 'Network error',
|
|
2577
|
-
status: 0,
|
|
2578
|
-
};
|
|
2579
|
-
}
|
|
2580
|
-
}
|
|
2581
|
-
// ============================================
|
|
2582
|
-
// TRIGGER MANAGEMENT
|
|
2583
|
-
// ============================================
|
|
2584
|
-
/**
|
|
2585
|
-
* Get all event triggers
|
|
2586
|
-
*/
|
|
2587
|
-
async getTriggers() {
|
|
2588
|
-
return this.request(`/api/workspaces/${this.workspaceId}/triggers`);
|
|
2589
|
-
}
|
|
2590
|
-
/**
|
|
2591
|
-
* Get a single trigger by ID
|
|
2592
|
-
*/
|
|
2593
|
-
async getTrigger(triggerId) {
|
|
2594
|
-
return this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`);
|
|
2595
|
-
}
|
|
2596
|
-
/**
|
|
2597
|
-
* Create a new event trigger
|
|
2598
|
-
*/
|
|
2599
|
-
async createTrigger(trigger) {
|
|
2600
|
-
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers`, {
|
|
2601
|
-
method: 'POST',
|
|
2602
|
-
body: JSON.stringify(trigger),
|
|
2603
|
-
});
|
|
2604
|
-
// Cache the trigger locally if successful
|
|
2605
|
-
if (result.success && result.data?._id) {
|
|
2606
|
-
this.triggers.set(result.data._id, result.data);
|
|
2607
|
-
}
|
|
2608
|
-
return result;
|
|
2609
|
-
}
|
|
2610
|
-
/**
|
|
2611
|
-
* Update an existing trigger
|
|
2612
|
-
*/
|
|
2613
|
-
async updateTrigger(triggerId, updates) {
|
|
2614
|
-
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
|
|
2615
|
-
method: 'PUT',
|
|
2616
|
-
body: JSON.stringify(updates),
|
|
2617
|
-
});
|
|
2618
|
-
// Update cache if successful
|
|
2619
|
-
if (result.success && result.data?._id) {
|
|
2620
|
-
this.triggers.set(result.data._id, result.data);
|
|
2621
|
-
}
|
|
2622
|
-
return result;
|
|
2623
|
-
}
|
|
2624
|
-
/**
|
|
2625
|
-
* Delete a trigger
|
|
2626
|
-
*/
|
|
2627
|
-
async deleteTrigger(triggerId) {
|
|
2628
|
-
const result = await this.request(`/api/workspaces/${this.workspaceId}/triggers/${triggerId}`, {
|
|
2629
|
-
method: 'DELETE',
|
|
2630
|
-
});
|
|
2631
|
-
// Remove from cache if successful
|
|
2632
|
-
if (result.success) {
|
|
2633
|
-
this.triggers.delete(triggerId);
|
|
2634
|
-
}
|
|
2635
|
-
return result;
|
|
2636
|
-
}
|
|
2637
|
-
/**
|
|
2638
|
-
* Activate a trigger
|
|
2639
|
-
*/
|
|
2640
|
-
async activateTrigger(triggerId) {
|
|
2641
|
-
return this.updateTrigger(triggerId, { isActive: true });
|
|
2642
|
-
}
|
|
2643
|
-
/**
|
|
2644
|
-
* Deactivate a trigger
|
|
2645
|
-
*/
|
|
2646
|
-
async deactivateTrigger(triggerId) {
|
|
2647
|
-
return this.updateTrigger(triggerId, { isActive: false });
|
|
2648
|
-
}
|
|
2649
|
-
// ============================================
|
|
2650
|
-
// EVENT HANDLING (CLIENT-SIDE)
|
|
2651
|
-
// ============================================
|
|
2652
|
-
/**
|
|
2653
|
-
* Register a local event listener for client-side triggers
|
|
2654
|
-
* This allows immediate client-side reactions to events
|
|
2655
|
-
*/
|
|
2656
|
-
on(eventType, callback) {
|
|
2657
|
-
if (!this.listeners.has(eventType)) {
|
|
2658
|
-
this.listeners.set(eventType, new Set());
|
|
2659
|
-
}
|
|
2660
|
-
this.listeners.get(eventType).add(callback);
|
|
2661
|
-
logger.debug(`Event listener registered: ${eventType}`);
|
|
2662
|
-
}
|
|
2663
|
-
/**
|
|
2664
|
-
* Remove an event listener
|
|
2665
|
-
*/
|
|
2666
|
-
off(eventType, callback) {
|
|
2667
|
-
const listeners = this.listeners.get(eventType);
|
|
2668
|
-
if (listeners) {
|
|
2669
|
-
listeners.delete(callback);
|
|
2670
|
-
}
|
|
2671
|
-
}
|
|
2672
|
-
/**
|
|
2673
|
-
* Emit an event (client-side only)
|
|
2674
|
-
* This will trigger any registered local listeners
|
|
2675
|
-
*/
|
|
2676
|
-
emit(eventType, data) {
|
|
2677
|
-
logger.debug(`Event emitted: ${eventType}`, data);
|
|
2678
|
-
const listeners = this.listeners.get(eventType);
|
|
2679
|
-
if (listeners) {
|
|
2680
|
-
listeners.forEach(callback => {
|
|
2681
|
-
try {
|
|
2682
|
-
callback(data);
|
|
2683
|
-
}
|
|
2684
|
-
catch (error) {
|
|
2685
|
-
logger.error(`Error in event listener for ${eventType}:`, error);
|
|
2686
|
-
}
|
|
2687
|
-
});
|
|
2688
|
-
}
|
|
2689
|
-
}
|
|
2690
|
-
/**
|
|
2691
|
-
* Check if conditions are met for a trigger
|
|
2692
|
-
* Supports dynamic field evaluation including custom fields and nested paths
|
|
2693
|
-
*/
|
|
2694
|
-
evaluateConditions(conditions, data) {
|
|
2695
|
-
if (!conditions || conditions.length === 0) {
|
|
2696
|
-
return true; // No conditions means always fire
|
|
2697
|
-
}
|
|
2698
|
-
return conditions.every(condition => {
|
|
2699
|
-
// Support dot notation for nested fields (e.g., 'customFields.industry')
|
|
2700
|
-
const fieldValue = condition.field.includes('.')
|
|
2701
|
-
? this.getNestedValue(data, condition.field)
|
|
2702
|
-
: data[condition.field];
|
|
2703
|
-
const targetValue = condition.value;
|
|
2704
|
-
switch (condition.operator) {
|
|
2705
|
-
case 'equals':
|
|
2706
|
-
return fieldValue === targetValue;
|
|
2707
|
-
case 'not_equals':
|
|
2708
|
-
return fieldValue !== targetValue;
|
|
2709
|
-
case 'contains':
|
|
2710
|
-
return String(fieldValue).includes(String(targetValue));
|
|
2711
|
-
case 'greater_than':
|
|
2712
|
-
return Number(fieldValue) > Number(targetValue);
|
|
2713
|
-
case 'less_than':
|
|
2714
|
-
return Number(fieldValue) < Number(targetValue);
|
|
2715
|
-
case 'in':
|
|
2716
|
-
return Array.isArray(targetValue) && targetValue.includes(fieldValue);
|
|
2717
|
-
case 'not_in':
|
|
2718
|
-
return Array.isArray(targetValue) && !targetValue.includes(fieldValue);
|
|
2719
|
-
default:
|
|
2720
|
-
return false;
|
|
2721
|
-
}
|
|
2722
|
-
});
|
|
2723
|
-
}
|
|
2724
|
-
/**
|
|
2725
|
-
* Execute actions for a triggered event (client-side preview)
|
|
2726
|
-
* Note: Actual execution happens on the backend
|
|
2727
|
-
*/
|
|
2728
|
-
async executeActions(trigger, data) {
|
|
2729
|
-
logger.info(`Executing actions for trigger: ${trigger.name}`);
|
|
2730
|
-
for (const action of trigger.actions) {
|
|
2731
|
-
try {
|
|
2732
|
-
await this.executeAction(action, data);
|
|
2733
|
-
}
|
|
2734
|
-
catch (error) {
|
|
2735
|
-
logger.error(`Failed to execute action:`, error);
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
/**
|
|
2740
|
-
* Execute a single action
|
|
2741
|
-
*/
|
|
2742
|
-
async executeAction(action, data) {
|
|
2743
|
-
switch (action.type) {
|
|
2744
|
-
case 'send_email':
|
|
2745
|
-
await this.executeSendEmail(action, data);
|
|
2746
|
-
break;
|
|
2747
|
-
case 'webhook':
|
|
2748
|
-
await this.executeWebhook(action, data);
|
|
2749
|
-
break;
|
|
2750
|
-
case 'create_task':
|
|
2751
|
-
await this.executeCreateTask(action, data);
|
|
2752
|
-
break;
|
|
2753
|
-
case 'update_contact':
|
|
2754
|
-
await this.executeUpdateContact(action, data);
|
|
2755
|
-
break;
|
|
2756
|
-
default:
|
|
2757
|
-
logger.warn(`Unknown action type:`, action);
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
/**
|
|
2761
|
-
* Execute send email action (via backend API)
|
|
2762
|
-
*/
|
|
2763
|
-
async executeSendEmail(action, data) {
|
|
2764
|
-
logger.debug('Sending email:', action);
|
|
2765
|
-
const payload = {
|
|
2766
|
-
to: this.replaceVariables(action.to, data),
|
|
2767
|
-
subject: action.subject ? this.replaceVariables(action.subject, data) : undefined,
|
|
2768
|
-
body: action.body ? this.replaceVariables(action.body, data) : undefined,
|
|
2769
|
-
templateId: action.templateId,
|
|
2770
|
-
cc: action.cc,
|
|
2771
|
-
bcc: action.bcc,
|
|
2772
|
-
from: action.from,
|
|
2773
|
-
delayMinutes: action.delayMinutes,
|
|
2774
|
-
};
|
|
2775
|
-
await this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
|
|
2776
|
-
method: 'POST',
|
|
2777
|
-
body: JSON.stringify(payload),
|
|
2778
|
-
});
|
|
2779
|
-
}
|
|
2780
|
-
/**
|
|
2781
|
-
* Execute webhook action
|
|
2782
|
-
*/
|
|
2783
|
-
async executeWebhook(action, data) {
|
|
2784
|
-
logger.debug('Calling webhook:', action.url);
|
|
2785
|
-
const body = action.body ? this.replaceVariables(action.body, data) : JSON.stringify(data);
|
|
2786
|
-
await fetch(action.url, {
|
|
2787
|
-
method: action.method,
|
|
2788
|
-
headers: {
|
|
2789
|
-
'Content-Type': 'application/json',
|
|
2790
|
-
...action.headers,
|
|
2791
|
-
},
|
|
2792
|
-
body,
|
|
2793
|
-
});
|
|
2794
|
-
}
|
|
2795
|
-
/**
|
|
2796
|
-
* Execute create task action
|
|
2797
|
-
*/
|
|
2798
|
-
async executeCreateTask(action, data) {
|
|
2799
|
-
logger.debug('Creating task:', action.title);
|
|
2800
|
-
const dueDate = action.dueDays
|
|
2801
|
-
? new Date(Date.now() + action.dueDays * 24 * 60 * 60 * 1000).toISOString()
|
|
2802
|
-
: undefined;
|
|
2803
|
-
await this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
|
|
2804
|
-
method: 'POST',
|
|
2805
|
-
body: JSON.stringify({
|
|
2806
|
-
title: this.replaceVariables(action.title, data),
|
|
2807
|
-
description: action.description ? this.replaceVariables(action.description, data) : undefined,
|
|
2808
|
-
priority: action.priority,
|
|
2809
|
-
dueDate,
|
|
2810
|
-
assignedTo: action.assignedTo,
|
|
2811
|
-
relatedContactId: typeof data.contactId === 'string' ? data.contactId : undefined,
|
|
2812
|
-
}),
|
|
2813
|
-
});
|
|
2814
|
-
}
|
|
2815
|
-
/**
|
|
2816
|
-
* Execute update contact action
|
|
2817
|
-
*/
|
|
2818
|
-
async executeUpdateContact(action, data) {
|
|
2819
|
-
const contactId = data.contactId || data._id;
|
|
2820
|
-
if (!contactId) {
|
|
2821
|
-
logger.warn('Cannot update contact: no contactId in data');
|
|
2822
|
-
return;
|
|
2823
|
-
}
|
|
2824
|
-
logger.debug('Updating contact:', contactId);
|
|
2825
|
-
await this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
2826
|
-
method: 'PUT',
|
|
2827
|
-
body: JSON.stringify(action.updates),
|
|
2828
|
-
});
|
|
2829
|
-
}
|
|
2830
|
-
/**
|
|
2831
|
-
* Replace variables in a string template
|
|
2832
|
-
* Supports syntax like {{contact.email}}, {{opportunity.value}}
|
|
2833
|
-
*/
|
|
2834
|
-
replaceVariables(template, data) {
|
|
2835
|
-
return template.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
|
|
2836
|
-
const value = this.getNestedValue(data, path.trim());
|
|
2837
|
-
return value !== undefined ? String(value) : match;
|
|
2838
|
-
});
|
|
2839
|
-
}
|
|
2840
|
-
/**
|
|
2841
|
-
* Get nested value from object using dot notation
|
|
2842
|
-
* Supports dynamic field access including custom fields
|
|
2843
|
-
*/
|
|
2844
|
-
getNestedValue(obj, path) {
|
|
2845
|
-
return path.split('.').reduce((current, key) => {
|
|
2846
|
-
return current !== null && current !== undefined && typeof current === 'object'
|
|
2847
|
-
? current[key]
|
|
2848
|
-
: undefined;
|
|
2849
|
-
}, obj);
|
|
2850
|
-
}
|
|
2851
|
-
/**
|
|
2852
|
-
* Extract all available field paths from a data object
|
|
2853
|
-
* Useful for dynamic field discovery based on platform-specific attributes
|
|
2854
|
-
* @param obj - The data object to extract fields from
|
|
2855
|
-
* @param prefix - Internal use for nested paths
|
|
2856
|
-
* @param maxDepth - Maximum depth to traverse (default: 3)
|
|
2857
|
-
* @returns Array of field paths (e.g., ['email', 'contact.firstName', 'customFields.industry'])
|
|
2858
|
-
*/
|
|
2859
|
-
extractAvailableFields(obj, prefix = '', maxDepth = 3) {
|
|
2860
|
-
if (maxDepth <= 0)
|
|
2861
|
-
return [];
|
|
2862
|
-
const fields = [];
|
|
2863
|
-
for (const key in obj) {
|
|
2864
|
-
if (!obj.hasOwnProperty(key))
|
|
2865
|
-
continue;
|
|
2866
|
-
const value = obj[key];
|
|
2867
|
-
const fieldPath = prefix ? `${prefix}.${key}` : key;
|
|
2868
|
-
fields.push(fieldPath);
|
|
2869
|
-
// Recursively traverse nested objects
|
|
2870
|
-
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
2871
|
-
const nestedFields = this.extractAvailableFields(value, fieldPath, maxDepth - 1);
|
|
2872
|
-
fields.push(...nestedFields);
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2875
|
-
return fields;
|
|
2876
|
-
}
|
|
2877
|
-
/**
|
|
2878
|
-
* Get available fields from sample data
|
|
2879
|
-
* Helps with dynamic field detection for platform-specific attributes
|
|
2880
|
-
* @param sampleData - Sample data object to analyze
|
|
2881
|
-
* @returns Array of available field paths
|
|
2882
|
-
*/
|
|
2883
|
-
getAvailableFields(sampleData) {
|
|
2884
|
-
return this.extractAvailableFields(sampleData);
|
|
2885
|
-
}
|
|
2886
|
-
// ============================================
|
|
2887
|
-
// HELPER METHODS FOR COMMON PATTERNS
|
|
2888
|
-
// ============================================
|
|
2889
|
-
/**
|
|
2890
|
-
* Create a simple email trigger
|
|
2891
|
-
* Helper method for common use case
|
|
2892
|
-
*/
|
|
2893
|
-
async createEmailTrigger(config) {
|
|
2894
|
-
return this.createTrigger({
|
|
2895
|
-
name: config.name,
|
|
2896
|
-
eventType: config.eventType,
|
|
2897
|
-
conditions: config.conditions,
|
|
2898
|
-
actions: [
|
|
2899
|
-
{
|
|
2900
|
-
type: 'send_email',
|
|
2901
|
-
to: config.to,
|
|
2902
|
-
subject: config.subject,
|
|
2903
|
-
body: config.body,
|
|
2904
|
-
},
|
|
2905
|
-
],
|
|
2906
|
-
isActive: true,
|
|
2907
|
-
});
|
|
2908
|
-
}
|
|
2909
|
-
/**
|
|
2910
|
-
* Create a task creation trigger
|
|
2911
|
-
*/
|
|
2912
|
-
async createTaskTrigger(config) {
|
|
2913
|
-
return this.createTrigger({
|
|
2914
|
-
name: config.name,
|
|
2915
|
-
eventType: config.eventType,
|
|
2916
|
-
conditions: config.conditions,
|
|
2917
|
-
actions: [
|
|
2918
|
-
{
|
|
2919
|
-
type: 'create_task',
|
|
2920
|
-
title: config.taskTitle,
|
|
2921
|
-
description: config.taskDescription,
|
|
2922
|
-
priority: config.priority,
|
|
2923
|
-
dueDays: config.dueDays,
|
|
2924
|
-
},
|
|
2925
|
-
],
|
|
2926
|
-
isActive: true,
|
|
2927
|
-
});
|
|
2928
|
-
}
|
|
2929
|
-
/**
|
|
2930
|
-
* Create a webhook trigger
|
|
2931
|
-
*/
|
|
2932
|
-
async createWebhookTrigger(config) {
|
|
2933
|
-
return this.createTrigger({
|
|
2934
|
-
name: config.name,
|
|
2935
|
-
eventType: config.eventType,
|
|
2936
|
-
conditions: config.conditions,
|
|
2937
|
-
actions: [
|
|
2938
|
-
{
|
|
2939
|
-
type: 'webhook',
|
|
2940
|
-
url: config.webhookUrl,
|
|
2941
|
-
method: config.method || 'POST',
|
|
2942
|
-
},
|
|
2943
|
-
],
|
|
2944
|
-
isActive: true,
|
|
2945
|
-
});
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
|
|
2949
|
-
/**
|
|
2950
|
-
* Clianta SDK - CRM API Client
|
|
2951
|
-
* @see SDK_VERSION in core/config.ts
|
|
2952
|
-
*/
|
|
2953
|
-
/**
|
|
2954
|
-
* CRM API Client for managing contacts and opportunities
|
|
2955
|
-
*/
|
|
2956
|
-
class CRMClient {
|
|
2957
|
-
constructor(apiEndpoint, workspaceId, authToken, apiKey) {
|
|
2958
|
-
this.apiEndpoint = apiEndpoint;
|
|
2959
|
-
this.workspaceId = workspaceId;
|
|
2960
|
-
this.authToken = authToken;
|
|
2961
|
-
this.apiKey = apiKey;
|
|
2962
|
-
this.triggers = new EventTriggersManager(apiEndpoint, workspaceId, authToken);
|
|
2963
|
-
}
|
|
2964
|
-
/**
|
|
2965
|
-
* Set authentication token for API requests (user JWT)
|
|
2966
|
-
*/
|
|
2967
|
-
setAuthToken(token) {
|
|
2968
|
-
this.authToken = token;
|
|
2969
|
-
this.apiKey = undefined;
|
|
2970
|
-
this.triggers.setAuthToken(token);
|
|
2971
|
-
}
|
|
2972
|
-
/**
|
|
2973
|
-
* Set workspace API key for server-to-server requests.
|
|
2974
|
-
* Use this instead of setAuthToken when integrating from an external app.
|
|
2975
|
-
*/
|
|
2976
|
-
setApiKey(key) {
|
|
2977
|
-
this.apiKey = key;
|
|
2978
|
-
this.authToken = undefined;
|
|
2979
|
-
}
|
|
2980
|
-
/**
|
|
2981
|
-
* Validate required parameter exists
|
|
2982
|
-
* @throws {Error} if value is null/undefined or empty string
|
|
2983
|
-
*/
|
|
2984
|
-
validateRequired(param, value, methodName) {
|
|
2985
|
-
if (value === null || value === undefined || value === '') {
|
|
2986
|
-
throw new Error(`[CRMClient.${methodName}] ${param} is required`);
|
|
2987
|
-
}
|
|
2988
|
-
}
|
|
2989
|
-
/**
|
|
2990
|
-
* Make authenticated API request
|
|
2991
|
-
*/
|
|
2992
|
-
async request(endpoint, options = {}) {
|
|
2993
|
-
const url = `${this.apiEndpoint}${endpoint}`;
|
|
2994
|
-
const headers = {
|
|
2995
|
-
'Content-Type': 'application/json',
|
|
2996
|
-
...(options.headers || {}),
|
|
2997
|
-
};
|
|
2998
|
-
if (this.apiKey) {
|
|
2999
|
-
headers['X-Api-Key'] = this.apiKey;
|
|
3000
|
-
}
|
|
3001
|
-
else if (this.authToken) {
|
|
3002
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
3003
|
-
}
|
|
3004
|
-
try {
|
|
3005
|
-
const response = await fetch(url, {
|
|
3006
|
-
...options,
|
|
3007
|
-
headers,
|
|
3008
|
-
});
|
|
3009
|
-
const data = await response.json();
|
|
3010
|
-
if (!response.ok) {
|
|
3011
|
-
return {
|
|
3012
|
-
success: false,
|
|
3013
|
-
error: data.message || 'Request failed',
|
|
3014
|
-
status: response.status,
|
|
3015
|
-
};
|
|
3016
|
-
}
|
|
3017
|
-
return {
|
|
3018
|
-
success: true,
|
|
3019
|
-
data: data.data || data,
|
|
3020
|
-
status: response.status,
|
|
3021
|
-
};
|
|
3022
|
-
}
|
|
3023
|
-
catch (error) {
|
|
3024
|
-
return {
|
|
3025
|
-
success: false,
|
|
3026
|
-
error: error instanceof Error ? error.message : 'Network error',
|
|
3027
|
-
status: 0,
|
|
3028
|
-
};
|
|
3029
|
-
}
|
|
3030
|
-
}
|
|
3031
|
-
// ============================================
|
|
3032
|
-
// INBOUND EVENTS API (API-key authenticated)
|
|
3033
|
-
// ============================================
|
|
3034
|
-
/**
|
|
3035
|
-
* Send an inbound event from an external app (e.g. user signup on client website).
|
|
3036
|
-
* Requires the client to be initialized with an API key via setApiKey() or the constructor.
|
|
3037
|
-
*
|
|
3038
|
-
* The contact is upserted in the CRM and matching workflow automations fire automatically.
|
|
3039
|
-
*
|
|
3040
|
-
* @example
|
|
3041
|
-
* const crm = new CRMClient('http://localhost:5000', 'WORKSPACE_ID');
|
|
3042
|
-
* crm.setApiKey('mm_live_...');
|
|
3043
|
-
*
|
|
3044
|
-
* await crm.sendEvent({
|
|
3045
|
-
* event: 'user.registered',
|
|
3046
|
-
* contact: { email: 'alice@example.com', firstName: 'Alice' },
|
|
3047
|
-
* data: { plan: 'free', signupSource: 'homepage' },
|
|
3048
|
-
* });
|
|
3049
|
-
*/
|
|
3050
|
-
async sendEvent(payload) {
|
|
3051
|
-
const url = `${this.apiEndpoint}/api/public/events`;
|
|
3052
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
3053
|
-
if (this.apiKey) {
|
|
3054
|
-
headers['X-Api-Key'] = this.apiKey;
|
|
3055
|
-
}
|
|
3056
|
-
else if (this.authToken) {
|
|
3057
|
-
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
3058
|
-
}
|
|
3059
|
-
try {
|
|
3060
|
-
const response = await fetch(url, {
|
|
3061
|
-
method: 'POST',
|
|
3062
|
-
headers,
|
|
3063
|
-
body: JSON.stringify(payload),
|
|
3064
|
-
});
|
|
3065
|
-
const data = await response.json();
|
|
3066
|
-
if (!response.ok) {
|
|
3067
|
-
return {
|
|
3068
|
-
success: false,
|
|
3069
|
-
contactCreated: false,
|
|
3070
|
-
event: payload.event,
|
|
3071
|
-
error: data.error || 'Request failed',
|
|
3072
|
-
};
|
|
3073
|
-
}
|
|
3074
|
-
return {
|
|
3075
|
-
success: data.success,
|
|
3076
|
-
contactCreated: data.contactCreated,
|
|
3077
|
-
contactId: data.contactId,
|
|
3078
|
-
event: data.event,
|
|
3079
|
-
};
|
|
3080
|
-
}
|
|
3081
|
-
catch (error) {
|
|
3082
|
-
return {
|
|
3083
|
-
success: false,
|
|
3084
|
-
contactCreated: false,
|
|
3085
|
-
event: payload.event,
|
|
3086
|
-
error: error instanceof Error ? error.message : 'Network error',
|
|
3087
|
-
};
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
// ============================================
|
|
3091
|
-
// CONTACTS API
|
|
3092
|
-
// ============================================
|
|
3093
|
-
/**
|
|
3094
|
-
* Get all contacts with pagination
|
|
3095
|
-
*/
|
|
3096
|
-
async getContacts(params) {
|
|
3097
|
-
const queryParams = new URLSearchParams();
|
|
3098
|
-
if (params?.page)
|
|
3099
|
-
queryParams.set('page', params.page.toString());
|
|
3100
|
-
if (params?.limit)
|
|
3101
|
-
queryParams.set('limit', params.limit.toString());
|
|
3102
|
-
if (params?.search)
|
|
3103
|
-
queryParams.set('search', params.search);
|
|
3104
|
-
if (params?.status)
|
|
3105
|
-
queryParams.set('status', params.status);
|
|
3106
|
-
const query = queryParams.toString();
|
|
3107
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts${query ? `?${query}` : ''}`;
|
|
3108
|
-
return this.request(endpoint);
|
|
3109
|
-
}
|
|
3110
|
-
/**
|
|
3111
|
-
* Get a single contact by ID
|
|
3112
|
-
*/
|
|
3113
|
-
async getContact(contactId) {
|
|
3114
|
-
this.validateRequired('contactId', contactId, 'getContact');
|
|
3115
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`);
|
|
3116
|
-
}
|
|
3117
|
-
/**
|
|
3118
|
-
* Create a new contact
|
|
3119
|
-
*/
|
|
3120
|
-
async createContact(contact) {
|
|
3121
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts`, {
|
|
3122
|
-
method: 'POST',
|
|
3123
|
-
body: JSON.stringify(contact),
|
|
3124
|
-
});
|
|
3125
|
-
}
|
|
3126
|
-
/**
|
|
3127
|
-
* Update an existing contact
|
|
3128
|
-
*/
|
|
3129
|
-
async updateContact(contactId, updates) {
|
|
3130
|
-
this.validateRequired('contactId', contactId, 'updateContact');
|
|
3131
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
3132
|
-
method: 'PUT',
|
|
3133
|
-
body: JSON.stringify(updates),
|
|
3134
|
-
});
|
|
3135
|
-
}
|
|
3136
|
-
/**
|
|
3137
|
-
* Delete a contact
|
|
3138
|
-
*/
|
|
3139
|
-
async deleteContact(contactId) {
|
|
3140
|
-
this.validateRequired('contactId', contactId, 'deleteContact');
|
|
3141
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}`, {
|
|
3142
|
-
method: 'DELETE',
|
|
3143
|
-
});
|
|
3144
|
-
}
|
|
3145
|
-
// ============================================
|
|
3146
|
-
// OPPORTUNITIES API
|
|
3147
|
-
// ============================================
|
|
3148
|
-
/**
|
|
3149
|
-
* Get all opportunities with pagination
|
|
3150
|
-
*/
|
|
3151
|
-
async getOpportunities(params) {
|
|
3152
|
-
const queryParams = new URLSearchParams();
|
|
3153
|
-
if (params?.page)
|
|
3154
|
-
queryParams.set('page', params.page.toString());
|
|
3155
|
-
if (params?.limit)
|
|
3156
|
-
queryParams.set('limit', params.limit.toString());
|
|
3157
|
-
if (params?.pipelineId)
|
|
3158
|
-
queryParams.set('pipelineId', params.pipelineId);
|
|
3159
|
-
if (params?.stageId)
|
|
3160
|
-
queryParams.set('stageId', params.stageId);
|
|
3161
|
-
const query = queryParams.toString();
|
|
3162
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/opportunities${query ? `?${query}` : ''}`;
|
|
3163
|
-
return this.request(endpoint);
|
|
3164
|
-
}
|
|
3165
|
-
/**
|
|
3166
|
-
* Get a single opportunity by ID
|
|
3167
|
-
*/
|
|
3168
|
-
async getOpportunity(opportunityId) {
|
|
3169
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`);
|
|
3170
|
-
}
|
|
3171
|
-
/**
|
|
3172
|
-
* Create a new opportunity
|
|
3173
|
-
*/
|
|
3174
|
-
async createOpportunity(opportunity) {
|
|
3175
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities`, {
|
|
3176
|
-
method: 'POST',
|
|
3177
|
-
body: JSON.stringify(opportunity),
|
|
3178
|
-
});
|
|
3179
|
-
}
|
|
3180
|
-
/**
|
|
3181
|
-
* Update an existing opportunity
|
|
3182
|
-
*/
|
|
3183
|
-
async updateOpportunity(opportunityId, updates) {
|
|
3184
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
|
|
3185
|
-
method: 'PUT',
|
|
3186
|
-
body: JSON.stringify(updates),
|
|
3187
|
-
});
|
|
3188
|
-
}
|
|
3189
|
-
/**
|
|
3190
|
-
* Delete an opportunity
|
|
3191
|
-
*/
|
|
3192
|
-
async deleteOpportunity(opportunityId) {
|
|
3193
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}`, {
|
|
3194
|
-
method: 'DELETE',
|
|
3195
|
-
});
|
|
3196
|
-
}
|
|
3197
|
-
/**
|
|
3198
|
-
* Move opportunity to a different stage
|
|
3199
|
-
*/
|
|
3200
|
-
async moveOpportunity(opportunityId, stageId) {
|
|
3201
|
-
return this.request(`/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/move`, {
|
|
3202
|
-
method: 'POST',
|
|
3203
|
-
body: JSON.stringify({ stageId }),
|
|
3204
|
-
});
|
|
3205
|
-
}
|
|
3206
|
-
// ============================================
|
|
3207
|
-
// COMPANIES API
|
|
3208
|
-
// ============================================
|
|
3209
|
-
/**
|
|
3210
|
-
* Get all companies with pagination
|
|
3211
|
-
*/
|
|
3212
|
-
async getCompanies(params) {
|
|
3213
|
-
const queryParams = new URLSearchParams();
|
|
3214
|
-
if (params?.page)
|
|
3215
|
-
queryParams.set('page', params.page.toString());
|
|
3216
|
-
if (params?.limit)
|
|
3217
|
-
queryParams.set('limit', params.limit.toString());
|
|
3218
|
-
if (params?.search)
|
|
3219
|
-
queryParams.set('search', params.search);
|
|
3220
|
-
if (params?.status)
|
|
3221
|
-
queryParams.set('status', params.status);
|
|
3222
|
-
if (params?.industry)
|
|
3223
|
-
queryParams.set('industry', params.industry);
|
|
3224
|
-
const query = queryParams.toString();
|
|
3225
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/companies${query ? `?${query}` : ''}`;
|
|
3226
|
-
return this.request(endpoint);
|
|
3227
|
-
}
|
|
3228
|
-
/**
|
|
3229
|
-
* Get a single company by ID
|
|
3230
|
-
*/
|
|
3231
|
-
async getCompany(companyId) {
|
|
3232
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`);
|
|
3233
|
-
}
|
|
3234
|
-
/**
|
|
3235
|
-
* Create a new company
|
|
3236
|
-
*/
|
|
3237
|
-
async createCompany(company) {
|
|
3238
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies`, {
|
|
3239
|
-
method: 'POST',
|
|
3240
|
-
body: JSON.stringify(company),
|
|
3241
|
-
});
|
|
3242
|
-
}
|
|
3243
|
-
/**
|
|
3244
|
-
* Update an existing company
|
|
3245
|
-
*/
|
|
3246
|
-
async updateCompany(companyId, updates) {
|
|
3247
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
|
|
3248
|
-
method: 'PUT',
|
|
3249
|
-
body: JSON.stringify(updates),
|
|
3250
|
-
});
|
|
3251
|
-
}
|
|
3252
|
-
/**
|
|
3253
|
-
* Delete a company
|
|
3254
|
-
*/
|
|
3255
|
-
async deleteCompany(companyId) {
|
|
3256
|
-
return this.request(`/api/workspaces/${this.workspaceId}/companies/${companyId}`, {
|
|
3257
|
-
method: 'DELETE',
|
|
3258
|
-
});
|
|
3259
|
-
}
|
|
3260
|
-
/**
|
|
3261
|
-
* Get contacts belonging to a company
|
|
3262
|
-
*/
|
|
3263
|
-
async getCompanyContacts(companyId, params) {
|
|
3264
|
-
const queryParams = new URLSearchParams();
|
|
3265
|
-
if (params?.page)
|
|
3266
|
-
queryParams.set('page', params.page.toString());
|
|
3267
|
-
if (params?.limit)
|
|
3268
|
-
queryParams.set('limit', params.limit.toString());
|
|
3269
|
-
const query = queryParams.toString();
|
|
3270
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/contacts${query ? `?${query}` : ''}`;
|
|
3271
|
-
return this.request(endpoint);
|
|
3272
|
-
}
|
|
3273
|
-
/**
|
|
3274
|
-
* Get deals/opportunities belonging to a company
|
|
3275
|
-
*/
|
|
3276
|
-
async getCompanyDeals(companyId, params) {
|
|
3277
|
-
const queryParams = new URLSearchParams();
|
|
3278
|
-
if (params?.page)
|
|
3279
|
-
queryParams.set('page', params.page.toString());
|
|
3280
|
-
if (params?.limit)
|
|
3281
|
-
queryParams.set('limit', params.limit.toString());
|
|
3282
|
-
const query = queryParams.toString();
|
|
3283
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/companies/${companyId}/deals${query ? `?${query}` : ''}`;
|
|
3284
|
-
return this.request(endpoint);
|
|
3285
|
-
}
|
|
3286
|
-
// ============================================
|
|
3287
|
-
// PIPELINES API
|
|
3288
|
-
// ============================================
|
|
3289
|
-
/**
|
|
3290
|
-
* Get all pipelines
|
|
3291
|
-
*/
|
|
3292
|
-
async getPipelines() {
|
|
3293
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines`);
|
|
3294
|
-
}
|
|
3295
|
-
/**
|
|
3296
|
-
* Get a single pipeline by ID
|
|
3297
|
-
*/
|
|
3298
|
-
async getPipeline(pipelineId) {
|
|
3299
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`);
|
|
3300
|
-
}
|
|
3301
|
-
/**
|
|
3302
|
-
* Create a new pipeline
|
|
3303
|
-
*/
|
|
3304
|
-
async createPipeline(pipeline) {
|
|
3305
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines`, {
|
|
3306
|
-
method: 'POST',
|
|
3307
|
-
body: JSON.stringify(pipeline),
|
|
3308
|
-
});
|
|
3309
|
-
}
|
|
3310
|
-
/**
|
|
3311
|
-
* Update an existing pipeline
|
|
3312
|
-
*/
|
|
3313
|
-
async updatePipeline(pipelineId, updates) {
|
|
3314
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
|
|
3315
|
-
method: 'PUT',
|
|
3316
|
-
body: JSON.stringify(updates),
|
|
3317
|
-
});
|
|
3318
|
-
}
|
|
3319
|
-
/**
|
|
3320
|
-
* Delete a pipeline
|
|
3321
|
-
*/
|
|
3322
|
-
async deletePipeline(pipelineId) {
|
|
3323
|
-
return this.request(`/api/workspaces/${this.workspaceId}/pipelines/${pipelineId}`, {
|
|
3324
|
-
method: 'DELETE',
|
|
3325
|
-
});
|
|
3326
|
-
}
|
|
3327
|
-
// ============================================
|
|
3328
|
-
// TASKS API
|
|
3329
|
-
// ============================================
|
|
3330
|
-
/**
|
|
3331
|
-
* Get all tasks with pagination
|
|
3332
|
-
*/
|
|
3333
|
-
async getTasks(params) {
|
|
3334
|
-
const queryParams = new URLSearchParams();
|
|
3335
|
-
if (params?.page)
|
|
3336
|
-
queryParams.set('page', params.page.toString());
|
|
3337
|
-
if (params?.limit)
|
|
3338
|
-
queryParams.set('limit', params.limit.toString());
|
|
3339
|
-
if (params?.status)
|
|
3340
|
-
queryParams.set('status', params.status);
|
|
3341
|
-
if (params?.priority)
|
|
3342
|
-
queryParams.set('priority', params.priority);
|
|
3343
|
-
if (params?.contactId)
|
|
3344
|
-
queryParams.set('contactId', params.contactId);
|
|
3345
|
-
if (params?.companyId)
|
|
3346
|
-
queryParams.set('companyId', params.companyId);
|
|
3347
|
-
if (params?.opportunityId)
|
|
3348
|
-
queryParams.set('opportunityId', params.opportunityId);
|
|
3349
|
-
const query = queryParams.toString();
|
|
3350
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/tasks${query ? `?${query}` : ''}`;
|
|
3351
|
-
return this.request(endpoint);
|
|
3352
|
-
}
|
|
3353
|
-
/**
|
|
3354
|
-
* Get a single task by ID
|
|
3355
|
-
*/
|
|
3356
|
-
async getTask(taskId) {
|
|
3357
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`);
|
|
3358
|
-
}
|
|
3359
|
-
/**
|
|
3360
|
-
* Create a new task
|
|
3361
|
-
*/
|
|
3362
|
-
async createTask(task) {
|
|
3363
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks`, {
|
|
3364
|
-
method: 'POST',
|
|
3365
|
-
body: JSON.stringify(task),
|
|
3366
|
-
});
|
|
3367
|
-
}
|
|
3368
|
-
/**
|
|
3369
|
-
* Update an existing task
|
|
3370
|
-
*/
|
|
3371
|
-
async updateTask(taskId, updates) {
|
|
3372
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
|
|
3373
|
-
method: 'PUT',
|
|
3374
|
-
body: JSON.stringify(updates),
|
|
3375
|
-
});
|
|
3376
|
-
}
|
|
3377
|
-
/**
|
|
3378
|
-
* Mark a task as completed
|
|
3379
|
-
*/
|
|
3380
|
-
async completeTask(taskId) {
|
|
3381
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}/complete`, {
|
|
3382
|
-
method: 'PATCH',
|
|
3383
|
-
});
|
|
3384
|
-
}
|
|
3385
|
-
/**
|
|
3386
|
-
* Delete a task
|
|
3387
|
-
*/
|
|
3388
|
-
async deleteTask(taskId) {
|
|
3389
|
-
return this.request(`/api/workspaces/${this.workspaceId}/tasks/${taskId}`, {
|
|
3390
|
-
method: 'DELETE',
|
|
3391
|
-
});
|
|
3392
|
-
}
|
|
3393
|
-
// ============================================
|
|
3394
|
-
// ACTIVITIES API
|
|
3395
|
-
// ============================================
|
|
3396
|
-
/**
|
|
3397
|
-
* Get activities for a contact
|
|
3398
|
-
*/
|
|
3399
|
-
async getContactActivities(contactId, params) {
|
|
3400
|
-
const queryParams = new URLSearchParams();
|
|
3401
|
-
if (params?.page)
|
|
3402
|
-
queryParams.set('page', params.page.toString());
|
|
3403
|
-
if (params?.limit)
|
|
3404
|
-
queryParams.set('limit', params.limit.toString());
|
|
3405
|
-
if (params?.type)
|
|
3406
|
-
queryParams.set('type', params.type);
|
|
3407
|
-
const query = queryParams.toString();
|
|
3408
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
|
|
3409
|
-
return this.request(endpoint);
|
|
3410
|
-
}
|
|
3411
|
-
/**
|
|
3412
|
-
* Get activities for an opportunity/deal
|
|
3413
|
-
*/
|
|
3414
|
-
async getOpportunityActivities(opportunityId, params) {
|
|
3415
|
-
const queryParams = new URLSearchParams();
|
|
3416
|
-
if (params?.page)
|
|
3417
|
-
queryParams.set('page', params.page.toString());
|
|
3418
|
-
if (params?.limit)
|
|
3419
|
-
queryParams.set('limit', params.limit.toString());
|
|
3420
|
-
if (params?.type)
|
|
3421
|
-
queryParams.set('type', params.type);
|
|
3422
|
-
const query = queryParams.toString();
|
|
3423
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${opportunityId}/activities${query ? `?${query}` : ''}`;
|
|
3424
|
-
return this.request(endpoint);
|
|
3425
|
-
}
|
|
3426
|
-
/**
|
|
3427
|
-
* Create a new activity
|
|
3428
|
-
*/
|
|
3429
|
-
async createActivity(activity) {
|
|
3430
|
-
// Determine the correct endpoint based on related entity
|
|
3431
|
-
let endpoint;
|
|
3432
|
-
if (activity.opportunityId) {
|
|
3433
|
-
endpoint = `/api/workspaces/${this.workspaceId}/opportunities/${activity.opportunityId}/activities`;
|
|
3434
|
-
}
|
|
3435
|
-
else if (activity.contactId) {
|
|
3436
|
-
endpoint = `/api/workspaces/${this.workspaceId}/contacts/${activity.contactId}/activities`;
|
|
3437
|
-
}
|
|
3438
|
-
else {
|
|
3439
|
-
endpoint = `/api/workspaces/${this.workspaceId}/activities`;
|
|
3440
|
-
}
|
|
3441
|
-
return this.request(endpoint, {
|
|
3442
|
-
method: 'POST',
|
|
3443
|
-
body: JSON.stringify(activity),
|
|
3444
|
-
});
|
|
3445
|
-
}
|
|
3446
|
-
/**
|
|
3447
|
-
* Update an existing activity
|
|
3448
|
-
*/
|
|
3449
|
-
async updateActivity(activityId, updates) {
|
|
3450
|
-
return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
|
|
3451
|
-
method: 'PATCH',
|
|
3452
|
-
body: JSON.stringify(updates),
|
|
3453
|
-
});
|
|
3454
|
-
}
|
|
3455
|
-
/**
|
|
3456
|
-
* Delete an activity
|
|
3457
|
-
*/
|
|
3458
|
-
async deleteActivity(activityId) {
|
|
3459
|
-
return this.request(`/api/workspaces/${this.workspaceId}/activities/${activityId}`, {
|
|
3460
|
-
method: 'DELETE',
|
|
3461
|
-
});
|
|
3462
|
-
}
|
|
3463
|
-
/**
|
|
3464
|
-
* Log a call activity
|
|
3465
|
-
*/
|
|
3466
|
-
async logCall(data) {
|
|
3467
|
-
return this.createActivity({
|
|
3468
|
-
type: 'call',
|
|
3469
|
-
title: `${data.direction === 'inbound' ? 'Inbound' : 'Outbound'} Call`,
|
|
3470
|
-
direction: data.direction,
|
|
3471
|
-
duration: data.duration,
|
|
3472
|
-
outcome: data.outcome,
|
|
3473
|
-
description: data.notes,
|
|
3474
|
-
contactId: data.contactId,
|
|
3475
|
-
opportunityId: data.opportunityId,
|
|
3476
|
-
});
|
|
3477
|
-
}
|
|
3478
|
-
/**
|
|
3479
|
-
* Log a meeting activity
|
|
3480
|
-
*/
|
|
3481
|
-
async logMeeting(data) {
|
|
3482
|
-
return this.createActivity({
|
|
3483
|
-
type: 'meeting',
|
|
3484
|
-
title: data.title,
|
|
3485
|
-
duration: data.duration,
|
|
3486
|
-
outcome: data.outcome,
|
|
3487
|
-
description: data.notes,
|
|
3488
|
-
contactId: data.contactId,
|
|
3489
|
-
opportunityId: data.opportunityId,
|
|
3490
|
-
});
|
|
3491
|
-
}
|
|
3492
|
-
/**
|
|
3493
|
-
* Add a note to a contact or opportunity
|
|
3494
|
-
*/
|
|
3495
|
-
async addNote(data) {
|
|
3496
|
-
return this.createActivity({
|
|
3497
|
-
type: 'note',
|
|
3498
|
-
title: 'Note',
|
|
3499
|
-
description: data.content,
|
|
3500
|
-
contactId: data.contactId,
|
|
3501
|
-
opportunityId: data.opportunityId,
|
|
3502
|
-
});
|
|
3503
|
-
}
|
|
3504
|
-
// ============================================
|
|
3505
|
-
// EMAIL TEMPLATES API
|
|
3506
|
-
// ============================================
|
|
3507
|
-
/**
|
|
3508
|
-
* Get all email templates
|
|
3509
|
-
*/
|
|
3510
|
-
async getEmailTemplates(params) {
|
|
3511
|
-
const queryParams = new URLSearchParams();
|
|
3512
|
-
if (params?.page)
|
|
3513
|
-
queryParams.set('page', params.page.toString());
|
|
3514
|
-
if (params?.limit)
|
|
3515
|
-
queryParams.set('limit', params.limit.toString());
|
|
3516
|
-
const query = queryParams.toString();
|
|
3517
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/email-templates${query ? `?${query}` : ''}`;
|
|
3518
|
-
return this.request(endpoint);
|
|
3519
|
-
}
|
|
3520
|
-
/**
|
|
3521
|
-
* Get a single email template by ID
|
|
3522
|
-
*/
|
|
3523
|
-
async getEmailTemplate(templateId) {
|
|
3524
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`);
|
|
3525
|
-
}
|
|
3526
|
-
/**
|
|
3527
|
-
* Create a new email template
|
|
3528
|
-
*/
|
|
3529
|
-
async createEmailTemplate(template) {
|
|
3530
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates`, {
|
|
3531
|
-
method: 'POST',
|
|
3532
|
-
body: JSON.stringify(template),
|
|
3533
|
-
});
|
|
3534
|
-
}
|
|
3535
|
-
/**
|
|
3536
|
-
* Update an email template
|
|
3537
|
-
*/
|
|
3538
|
-
async updateEmailTemplate(templateId, updates) {
|
|
3539
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
|
|
3540
|
-
method: 'PUT',
|
|
3541
|
-
body: JSON.stringify(updates),
|
|
3542
|
-
});
|
|
3543
|
-
}
|
|
3544
|
-
/**
|
|
3545
|
-
* Delete an email template
|
|
3546
|
-
*/
|
|
3547
|
-
async deleteEmailTemplate(templateId) {
|
|
3548
|
-
return this.request(`/api/workspaces/${this.workspaceId}/email-templates/${templateId}`, {
|
|
3549
|
-
method: 'DELETE',
|
|
3550
|
-
});
|
|
3551
|
-
}
|
|
3552
|
-
/**
|
|
3553
|
-
* Send an email using a template
|
|
3554
|
-
*/
|
|
3555
|
-
async sendEmail(data) {
|
|
3556
|
-
return this.request(`/api/workspaces/${this.workspaceId}/emails/send`, {
|
|
3557
|
-
method: 'POST',
|
|
3558
|
-
body: JSON.stringify(data),
|
|
3559
|
-
});
|
|
3560
|
-
}
|
|
3561
|
-
// ============================================
|
|
3562
|
-
// READ-BACK / DATA RETRIEVAL API
|
|
3563
|
-
// ============================================
|
|
3564
|
-
/**
|
|
3565
|
-
* Get a contact by email address.
|
|
3566
|
-
* Returns the first matching contact from a search query.
|
|
3567
|
-
*/
|
|
3568
|
-
async getContactByEmail(email) {
|
|
3569
|
-
this.validateRequired('email', email, 'getContactByEmail');
|
|
3570
|
-
const queryParams = new URLSearchParams({ search: email, limit: '1' });
|
|
3571
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts?${queryParams.toString()}`);
|
|
3572
|
-
}
|
|
3573
|
-
/**
|
|
3574
|
-
* Get activity timeline for a contact
|
|
3575
|
-
*/
|
|
3576
|
-
async getContactActivity(contactId, params) {
|
|
3577
|
-
this.validateRequired('contactId', contactId, 'getContactActivity');
|
|
3578
|
-
const queryParams = new URLSearchParams();
|
|
3579
|
-
if (params?.page)
|
|
3580
|
-
queryParams.set('page', params.page.toString());
|
|
3581
|
-
if (params?.limit)
|
|
3582
|
-
queryParams.set('limit', params.limit.toString());
|
|
3583
|
-
if (params?.type)
|
|
3584
|
-
queryParams.set('type', params.type);
|
|
3585
|
-
if (params?.startDate)
|
|
3586
|
-
queryParams.set('startDate', params.startDate);
|
|
3587
|
-
if (params?.endDate)
|
|
3588
|
-
queryParams.set('endDate', params.endDate);
|
|
3589
|
-
const query = queryParams.toString();
|
|
3590
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/activities${query ? `?${query}` : ''}`;
|
|
3591
|
-
return this.request(endpoint);
|
|
3592
|
-
}
|
|
3593
|
-
/**
|
|
3594
|
-
* Get engagement metrics for a contact (via their linked visitor data)
|
|
3595
|
-
*/
|
|
3596
|
-
async getContactEngagement(contactId) {
|
|
3597
|
-
this.validateRequired('contactId', contactId, 'getContactEngagement');
|
|
3598
|
-
return this.request(`/api/workspaces/${this.workspaceId}/contacts/${contactId}/engagement`);
|
|
3599
|
-
}
|
|
3600
|
-
/**
|
|
3601
|
-
* Get a full timeline for a contact including events, activities, and opportunities
|
|
3602
|
-
*/
|
|
3603
|
-
async getContactTimeline(contactId, params) {
|
|
3604
|
-
this.validateRequired('contactId', contactId, 'getContactTimeline');
|
|
3605
|
-
const queryParams = new URLSearchParams();
|
|
3606
|
-
if (params?.page)
|
|
3607
|
-
queryParams.set('page', params.page.toString());
|
|
3608
|
-
if (params?.limit)
|
|
3609
|
-
queryParams.set('limit', params.limit.toString());
|
|
3610
|
-
const query = queryParams.toString();
|
|
3611
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts/${contactId}/timeline${query ? `?${query}` : ''}`;
|
|
3612
|
-
return this.request(endpoint);
|
|
3613
|
-
}
|
|
3614
|
-
/**
|
|
3615
|
-
* Search contacts with advanced filters
|
|
3616
|
-
*/
|
|
3617
|
-
async searchContacts(query, filters) {
|
|
3618
|
-
const queryParams = new URLSearchParams();
|
|
3619
|
-
queryParams.set('search', query);
|
|
3620
|
-
if (filters?.status)
|
|
3621
|
-
queryParams.set('status', filters.status);
|
|
3622
|
-
if (filters?.lifecycleStage)
|
|
3623
|
-
queryParams.set('lifecycleStage', filters.lifecycleStage);
|
|
3624
|
-
if (filters?.source)
|
|
3625
|
-
queryParams.set('source', filters.source);
|
|
3626
|
-
if (filters?.tags)
|
|
3627
|
-
queryParams.set('tags', filters.tags.join(','));
|
|
3628
|
-
if (filters?.page)
|
|
3629
|
-
queryParams.set('page', filters.page.toString());
|
|
3630
|
-
if (filters?.limit)
|
|
3631
|
-
queryParams.set('limit', filters.limit.toString());
|
|
3632
|
-
const qs = queryParams.toString();
|
|
3633
|
-
const endpoint = `/api/workspaces/${this.workspaceId}/contacts${qs ? `?${qs}` : ''}`;
|
|
3634
|
-
return this.request(endpoint);
|
|
3635
|
-
}
|
|
3636
|
-
// ============================================
|
|
3637
|
-
// WEBHOOK MANAGEMENT API
|
|
3638
|
-
// ============================================
|
|
3639
|
-
/**
|
|
3640
|
-
* List all webhook subscriptions
|
|
3641
|
-
*/
|
|
3642
|
-
async listWebhooks(params) {
|
|
3643
|
-
const queryParams = new URLSearchParams();
|
|
3644
|
-
if (params?.page)
|
|
3645
|
-
queryParams.set('page', params.page.toString());
|
|
3646
|
-
if (params?.limit)
|
|
3647
|
-
queryParams.set('limit', params.limit.toString());
|
|
3648
|
-
const query = queryParams.toString();
|
|
3649
|
-
return this.request(`/api/workspaces/${this.workspaceId}/webhooks${query ? `?${query}` : ''}`);
|
|
3650
|
-
}
|
|
3651
|
-
/**
|
|
3652
|
-
* Create a new webhook subscription
|
|
3653
|
-
*/
|
|
3654
|
-
async createWebhook(data) {
|
|
3655
|
-
return this.request(`/api/workspaces/${this.workspaceId}/webhooks`, {
|
|
3656
|
-
method: 'POST',
|
|
3657
|
-
body: JSON.stringify(data),
|
|
3658
|
-
});
|
|
3659
|
-
}
|
|
3660
|
-
/**
|
|
3661
|
-
* Delete a webhook subscription
|
|
3662
|
-
*/
|
|
3663
|
-
async deleteWebhook(webhookId) {
|
|
3664
|
-
this.validateRequired('webhookId', webhookId, 'deleteWebhook');
|
|
3665
|
-
return this.request(`/api/workspaces/${this.workspaceId}/webhooks/${webhookId}`, {
|
|
3666
|
-
method: 'DELETE',
|
|
3667
|
-
});
|
|
3668
|
-
}
|
|
3669
|
-
// ============================================
|
|
3670
|
-
// EVENT TRIGGERS API (delegated to triggers manager)
|
|
3671
|
-
// ============================================
|
|
3672
|
-
/**
|
|
3673
|
-
* Get all event triggers
|
|
3674
|
-
*/
|
|
3675
|
-
async getEventTriggers() {
|
|
3676
|
-
return this.triggers.getTriggers();
|
|
3677
|
-
}
|
|
3678
|
-
/**
|
|
3679
|
-
* Create a new event trigger
|
|
3680
|
-
*/
|
|
3681
|
-
async createEventTrigger(trigger) {
|
|
3682
|
-
return this.triggers.createTrigger(trigger);
|
|
3683
|
-
}
|
|
3684
|
-
/**
|
|
3685
|
-
* Update an event trigger
|
|
3686
|
-
*/
|
|
3687
|
-
async updateEventTrigger(triggerId, updates) {
|
|
3688
|
-
return this.triggers.updateTrigger(triggerId, updates);
|
|
3689
|
-
}
|
|
3690
|
-
/**
|
|
3691
|
-
* Delete an event trigger
|
|
3692
|
-
*/
|
|
3693
|
-
async deleteEventTrigger(triggerId) {
|
|
3694
|
-
return this.triggers.deleteTrigger(triggerId);
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
|
|
3698
|
-
/**
|
|
3699
|
-
* Privacy-safe visitor API client.
|
|
3700
|
-
* All methods return data for the current visitor only (no cross-visitor access).
|
|
3701
|
-
*/
|
|
3702
|
-
class VisitorClient {
|
|
3703
|
-
constructor(transport, workspaceId, visitorId) {
|
|
3704
|
-
this.transport = transport;
|
|
3705
|
-
this.workspaceId = workspaceId;
|
|
3706
|
-
this.visitorId = visitorId;
|
|
3707
|
-
}
|
|
3708
|
-
/** Update visitorId (e.g. after reset) */
|
|
3709
|
-
setVisitorId(id) {
|
|
3710
|
-
this.visitorId = id;
|
|
3711
|
-
}
|
|
3712
|
-
basePath() {
|
|
3713
|
-
return `/api/public/track/visitor/${this.workspaceId}/${this.visitorId}`;
|
|
3714
|
-
}
|
|
3715
|
-
/**
|
|
3716
|
-
* Get the current visitor's profile from the CRM.
|
|
3717
|
-
* Returns visitor data and linked contact info if identified.
|
|
3718
|
-
*/
|
|
3719
|
-
async getProfile() {
|
|
3720
|
-
const result = await this.transport.fetchData(`${this.basePath()}/profile`);
|
|
3721
|
-
if (result.success && result.data) {
|
|
3722
|
-
logger.debug('Visitor profile fetched:', result.data);
|
|
3723
|
-
return result.data;
|
|
3724
|
-
}
|
|
3725
|
-
logger.warn('Failed to fetch visitor profile:', result.error);
|
|
3726
|
-
return null;
|
|
3727
|
-
}
|
|
3728
|
-
/**
|
|
3729
|
-
* Get the current visitor's recent activity/events.
|
|
3730
|
-
* Returns paginated list of tracking events.
|
|
3731
|
-
*/
|
|
3732
|
-
async getActivity(options) {
|
|
3733
|
-
const params = {};
|
|
3734
|
-
if (options?.page)
|
|
3735
|
-
params.page = options.page.toString();
|
|
3736
|
-
if (options?.limit)
|
|
3737
|
-
params.limit = options.limit.toString();
|
|
3738
|
-
if (options?.eventType)
|
|
3739
|
-
params.eventType = options.eventType;
|
|
3740
|
-
if (options?.startDate)
|
|
3741
|
-
params.startDate = options.startDate;
|
|
3742
|
-
if (options?.endDate)
|
|
3743
|
-
params.endDate = options.endDate;
|
|
3744
|
-
const result = await this.transport.fetchData(`${this.basePath()}/activity`, params);
|
|
3745
|
-
if (result.success && result.data) {
|
|
3746
|
-
return result.data;
|
|
3747
|
-
}
|
|
3748
|
-
logger.warn('Failed to fetch visitor activity:', result.error);
|
|
3749
|
-
return null;
|
|
3750
|
-
}
|
|
3751
|
-
/**
|
|
3752
|
-
* Get a summarized journey timeline for the current visitor.
|
|
3753
|
-
* Includes top pages, sessions, time spent, and recent activities.
|
|
3754
|
-
*/
|
|
3755
|
-
async getTimeline() {
|
|
3756
|
-
const result = await this.transport.fetchData(`${this.basePath()}/timeline`);
|
|
3757
|
-
if (result.success && result.data) {
|
|
3758
|
-
return result.data;
|
|
3759
|
-
}
|
|
3760
|
-
logger.warn('Failed to fetch visitor timeline:', result.error);
|
|
3761
|
-
return null;
|
|
3762
|
-
}
|
|
3763
|
-
/**
|
|
3764
|
-
* Get engagement metrics for the current visitor.
|
|
3765
|
-
* Includes time on site, page views, bounce rate, and engagement score.
|
|
3766
|
-
*/
|
|
3767
|
-
async getEngagement() {
|
|
3768
|
-
const result = await this.transport.fetchData(`${this.basePath()}/engagement`);
|
|
3769
|
-
if (result.success && result.data) {
|
|
3770
|
-
return result.data;
|
|
3771
|
-
}
|
|
3772
|
-
logger.warn('Failed to fetch visitor engagement:', result.error);
|
|
3773
|
-
return null;
|
|
3774
|
-
}
|
|
3775
|
-
}
|
|
3776
|
-
|
|
3777
2515
|
/**
|
|
3778
2516
|
* Clianta SDK - Main Tracker Class
|
|
3779
2517
|
* @see SDK_VERSION in core/config.ts
|
|
@@ -3822,17 +2560,12 @@ class Tracker {
|
|
|
3822
2560
|
this.visitorId = this.createVisitorId();
|
|
3823
2561
|
this.sessionId = this.createSessionId();
|
|
3824
2562
|
logger.debug('IDs created', { visitorId: this.visitorId, sessionId: this.sessionId });
|
|
3825
|
-
// Initialize visitor API client
|
|
3826
|
-
this.visitor = new VisitorClient(this.transport, this.workspaceId, this.visitorId);
|
|
3827
2563
|
// Security warnings
|
|
3828
2564
|
if (this.config.apiEndpoint.startsWith('http://') &&
|
|
3829
2565
|
typeof window !== 'undefined' &&
|
|
3830
2566
|
!window.location.hostname.includes('localhost') &&
|
|
3831
2567
|
!window.location.hostname.includes('127.0.0.1')) {
|
|
3832
|
-
logger.warn('apiEndpoint uses HTTP — events
|
|
3833
|
-
}
|
|
3834
|
-
if (this.config.apiKey && typeof window !== 'undefined') {
|
|
3835
|
-
logger.warn('API key is exposed in client-side code. Use API keys only in server-side (Node.js) environments.');
|
|
2568
|
+
logger.warn('apiEndpoint uses HTTP — events will be sent unencrypted. Use HTTPS in production.');
|
|
3836
2569
|
}
|
|
3837
2570
|
// Initialize plugins
|
|
3838
2571
|
this.initPlugins();
|
|
@@ -4028,63 +2761,6 @@ class Tracker {
|
|
|
4028
2761
|
return null;
|
|
4029
2762
|
}
|
|
4030
2763
|
}
|
|
4031
|
-
/**
|
|
4032
|
-
* Send a server-side inbound event via the API key endpoint.
|
|
4033
|
-
* Convenience proxy to CRMClient.sendEvent() — requires apiKey in config.
|
|
4034
|
-
*/
|
|
4035
|
-
async sendEvent(payload) {
|
|
4036
|
-
const apiKey = this.config.apiKey;
|
|
4037
|
-
if (!apiKey) {
|
|
4038
|
-
logger.error('sendEvent() requires an apiKey in the SDK config');
|
|
4039
|
-
return { success: false, contactCreated: false, event: payload.event, error: 'No API key configured' };
|
|
4040
|
-
}
|
|
4041
|
-
const client = new CRMClient(this.config.apiEndpoint, this.workspaceId, undefined, apiKey);
|
|
4042
|
-
return client.sendEvent(payload);
|
|
4043
|
-
}
|
|
4044
|
-
/**
|
|
4045
|
-
* Get the current visitor's profile from the CRM.
|
|
4046
|
-
* @deprecated Use `tracker.visitor.getProfile()` instead.
|
|
4047
|
-
*/
|
|
4048
|
-
async getVisitorProfile() {
|
|
4049
|
-
if (!this.isInitialized) {
|
|
4050
|
-
logger.warn('SDK not initialized');
|
|
4051
|
-
return null;
|
|
4052
|
-
}
|
|
4053
|
-
return this.visitor.getProfile();
|
|
4054
|
-
}
|
|
4055
|
-
/**
|
|
4056
|
-
* Get the current visitor's recent activity/events.
|
|
4057
|
-
* @deprecated Use `tracker.visitor.getActivity()` instead.
|
|
4058
|
-
*/
|
|
4059
|
-
async getVisitorActivity(options) {
|
|
4060
|
-
if (!this.isInitialized) {
|
|
4061
|
-
logger.warn('SDK not initialized');
|
|
4062
|
-
return null;
|
|
4063
|
-
}
|
|
4064
|
-
return this.visitor.getActivity(options);
|
|
4065
|
-
}
|
|
4066
|
-
/**
|
|
4067
|
-
* Get a summarized journey timeline for the current visitor.
|
|
4068
|
-
* @deprecated Use `tracker.visitor.getTimeline()` instead.
|
|
4069
|
-
*/
|
|
4070
|
-
async getVisitorTimeline() {
|
|
4071
|
-
if (!this.isInitialized) {
|
|
4072
|
-
logger.warn('SDK not initialized');
|
|
4073
|
-
return null;
|
|
4074
|
-
}
|
|
4075
|
-
return this.visitor.getTimeline();
|
|
4076
|
-
}
|
|
4077
|
-
/**
|
|
4078
|
-
* Get engagement metrics for the current visitor.
|
|
4079
|
-
* @deprecated Use `tracker.visitor.getEngagement()` instead.
|
|
4080
|
-
*/
|
|
4081
|
-
async getVisitorEngagement() {
|
|
4082
|
-
if (!this.isInitialized) {
|
|
4083
|
-
logger.warn('SDK not initialized');
|
|
4084
|
-
return null;
|
|
4085
|
-
}
|
|
4086
|
-
return this.visitor.getEngagement();
|
|
4087
|
-
}
|
|
4088
2764
|
/**
|
|
4089
2765
|
* Retry pending identify call
|
|
4090
2766
|
*/
|
|
@@ -4494,7 +3170,12 @@ class Tracker {
|
|
|
4494
3170
|
|
|
4495
3171
|
/**
|
|
4496
3172
|
* Clianta SDK
|
|
4497
|
-
*
|
|
3173
|
+
* Client-side tracking SDK for CRM — tracks visitors, identifies contacts,
|
|
3174
|
+
* captures forms, and writes CRM data from client websites.
|
|
3175
|
+
*
|
|
3176
|
+
* This SDK is designed to run on CLIENT WEBSITES (React, Next.js, Vue, etc.)
|
|
3177
|
+
* It only SENDS data to your CRM — it never reads CRM data back.
|
|
3178
|
+
*
|
|
4498
3179
|
* @see SDK_VERSION in core/config.ts
|
|
4499
3180
|
*/
|
|
4500
3181
|
// Global instance cache
|
|
@@ -4536,31 +3217,55 @@ function clianta(workspaceId, config) {
|
|
|
4536
3217
|
globalInstance = new Tracker(workspaceId, config);
|
|
4537
3218
|
return globalInstance;
|
|
4538
3219
|
}
|
|
4539
|
-
// Attach to window for <script> usage
|
|
3220
|
+
// Attach to window for <script> tag usage + AUTO-INIT
|
|
4540
3221
|
if (typeof window !== 'undefined') {
|
|
4541
3222
|
window.clianta = clianta;
|
|
4542
3223
|
window.Clianta = {
|
|
4543
3224
|
clianta,
|
|
4544
3225
|
Tracker,
|
|
4545
|
-
CRMClient,
|
|
4546
3226
|
ConsentManager,
|
|
4547
|
-
EventTriggersManager,
|
|
4548
3227
|
};
|
|
3228
|
+
// ============================================
|
|
3229
|
+
// AUTO-INIT FROM SCRIPT TAG
|
|
3230
|
+
// ============================================
|
|
3231
|
+
// Enables true plug-and-play:
|
|
3232
|
+
// <script src="clianta.min.js" data-project-id="YOUR_ID"></script>
|
|
3233
|
+
// That's it — everything auto-tracks.
|
|
3234
|
+
const autoInit = () => {
|
|
3235
|
+
const scripts = document.querySelectorAll('script[data-project-id]');
|
|
3236
|
+
const script = scripts[scripts.length - 1]; // last matching script
|
|
3237
|
+
if (!script)
|
|
3238
|
+
return;
|
|
3239
|
+
const projectId = script.getAttribute('data-project-id');
|
|
3240
|
+
if (!projectId)
|
|
3241
|
+
return;
|
|
3242
|
+
const debug = script.hasAttribute('data-debug');
|
|
3243
|
+
const instance = clianta(projectId, { debug });
|
|
3244
|
+
// Expose the auto-initialized instance globally
|
|
3245
|
+
window.__clianta = instance;
|
|
3246
|
+
};
|
|
3247
|
+
// Run after DOM is ready
|
|
3248
|
+
if (document.readyState === 'loading') {
|
|
3249
|
+
document.addEventListener('DOMContentLoaded', autoInit);
|
|
3250
|
+
}
|
|
3251
|
+
else {
|
|
3252
|
+
autoInit();
|
|
3253
|
+
}
|
|
4549
3254
|
}
|
|
4550
3255
|
|
|
4551
3256
|
/**
|
|
4552
3257
|
* Clianta SDK - React Integration
|
|
4553
3258
|
*
|
|
4554
|
-
*
|
|
4555
|
-
*
|
|
3259
|
+
* TRUE PLUG-AND-PLAY: Just wrap your app with <CliantaProvider projectId="xxx" />
|
|
3260
|
+
* and everything auto-tracks — page views, forms, clicks, scroll, engagement,
|
|
3261
|
+
* downloads, exit intent, errors, performance. Zero manual code needed.
|
|
4556
3262
|
*/
|
|
4557
3263
|
const CliantaContext = react.createContext({
|
|
4558
3264
|
tracker: null,
|
|
4559
3265
|
isReady: false,
|
|
4560
3266
|
});
|
|
4561
3267
|
/**
|
|
4562
|
-
* Internal ErrorBoundary
|
|
4563
|
-
* Catches render-time errors in the provider tree.
|
|
3268
|
+
* Internal ErrorBoundary — SDK crashes never break the host app.
|
|
4564
3269
|
*/
|
|
4565
3270
|
class CliantaErrorBoundary extends react.Component {
|
|
4566
3271
|
constructor(props) {
|
|
@@ -4576,56 +3281,39 @@ class CliantaErrorBoundary extends react.Component {
|
|
|
4576
3281
|
}
|
|
4577
3282
|
render() {
|
|
4578
3283
|
if (this.state.hasError) {
|
|
4579
|
-
// Render children anyway — SDK failure shouldn't break the host UI
|
|
4580
3284
|
return this.props.fallback ?? this.props.children;
|
|
4581
3285
|
}
|
|
4582
3286
|
return this.props.children;
|
|
4583
3287
|
}
|
|
4584
3288
|
}
|
|
4585
3289
|
/**
|
|
4586
|
-
* CliantaProvider -
|
|
3290
|
+
* CliantaProvider — Plug-and-play tracking for React/Next.js
|
|
4587
3291
|
*
|
|
4588
|
-
*
|
|
3292
|
+
* Just wrap your app. Everything auto-tracks. Done.
|
|
4589
3293
|
*
|
|
4590
3294
|
* @example
|
|
4591
|
-
* //
|
|
4592
|
-
*
|
|
4593
|
-
*
|
|
4594
|
-
* const config: CliantaConfig = {
|
|
4595
|
-
* projectId: 'your-project-id',
|
|
4596
|
-
* apiEndpoint: process.env.NEXT_PUBLIC_CLIANTA_API_ENDPOINT || 'http://localhost:5000',
|
|
4597
|
-
* debug: process.env.NODE_ENV === 'development',
|
|
4598
|
-
* };
|
|
4599
|
-
*
|
|
4600
|
-
* export default config;
|
|
4601
|
-
*
|
|
4602
|
-
* // In app/layout.tsx or main.tsx:
|
|
4603
|
-
* import { CliantaProvider } from '@clianta/sdk/react';
|
|
4604
|
-
* import cliantaConfig from '../clianta.config';
|
|
4605
|
-
*
|
|
4606
|
-
* <CliantaProvider config={cliantaConfig}>
|
|
3295
|
+
* // app/layout.tsx — that's it, one line:
|
|
3296
|
+
* <CliantaProvider projectId={process.env.NEXT_PUBLIC_CLIANTA_ID!}>
|
|
4607
3297
|
* {children}
|
|
4608
3298
|
* </CliantaProvider>
|
|
4609
3299
|
*/
|
|
4610
|
-
function CliantaProvider({ config, children, onError }) {
|
|
3300
|
+
function CliantaProvider({ projectId, debug, config, children, onError }) {
|
|
4611
3301
|
const [tracker, setTracker] = react.useState(null);
|
|
4612
3302
|
const [isReady, setIsReady] = react.useState(false);
|
|
4613
|
-
|
|
4614
|
-
const projectIdRef = react.useRef(config.projectId);
|
|
3303
|
+
const projectIdRef = react.useRef(projectId);
|
|
4615
3304
|
react.useEffect(() => {
|
|
4616
|
-
// Initialize tracker with config
|
|
4617
|
-
const projectId = config.projectId;
|
|
4618
3305
|
if (!projectId) {
|
|
4619
|
-
console.error('[Clianta] Missing projectId
|
|
3306
|
+
console.error('[Clianta] Missing projectId prop on CliantaProvider');
|
|
4620
3307
|
return;
|
|
4621
3308
|
}
|
|
4622
|
-
// Only re-initialize if projectId actually changed
|
|
4623
3309
|
if (projectIdRef.current !== projectId) {
|
|
4624
3310
|
projectIdRef.current = projectId;
|
|
4625
3311
|
}
|
|
4626
3312
|
try {
|
|
4627
|
-
|
|
4628
|
-
|
|
3313
|
+
const options = {
|
|
3314
|
+
debug: debug ?? false,
|
|
3315
|
+
...config, // advanced config overrides
|
|
3316
|
+
};
|
|
4629
3317
|
const instance = clianta(projectId, options);
|
|
4630
3318
|
setTracker(instance);
|
|
4631
3319
|
setIsReady(true);
|
|
@@ -4634,48 +3322,33 @@ function CliantaProvider({ config, children, onError }) {
|
|
|
4634
3322
|
console.error('[Clianta] Failed to initialize SDK:', error);
|
|
4635
3323
|
onError?.(error, { componentStack: '' });
|
|
4636
3324
|
}
|
|
4637
|
-
// Cleanup: flush pending events on unmount
|
|
4638
3325
|
return () => {
|
|
4639
3326
|
tracker?.flush();
|
|
4640
3327
|
setIsReady(false);
|
|
4641
3328
|
};
|
|
4642
3329
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4643
|
-
}, [
|
|
3330
|
+
}, [projectId]);
|
|
4644
3331
|
return (jsxRuntime.jsx(CliantaErrorBoundary, { onError: onError, children: jsxRuntime.jsx(CliantaContext.Provider, { value: { tracker, isReady }, children: children }) }));
|
|
4645
3332
|
}
|
|
4646
3333
|
// ============================================
|
|
4647
3334
|
// HOOKS
|
|
4648
3335
|
// ============================================
|
|
4649
3336
|
/**
|
|
4650
|
-
* useClianta
|
|
4651
|
-
*
|
|
4652
|
-
* @example
|
|
4653
|
-
* const tracker = useClianta();
|
|
4654
|
-
* tracker?.track('button_click', 'CTA Button');
|
|
3337
|
+
* useClianta — Access the tracker instance
|
|
4655
3338
|
*/
|
|
4656
3339
|
function useClianta() {
|
|
4657
3340
|
const { tracker } = react.useContext(CliantaContext);
|
|
4658
3341
|
return tracker;
|
|
4659
3342
|
}
|
|
4660
3343
|
/**
|
|
4661
|
-
* useCliantaReady
|
|
4662
|
-
*
|
|
4663
|
-
* @example
|
|
4664
|
-
* const { isReady, tracker } = useCliantaReady();
|
|
4665
|
-
* if (isReady) {
|
|
4666
|
-
* tracker.track('purchase', 'Order', { value: 99 });
|
|
4667
|
-
* }
|
|
3344
|
+
* useCliantaReady — Check if SDK is initialized
|
|
4668
3345
|
*/
|
|
4669
3346
|
function useCliantaReady() {
|
|
4670
3347
|
const { tracker, isReady } = react.useContext(CliantaContext);
|
|
4671
3348
|
return { isReady, tracker };
|
|
4672
3349
|
}
|
|
4673
3350
|
/**
|
|
4674
|
-
* useCliantaTrack
|
|
4675
|
-
*
|
|
4676
|
-
* @example
|
|
4677
|
-
* const track = useCliantaTrack();
|
|
4678
|
-
* track('purchase', 'Order Completed', { orderId: '123' });
|
|
3351
|
+
* useCliantaTrack — Quick tracking hook
|
|
4679
3352
|
*/
|
|
4680
3353
|
function useCliantaTrack() {
|
|
4681
3354
|
const tracker = useClianta();
|