@encatch/web-sdk 0.0.35-beta.8 → 1.0.0-beta.0

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