@encatch/web-sdk 0.0.13
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 +756 -0
- package/dist-sdk/plugin/sdk/core-wrapper-CAKT3nVS.js +24258 -0
- package/dist-sdk/plugin/sdk/module-CGKWqAgY.js +495 -0
- package/dist-sdk/plugin/sdk/module.js +5 -0
- package/dist-sdk/plugin/sdk/preview-sdk.html +1182 -0
- package/dist-sdk/plugin/sdk/vite.svg +15 -0
- package/dist-sdk/plugin/sdk/web-form-engine-core.css +1 -0
- package/index.d.ts +207 -0
- package/package.json +72 -0
- package/src/@types/encatch-type.ts +111 -0
- package/src/encatch-instance.ts +161 -0
- package/src/feedback-api-types.ts +18 -0
- package/src/hooks/useDevice.ts +27 -0
- package/src/hooks/useFeedbackInterval.ts +71 -0
- package/src/hooks/useFeedbackTriggers.ts +342 -0
- package/src/hooks/useFetchElligibleFeedbackConfiguration.ts +330 -0
- package/src/hooks/useFetchFeedbackConfigurationDetails.ts +82 -0
- package/src/hooks/usePageChangeTracker.ts +88 -0
- package/src/hooks/usePrepopulatedAnswers.ts +123 -0
- package/src/hooks/useRefineTextForm.ts +48 -0
- package/src/hooks/useSubmitFeedbackForm.ts +125 -0
- package/src/hooks/useUserSession.ts +53 -0
- package/src/module.tsx +427 -0
- package/src/store/formResponses.ts +211 -0
- package/src/utils/browser-details.ts +35 -0
- package/src/utils/duration-utils.ts +143 -0
- package/src/utils/feedback-frequency-storage.ts +214 -0
- package/src/utils/feedback-storage.ts +166 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useState } from "preact/hooks";
|
|
2
|
+
import { SubmitFeedback, ViewFeedback } from "@encatch/schema";
|
|
3
|
+
import { EncatchApiSDK } from "@encatch/api-sdk";
|
|
4
|
+
import { moveFeedbackToSubmitted } from "../utils/feedback-storage";
|
|
5
|
+
|
|
6
|
+
export interface SubmitFeedbackResponse {
|
|
7
|
+
success?: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useSubmitFeedbackForm = ({
|
|
12
|
+
apiKey,
|
|
13
|
+
hostUrl,
|
|
14
|
+
}: {
|
|
15
|
+
apiKey: string;
|
|
16
|
+
hostUrl: string;
|
|
17
|
+
}) => {
|
|
18
|
+
const [loadingSubmitFeedback, setLoadingSubmitFeedback] = useState(false);
|
|
19
|
+
const [errorSubmitFeedback, setErrorSubmitFeedback] = useState<string | null>(
|
|
20
|
+
null
|
|
21
|
+
);
|
|
22
|
+
const [successSubmitFeedback, setSuccessSubmitFeedback] = useState(false);
|
|
23
|
+
|
|
24
|
+
// New state for viewFeedback
|
|
25
|
+
const [loadingViewFeedback, setLoadingViewFeedback] = useState(false);
|
|
26
|
+
const [errorViewFeedback, setErrorViewFeedback] = useState<string | null>(
|
|
27
|
+
null
|
|
28
|
+
);
|
|
29
|
+
const [successViewFeedback, setSuccessViewFeedback] = useState(false);
|
|
30
|
+
|
|
31
|
+
// Instantiate the SDK once
|
|
32
|
+
const sdk = new EncatchApiSDK({
|
|
33
|
+
apiKey,
|
|
34
|
+
hostUrl,
|
|
35
|
+
appPackageName: window.location.hostname,
|
|
36
|
+
enableLogging: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const submitFeedback = async (
|
|
40
|
+
params: SubmitFeedback
|
|
41
|
+
): Promise<SubmitFeedbackResponse> => {
|
|
42
|
+
setLoadingSubmitFeedback(true);
|
|
43
|
+
setErrorSubmitFeedback(null);
|
|
44
|
+
setSuccessSubmitFeedback(false);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Ensure sessionInfo is always provided (required by SDK)
|
|
48
|
+
if (!params.sessionInfo) {
|
|
49
|
+
throw new Error("sessionInfo is required for submitting feedback");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Use the SDK's submitFeedback method
|
|
53
|
+
// Type assertion needed because SDK requires sessionInfo to be non-optional
|
|
54
|
+
const result = await sdk.submitFeedback(params as any);
|
|
55
|
+
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
throw new Error(result.error || "Unknown error from SDK");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Move feedback from pending to submitted with timestamp
|
|
61
|
+
const feedbackConfigId = params.formConfig.feedbackConfigurationId;
|
|
62
|
+
if (feedbackConfigId) {
|
|
63
|
+
moveFeedbackToSubmitted(feedbackConfigId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setSuccessSubmitFeedback(true);
|
|
67
|
+
return { success: true };
|
|
68
|
+
} catch (err) {
|
|
69
|
+
const errorMessage =
|
|
70
|
+
err instanceof Error
|
|
71
|
+
? err.message
|
|
72
|
+
: "An error occurred while submitting feedback";
|
|
73
|
+
setErrorSubmitFeedback(errorMessage);
|
|
74
|
+
return { error: errorMessage };
|
|
75
|
+
} finally {
|
|
76
|
+
setLoadingSubmitFeedback(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const viewFeedback = async (
|
|
81
|
+
params: ViewFeedback
|
|
82
|
+
): Promise<SubmitFeedbackResponse> => {
|
|
83
|
+
setLoadingViewFeedback(true);
|
|
84
|
+
setErrorViewFeedback(null);
|
|
85
|
+
setSuccessViewFeedback(false);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
// Ensure sessionInfo is always provided (required by SDK)
|
|
89
|
+
if (!params.sessionInfo) {
|
|
90
|
+
throw new Error("sessionInfo is required for viewing feedback");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Use the SDK's viewFeedback method
|
|
94
|
+
// Type assertion needed because SDK requires sessionInfo to be non-optional
|
|
95
|
+
const result = await sdk.viewFeedback(params as any);
|
|
96
|
+
|
|
97
|
+
if (!result.success) {
|
|
98
|
+
throw new Error(result.error || "Unknown error from SDK");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setSuccessViewFeedback(true);
|
|
102
|
+
return { success: true };
|
|
103
|
+
} catch (err) {
|
|
104
|
+
const errorMessage =
|
|
105
|
+
err instanceof Error
|
|
106
|
+
? err.message
|
|
107
|
+
: "An error occurred while viewing feedback";
|
|
108
|
+
setErrorViewFeedback(errorMessage);
|
|
109
|
+
return { error: errorMessage };
|
|
110
|
+
} finally {
|
|
111
|
+
setLoadingViewFeedback(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
loadingSubmitFeedback,
|
|
117
|
+
errorSubmitFeedback,
|
|
118
|
+
successSubmitFeedback,
|
|
119
|
+
submitFeedback,
|
|
120
|
+
loadingViewFeedback,
|
|
121
|
+
errorViewFeedback,
|
|
122
|
+
successViewFeedback,
|
|
123
|
+
viewFeedback,
|
|
124
|
+
};
|
|
125
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { UserInfo } from "@encatch/schema";
|
|
2
|
+
import { useState, useEffect } from "preact/hooks";
|
|
3
|
+
|
|
4
|
+
const USER_STORAGE_KEY = "encatch_app_user";
|
|
5
|
+
const SESSION_STORAGE_KEY = "app_session";
|
|
6
|
+
|
|
7
|
+
function generateSessionId() {
|
|
8
|
+
return crypto.randomUUID();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useUserSession = () => {
|
|
12
|
+
const [userInfoObject, setUserInfoObject] = useState<UserInfo>({
|
|
13
|
+
userName: "",
|
|
14
|
+
properties: {},
|
|
15
|
+
});
|
|
16
|
+
const [sessionId, setSessionId] = useState<string | null>(null);
|
|
17
|
+
|
|
18
|
+
const updateUserInfo = (userId: string, traits?: Record<string, any>) => {
|
|
19
|
+
const newUserInfo: UserInfo = {
|
|
20
|
+
userName: userId,
|
|
21
|
+
properties: traits || {},
|
|
22
|
+
};
|
|
23
|
+
setUserInfoObject(newUserInfo);
|
|
24
|
+
localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(newUserInfo));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
// Handle session - check if there's an existing session
|
|
29
|
+
const storedSessionId = localStorage.getItem(SESSION_STORAGE_KEY);
|
|
30
|
+
if (storedSessionId) {
|
|
31
|
+
setSessionId(storedSessionId);
|
|
32
|
+
} else {
|
|
33
|
+
// Create new session if none exists
|
|
34
|
+
const newSessionId = generateSessionId();
|
|
35
|
+
localStorage.setItem(SESSION_STORAGE_KEY, newSessionId);
|
|
36
|
+
setSessionId(newSessionId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Handle user info
|
|
40
|
+
const storedUser = localStorage.getItem(USER_STORAGE_KEY);
|
|
41
|
+
if (storedUser) {
|
|
42
|
+
setUserInfoObject(JSON.parse(storedUser));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
userInfoObject,
|
|
49
|
+
updateUserInfo,
|
|
50
|
+
sessionId,
|
|
51
|
+
setUserInfoObject,
|
|
52
|
+
};
|
|
53
|
+
};
|
package/src/module.tsx
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module entry point for @encatch/web-sdk
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { Encatch } from '@encatch/web-sdk';
|
|
7
|
+
*
|
|
8
|
+
* const encatch = new Encatch();
|
|
9
|
+
* encatch.init('YOUR_API_KEY', {
|
|
10
|
+
* host: 'https://your-host.com',
|
|
11
|
+
* autoStartEnabled: true
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createEncatchInstance } from "./encatch-instance";
|
|
17
|
+
import { EncatchConfig, FormEventType, FormEventPayload } from "./@types/encatch-type";
|
|
18
|
+
|
|
19
|
+
let coreLoaded = false;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load the core wrapper for module usage
|
|
23
|
+
* SSR-safe: Only runs in browser environment
|
|
24
|
+
*/
|
|
25
|
+
async function loadCore() {
|
|
26
|
+
// SSR safety check
|
|
27
|
+
if (coreLoaded || typeof window === "undefined" || typeof document === "undefined") return;
|
|
28
|
+
coreLoaded = true;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Dynamically import and render the core wrapper
|
|
32
|
+
const { render } = await import("preact");
|
|
33
|
+
const CoreWrapper = (await import("./core-wrapper")).default;
|
|
34
|
+
|
|
35
|
+
// Ensure root exists (SSR-safe)
|
|
36
|
+
if (typeof document === "undefined") return;
|
|
37
|
+
|
|
38
|
+
let root = document.getElementById("enisght-root");
|
|
39
|
+
if (!root && document.body) {
|
|
40
|
+
root = document.createElement("div");
|
|
41
|
+
root.id = "enisght-root";
|
|
42
|
+
document.body.appendChild(root);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Render the core wrapper (only if we have a root)
|
|
46
|
+
if (root) {
|
|
47
|
+
render(<CoreWrapper />, root);
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error("Failed to load Encatch core:", err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Singleton instance for static API
|
|
55
|
+
let defaultInstance: Encatch | null = null;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Encatch SDK class for module-based usage
|
|
59
|
+
*/
|
|
60
|
+
export class Encatch {
|
|
61
|
+
private instance: EncatchGlobal;
|
|
62
|
+
|
|
63
|
+
constructor() {
|
|
64
|
+
this.instance = createEncatchInstance();
|
|
65
|
+
|
|
66
|
+
// Sync instance with window.encatch for core-wrapper compatibility
|
|
67
|
+
// This ensures core-wrapper can access the instance
|
|
68
|
+
if (typeof window !== "undefined") {
|
|
69
|
+
// Only set if not already set (to avoid overwriting CDN version)
|
|
70
|
+
if (!window.encatch) {
|
|
71
|
+
window.encatch = this.instance;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Ensure the root div exists (SSR-safe)
|
|
76
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
77
|
+
if (!document.getElementById("enisght-root")) {
|
|
78
|
+
const rootDiv = document.createElement("div");
|
|
79
|
+
rootDiv.id = "enisght-root";
|
|
80
|
+
if (document.body) {
|
|
81
|
+
document.body.appendChild(rootDiv);
|
|
82
|
+
} else {
|
|
83
|
+
// Wait for DOM to be ready
|
|
84
|
+
if (document.readyState === "loading") {
|
|
85
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
86
|
+
if (!document.getElementById("enisght-root") && document.body) {
|
|
87
|
+
document.body.appendChild(rootDiv);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Static method to initialize the default Encatch instance
|
|
98
|
+
* This provides a convenient API: Encatch.init(apiKey, options)
|
|
99
|
+
*/
|
|
100
|
+
static init(apiKey: string, options?: EncatchConfig): Encatch {
|
|
101
|
+
if (!defaultInstance) {
|
|
102
|
+
defaultInstance = new Encatch();
|
|
103
|
+
}
|
|
104
|
+
defaultInstance.init(apiKey, options);
|
|
105
|
+
return defaultInstance;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get the default singleton instance
|
|
110
|
+
*/
|
|
111
|
+
static getInstance(): Encatch {
|
|
112
|
+
if (!defaultInstance) {
|
|
113
|
+
defaultInstance = new Encatch();
|
|
114
|
+
}
|
|
115
|
+
return defaultInstance;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Initialize the Encatch SDK
|
|
120
|
+
* SSR-safe: Can be called on server, but only initializes in browser
|
|
121
|
+
*/
|
|
122
|
+
init(apiKey: string, options?: EncatchConfig): void {
|
|
123
|
+
// SSR safety check
|
|
124
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
125
|
+
// In SSR environment, just store config for later initialization
|
|
126
|
+
this.instance.apiKey = apiKey;
|
|
127
|
+
if (options) {
|
|
128
|
+
Object.assign(this.instance.config, options);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Override the init to load core directly instead of via script tag
|
|
134
|
+
if (this.instance.initialized) return;
|
|
135
|
+
|
|
136
|
+
// Update all config first
|
|
137
|
+
this.instance.apiKey = apiKey;
|
|
138
|
+
// Set host with default if not provided
|
|
139
|
+
this.instance.config.host = options?.host || "https://app.encatch.com";
|
|
140
|
+
if (options?.autoStartSessionDisabled !== undefined) {
|
|
141
|
+
this.instance.config.autoStartSessionDisabled =
|
|
142
|
+
options.autoStartSessionDisabled;
|
|
143
|
+
}
|
|
144
|
+
if (options?.processAfterIdentitySet !== undefined) {
|
|
145
|
+
this.instance.config.processAfterIdentitySet =
|
|
146
|
+
options.processAfterIdentitySet;
|
|
147
|
+
} else {
|
|
148
|
+
this.instance.config.processAfterIdentitySet = false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (options?.autoStartEnabled !== undefined) {
|
|
152
|
+
this.instance.config.autoStartEnabled = options.autoStartEnabled;
|
|
153
|
+
}
|
|
154
|
+
if (options?.themeMode) {
|
|
155
|
+
this.instance.config.themeMode = options.themeMode;
|
|
156
|
+
} else {
|
|
157
|
+
this.instance.config.themeMode = "light";
|
|
158
|
+
}
|
|
159
|
+
if (options?.language) {
|
|
160
|
+
this.instance.config.language = options.language;
|
|
161
|
+
} else {
|
|
162
|
+
this.instance.config.language = "en";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (options?.customCssLink) {
|
|
166
|
+
this.instance.config.customCssLink = options.customCssLink;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (options?.setUser) {
|
|
170
|
+
this.instance.config.setUser = options.setUser;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (options?.onFormEvent) {
|
|
174
|
+
this.instance.config.onFormEvent = options.onFormEvent;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (options?.customProperties) {
|
|
178
|
+
this.instance.config.customProperties = options.customProperties;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (options?.onSessionDisabled) {
|
|
182
|
+
this.instance.config.onSessionDisabled = options.onSessionDisabled;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Mark as initialized
|
|
186
|
+
this.instance.initialized = true;
|
|
187
|
+
|
|
188
|
+
// Sync instance with window.encatch AFTER all config is set
|
|
189
|
+
// This ensures core-wrapper can access all properties
|
|
190
|
+
if (typeof window !== "undefined") {
|
|
191
|
+
window.encatch = this.instance;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Load core directly for module usage (after window.encatch is set)
|
|
195
|
+
loadCore();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Track a custom event
|
|
200
|
+
*/
|
|
201
|
+
trackEvent(eventName: string, properties?: Record<string, any>): void {
|
|
202
|
+
if (this.instance.trackEvent) {
|
|
203
|
+
this.instance.trackEvent(eventName, properties);
|
|
204
|
+
} else {
|
|
205
|
+
this.instance._i.push(["trackEvent", eventName, properties]);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Start the SDK session
|
|
211
|
+
*/
|
|
212
|
+
start(
|
|
213
|
+
userId?: string,
|
|
214
|
+
traits?: {
|
|
215
|
+
$set?: Record<string, any>;
|
|
216
|
+
$set_once?: Record<string, any>;
|
|
217
|
+
$counter?: Record<string, any>;
|
|
218
|
+
$unset?: string[];
|
|
219
|
+
[key: string]: any;
|
|
220
|
+
}
|
|
221
|
+
): void {
|
|
222
|
+
if (this.instance.start) {
|
|
223
|
+
this.instance.start(userId, traits);
|
|
224
|
+
} else {
|
|
225
|
+
this.instance._i.push(["start", userId, traits]);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Stop the SDK session
|
|
231
|
+
*/
|
|
232
|
+
stop(): void {
|
|
233
|
+
if (this.instance.stop) {
|
|
234
|
+
this.instance.stop();
|
|
235
|
+
} else {
|
|
236
|
+
this.instance._i.push(["stop"]);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Set or update user identity
|
|
242
|
+
*/
|
|
243
|
+
setUser(
|
|
244
|
+
userId?: string,
|
|
245
|
+
traits?: {
|
|
246
|
+
$set?: Record<string, any>;
|
|
247
|
+
$set_once?: Record<string, any>;
|
|
248
|
+
$counter?: Record<string, any>;
|
|
249
|
+
$unset?: string[];
|
|
250
|
+
[key: string]: any;
|
|
251
|
+
}
|
|
252
|
+
): void {
|
|
253
|
+
if (this.instance.setUser) {
|
|
254
|
+
this.instance.setUser(userId, traits);
|
|
255
|
+
} else {
|
|
256
|
+
this.instance._i.push(["setUser", userId, traits]);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Set theme mode
|
|
262
|
+
*/
|
|
263
|
+
setThemeMode(theme: "light" | "dark"): void {
|
|
264
|
+
if (this.instance.setThemeMode) {
|
|
265
|
+
this.instance.setThemeMode(theme);
|
|
266
|
+
} else {
|
|
267
|
+
this.instance._i.push(["setThemeMode", theme]);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Set language
|
|
273
|
+
*/
|
|
274
|
+
setLanguage(language: string): void {
|
|
275
|
+
if (this.instance.setLanguage) {
|
|
276
|
+
this.instance.setLanguage(language);
|
|
277
|
+
} else {
|
|
278
|
+
this.instance._i.push(["setLanguage", language]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Set custom properties
|
|
284
|
+
*/
|
|
285
|
+
setCustomProperties(customProperties: Record<string, string>): void {
|
|
286
|
+
if (this.instance.setCustomProperties) {
|
|
287
|
+
this.instance.setCustomProperties(customProperties);
|
|
288
|
+
} else {
|
|
289
|
+
this.instance._i.push(["setCustomProperties", customProperties]);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Open feedback by configuration ID
|
|
295
|
+
*/
|
|
296
|
+
openFeedbackById(
|
|
297
|
+
feedbackConfigurationId: string,
|
|
298
|
+
theme?: "light" | "dark",
|
|
299
|
+
language?: string,
|
|
300
|
+
event?: string,
|
|
301
|
+
customProperties?: Record<string, string>,
|
|
302
|
+
prepopulatedAnswers?: any[]
|
|
303
|
+
): void {
|
|
304
|
+
if (this.instance.openFeedbackById) {
|
|
305
|
+
this.instance.openFeedbackById(
|
|
306
|
+
feedbackConfigurationId,
|
|
307
|
+
theme,
|
|
308
|
+
language,
|
|
309
|
+
event,
|
|
310
|
+
customProperties,
|
|
311
|
+
prepopulatedAnswers
|
|
312
|
+
);
|
|
313
|
+
} else {
|
|
314
|
+
this.instance._i.push([
|
|
315
|
+
"openFeedbackById",
|
|
316
|
+
feedbackConfigurationId,
|
|
317
|
+
theme,
|
|
318
|
+
language,
|
|
319
|
+
event,
|
|
320
|
+
customProperties,
|
|
321
|
+
prepopulatedAnswers,
|
|
322
|
+
]);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Open feedback by configuration name
|
|
328
|
+
*/
|
|
329
|
+
openFeedbackByName(
|
|
330
|
+
feedbackConfigurationName: string,
|
|
331
|
+
theme?: "light" | "dark",
|
|
332
|
+
language?: string,
|
|
333
|
+
event?: string,
|
|
334
|
+
customProperties?: Record<string, string>,
|
|
335
|
+
prepopulatedAnswers?: any[]
|
|
336
|
+
): void {
|
|
337
|
+
if (this.instance.openFeedbackByName) {
|
|
338
|
+
this.instance.openFeedbackByName(
|
|
339
|
+
feedbackConfigurationName,
|
|
340
|
+
theme,
|
|
341
|
+
language,
|
|
342
|
+
event,
|
|
343
|
+
customProperties,
|
|
344
|
+
prepopulatedAnswers
|
|
345
|
+
);
|
|
346
|
+
} else {
|
|
347
|
+
this.instance._i.push([
|
|
348
|
+
"openFeedbackByName",
|
|
349
|
+
feedbackConfigurationName,
|
|
350
|
+
theme,
|
|
351
|
+
language,
|
|
352
|
+
event,
|
|
353
|
+
customProperties,
|
|
354
|
+
prepopulatedAnswers,
|
|
355
|
+
]);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Verify which feedback IDs are eligible
|
|
361
|
+
*/
|
|
362
|
+
verifyFeedbackIds(feedbackConfigurationIds: string[]): string[] {
|
|
363
|
+
if (this.instance.verifyFeedbackIds) {
|
|
364
|
+
return this.instance.verifyFeedbackIds(feedbackConfigurationIds);
|
|
365
|
+
}
|
|
366
|
+
return [];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Force fetch eligible feedbacks
|
|
371
|
+
*/
|
|
372
|
+
async forceFetchEligibleFeedbacks(): Promise<void> {
|
|
373
|
+
if (this.instance.forceFetchEligibleFeedbacks) {
|
|
374
|
+
return this.instance.forceFetchEligibleFeedbacks();
|
|
375
|
+
}
|
|
376
|
+
return Promise.resolve();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Capture page scroll event
|
|
381
|
+
*/
|
|
382
|
+
capturePageScrollEvent(scrollPercent: string): void {
|
|
383
|
+
if (this.instance.capturePageScrollEvent) {
|
|
384
|
+
this.instance.capturePageScrollEvent(scrollPercent);
|
|
385
|
+
} else {
|
|
386
|
+
this.instance._i.push(["capturePageScrollEvent", scrollPercent]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Subscribe to form events
|
|
392
|
+
*/
|
|
393
|
+
on<T extends FormEventType>(
|
|
394
|
+
eventType: T,
|
|
395
|
+
callback: (payload: FormEventPayload[T]) => void
|
|
396
|
+
): () => void {
|
|
397
|
+
if (this.instance.on) {
|
|
398
|
+
return this.instance.on(eventType, callback);
|
|
399
|
+
}
|
|
400
|
+
// Fallback to queue
|
|
401
|
+
const subscription = { eventType, callback };
|
|
402
|
+
this.instance._eventSubscriptions = this.instance._eventSubscriptions || [];
|
|
403
|
+
this.instance._eventSubscriptions.push(subscription);
|
|
404
|
+
return () => {
|
|
405
|
+
const index = this.instance._eventSubscriptions?.indexOf(subscription);
|
|
406
|
+
if (index !== undefined && index > -1) {
|
|
407
|
+
this.instance._eventSubscriptions?.splice(index, 1);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Get the internal instance (for advanced usage)
|
|
414
|
+
*/
|
|
415
|
+
getInstance(): EncatchGlobal {
|
|
416
|
+
return this.instance;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Export a default instance for convenience (SSR-safe)
|
|
421
|
+
export const encatch = typeof window !== "undefined" ? new Encatch() : (null as any);
|
|
422
|
+
|
|
423
|
+
// Export types
|
|
424
|
+
export type { EncatchConfig, OnFormEventHandler, FormEventBuilder } from "./@types/encatch-type";
|
|
425
|
+
|
|
426
|
+
// Encatch class is already exported above with "export class Encatch"
|
|
427
|
+
|