mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-21 03:03:25 -05:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 734f1f4bf2 | |||
| 752cdd6ee9 | |||
| 1672eefe50 | |||
| fecedb7d30 |
@@ -6,10 +6,11 @@ import { makeRequest } from "../../utils/make-request";
|
|||||||
export class AttributeAPI {
|
export class AttributeAPI {
|
||||||
private appUrl: string;
|
private appUrl: string;
|
||||||
private environmentId: string;
|
private environmentId: string;
|
||||||
|
private isDebug: boolean;
|
||||||
constructor(appUrl: string, environmentId: string) {
|
constructor(appUrl: string, environmentId: string, isDebug: boolean) {
|
||||||
this.appUrl = appUrl;
|
this.appUrl = appUrl;
|
||||||
this.environmentId = environmentId;
|
this.environmentId = environmentId;
|
||||||
|
this.isDebug = isDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
@@ -25,7 +26,8 @@ export class AttributeAPI {
|
|||||||
this.appUrl,
|
this.appUrl,
|
||||||
`/api/v1/client/${this.environmentId}/contacts/${attributeUpdateInput.userId}/attributes`,
|
`/api/v1/client/${this.environmentId}/contacts/${attributeUpdateInput.userId}/attributes`,
|
||||||
"PUT",
|
"PUT",
|
||||||
{ attributes }
|
{ attributes },
|
||||||
|
this.isDebug
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,23 @@ import { makeRequest } from "../../utils/make-request";
|
|||||||
export class DisplayAPI {
|
export class DisplayAPI {
|
||||||
private appUrl: string;
|
private appUrl: string;
|
||||||
private environmentId: string;
|
private environmentId: string;
|
||||||
|
private isDebug: boolean;
|
||||||
|
|
||||||
constructor(appUrl: string, environmentId: string) {
|
constructor(appUrl: string, environmentId: string, isDebug: boolean) {
|
||||||
this.appUrl = appUrl;
|
this.appUrl = appUrl;
|
||||||
this.environmentId = environmentId;
|
this.environmentId = environmentId;
|
||||||
|
this.isDebug = isDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
displayInput: Omit<TDisplayCreateInput, "environmentId">
|
displayInput: Omit<TDisplayCreateInput, "environmentId">
|
||||||
): Promise<Result<{ id: string }, ApiErrorResponse>> {
|
): Promise<Result<{ id: string }, ApiErrorResponse>> {
|
||||||
return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/displays`, "POST", displayInput);
|
return makeRequest(
|
||||||
|
this.appUrl,
|
||||||
|
`/api/v1/client/${this.environmentId}/displays`,
|
||||||
|
"POST",
|
||||||
|
displayInput,
|
||||||
|
this.isDebug
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,21 @@ import { makeRequest } from "../../utils/make-request";
|
|||||||
export class EnvironmentAPI {
|
export class EnvironmentAPI {
|
||||||
private appUrl: string;
|
private appUrl: string;
|
||||||
private environmentId: string;
|
private environmentId: string;
|
||||||
|
private isDebug: boolean;
|
||||||
|
|
||||||
constructor(appUrl: string, environmentId: string) {
|
constructor(appUrl: string, environmentId: string, isDebug: boolean) {
|
||||||
this.appUrl = appUrl;
|
this.appUrl = appUrl;
|
||||||
this.environmentId = environmentId;
|
this.environmentId = environmentId;
|
||||||
|
this.isDebug = isDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getState(): Promise<Result<TJsEnvironmentState, ApiErrorResponse>> {
|
async getState(): Promise<Result<TJsEnvironmentState, ApiErrorResponse>> {
|
||||||
return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/environment`, "GET");
|
return makeRequest(
|
||||||
|
this.appUrl,
|
||||||
|
`/api/v1/client/${this.environmentId}/environment`,
|
||||||
|
"GET",
|
||||||
|
undefined,
|
||||||
|
this.isDebug
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,14 @@ export class Client {
|
|||||||
environment: EnvironmentAPI;
|
environment: EnvironmentAPI;
|
||||||
|
|
||||||
constructor(options: ApiConfig) {
|
constructor(options: ApiConfig) {
|
||||||
const { appUrl, environmentId } = options;
|
const { appUrl, environmentId, isDebug } = options;
|
||||||
|
const isDebugMode = isDebug ?? false;
|
||||||
|
|
||||||
this.response = new ResponseAPI(appUrl, environmentId);
|
this.response = new ResponseAPI(appUrl, environmentId, isDebugMode);
|
||||||
this.display = new DisplayAPI(appUrl, environmentId);
|
this.display = new DisplayAPI(appUrl, environmentId, isDebugMode);
|
||||||
this.attribute = new AttributeAPI(appUrl, environmentId);
|
this.attribute = new AttributeAPI(appUrl, environmentId, isDebugMode);
|
||||||
this.storage = new StorageAPI(appUrl, environmentId);
|
this.storage = new StorageAPI(appUrl, environmentId);
|
||||||
this.user = new UserAPI(appUrl, environmentId);
|
this.user = new UserAPI(appUrl, environmentId, isDebugMode);
|
||||||
this.environment = new EnvironmentAPI(appUrl, environmentId);
|
this.environment = new EnvironmentAPI(appUrl, environmentId, isDebugMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,23 @@ type TResponseUpdateInputWithResponseId = TResponseUpdateInput & { responseId: s
|
|||||||
export class ResponseAPI {
|
export class ResponseAPI {
|
||||||
private appUrl: string;
|
private appUrl: string;
|
||||||
private environmentId: string;
|
private environmentId: string;
|
||||||
|
private isDebug: boolean;
|
||||||
constructor(appUrl: string, environmentId: string) {
|
constructor(appUrl: string, environmentId: string, isDebug: boolean) {
|
||||||
this.appUrl = appUrl;
|
this.appUrl = appUrl;
|
||||||
this.environmentId = environmentId;
|
this.environmentId = environmentId;
|
||||||
|
this.isDebug = isDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
responseInput: Omit<TResponseInput, "environmentId">
|
responseInput: Omit<TResponseInput, "environmentId">
|
||||||
): Promise<Result<{ id: string }, ApiErrorResponse>> {
|
): Promise<Result<{ id: string }, ApiErrorResponse>> {
|
||||||
return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/responses`, "POST", responseInput);
|
return makeRequest(
|
||||||
|
this.appUrl,
|
||||||
|
`/api/v1/client/${this.environmentId}/responses`,
|
||||||
|
"POST",
|
||||||
|
responseInput,
|
||||||
|
this.isDebug
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update({
|
async update({
|
||||||
@@ -29,13 +36,19 @@ export class ResponseAPI {
|
|||||||
variables,
|
variables,
|
||||||
language,
|
language,
|
||||||
}: TResponseUpdateInputWithResponseId): Promise<Result<object, ApiErrorResponse>> {
|
}: TResponseUpdateInputWithResponseId): Promise<Result<object, ApiErrorResponse>> {
|
||||||
return makeRequest(this.appUrl, `/api/v1/client/${this.environmentId}/responses/${responseId}`, "PUT", {
|
return makeRequest(
|
||||||
finished,
|
this.appUrl,
|
||||||
endingId,
|
`/api/v1/client/${this.environmentId}/responses/${responseId}`,
|
||||||
data,
|
"PUT",
|
||||||
ttc,
|
{
|
||||||
variables,
|
finished,
|
||||||
language,
|
endingId,
|
||||||
});
|
data,
|
||||||
|
ttc,
|
||||||
|
variables,
|
||||||
|
language,
|
||||||
|
},
|
||||||
|
this.isDebug
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import { makeRequest } from "../../utils/make-request";
|
|||||||
export class UserAPI {
|
export class UserAPI {
|
||||||
private appUrl: string;
|
private appUrl: string;
|
||||||
private environmentId: string;
|
private environmentId: string;
|
||||||
|
private isDebug: boolean;
|
||||||
|
|
||||||
constructor(appUrl: string, environmentId: string) {
|
constructor(appUrl: string, environmentId: string, isDebug: boolean) {
|
||||||
this.appUrl = appUrl;
|
this.appUrl = appUrl;
|
||||||
this.environmentId = environmentId;
|
this.environmentId = environmentId;
|
||||||
|
this.isDebug = isDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createOrUpdate(userUpdateInput: { userId: string; attributes?: Record<string, string> }): Promise<
|
async createOrUpdate(userUpdateInput: { userId: string; attributes?: Record<string, string> }): Promise<
|
||||||
@@ -37,9 +39,15 @@ export class UserAPI {
|
|||||||
attributes[key] = String(userUpdateInput.attributes[key]);
|
attributes[key] = String(userUpdateInput.attributes[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeRequest(this.appUrl, `/api/v2/client/${this.environmentId}/user`, "POST", {
|
return makeRequest(
|
||||||
userId: userUpdateInput.userId,
|
this.appUrl,
|
||||||
attributes,
|
`/api/v2/client/${this.environmentId}/user`,
|
||||||
});
|
"POST",
|
||||||
|
{
|
||||||
|
userId: userUpdateInput.userId,
|
||||||
|
attributes,
|
||||||
|
},
|
||||||
|
this.isDebug
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { type ApiErrorResponse } from "@formbricks/types/errors";
|
|||||||
export interface ApiConfig {
|
export interface ApiConfig {
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
appUrl: string;
|
appUrl: string;
|
||||||
|
isDebug?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiResponse = ApiSuccessResponse | ApiErrorResponse;
|
export type ApiResponse = ApiSuccessResponse | ApiErrorResponse;
|
||||||
|
|||||||
@@ -6,16 +6,17 @@ export const makeRequest = async <T>(
|
|||||||
appUrl: string,
|
appUrl: string,
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||||
data?: unknown
|
data?: unknown,
|
||||||
|
isDebug?: boolean
|
||||||
): Promise<Result<T, ApiErrorResponse>> => {
|
): Promise<Result<T, ApiErrorResponse>> => {
|
||||||
const url = new URL(appUrl + endpoint);
|
const url = new URL(appUrl + endpoint);
|
||||||
const body = data ? JSON.stringify(data) : undefined;
|
const body = data ? JSON.stringify(data) : undefined;
|
||||||
|
|
||||||
const res = await wrapThrowsAsync(fetch)(url.toString(), {
|
const res = await wrapThrowsAsync(fetch)(url.toString(), {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
...(isDebug && { cache: "no-store" }),
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { checkPageUrl } from "@/lib/survey/no-code-action";
|
|||||||
import * as Attribute from "@/lib/user/attribute";
|
import * as Attribute from "@/lib/user/attribute";
|
||||||
import * as User from "@/lib/user/user";
|
import * as User from "@/lib/user/user";
|
||||||
import { type TConfigInput, type TLegacyConfigInput } from "@/types/config";
|
import { type TConfigInput, type TLegacyConfigInput } from "@/types/config";
|
||||||
|
import { type TTrackProperties } from "@/types/survey";
|
||||||
|
|
||||||
const queue = new CommandQueue();
|
const queue = new CommandQueue();
|
||||||
|
|
||||||
@@ -67,8 +68,12 @@ const logout = async (): Promise<void> => {
|
|||||||
await queue.wait();
|
await queue.wait();
|
||||||
};
|
};
|
||||||
|
|
||||||
const track = async (code: string): Promise<void> => {
|
/**
|
||||||
queue.add(Action.trackCodeAction, true, code);
|
* @param code - The code of the action to track
|
||||||
|
* @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
|
||||||
|
*/
|
||||||
|
const track = async (code: string, properties?: TTrackProperties): Promise<void> => {
|
||||||
|
queue.add(Action.trackCodeAction, true, code, properties as unknown);
|
||||||
await queue.wait();
|
await queue.wait();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import { checkSetup } from "@/lib/common/setup";
|
|||||||
import { wrapThrowsAsync } from "@/lib/common/utils";
|
import { wrapThrowsAsync } from "@/lib/common/utils";
|
||||||
import type { Result } from "@/types/error";
|
import type { Result } from "@/types/error";
|
||||||
|
|
||||||
|
export type TCommandQueueCommand = (
|
||||||
|
...args: any[]
|
||||||
|
) => Promise<Result<void, unknown>> | Result<void, unknown> | Promise<void>;
|
||||||
|
|
||||||
export class CommandQueue {
|
export class CommandQueue {
|
||||||
private queue: {
|
private queue: {
|
||||||
command: (...args: any[]) => Promise<Result<void, unknown>> | Result<void, unknown> | Promise<void>;
|
command: TCommandQueueCommand;
|
||||||
checkSetup: boolean;
|
checkSetup: boolean;
|
||||||
commandArgs: any[];
|
commandArgs: any[];
|
||||||
}[] = [];
|
}[] = [];
|
||||||
@@ -14,11 +18,7 @@ export class CommandQueue {
|
|||||||
private resolvePromise: (() => void) | null = null;
|
private resolvePromise: (() => void) | null = null;
|
||||||
private commandPromise: Promise<void> | null = null;
|
private commandPromise: Promise<void> | null = null;
|
||||||
|
|
||||||
public add<A>(
|
public add<A>(command: TCommandQueueCommand, shouldCheckSetup = true, ...args: A[]): void {
|
||||||
command: (...args: A[]) => Promise<Result<void, unknown>> | Result<void, unknown> | Promise<void>,
|
|
||||||
shouldCheckSetup = true,
|
|
||||||
...args: A[]
|
|
||||||
): void {
|
|
||||||
this.queue.push({ command, checkSetup: shouldCheckSetup, commandArgs: args });
|
this.queue.push({ command, checkSetup: shouldCheckSetup, commandArgs: args });
|
||||||
|
|
||||||
if (!this.running) {
|
if (!this.running) {
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ export const setup = async (
|
|||||||
addWidgetContainer();
|
addWidgetContainer();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
!isDebug &&
|
||||||
existingConfig?.environment &&
|
existingConfig?.environment &&
|
||||||
existingConfig.environmentId === configInput.environmentId &&
|
existingConfig.environmentId === configInput.environmentId &&
|
||||||
existingConfig.appUrl === configInput.appUrl
|
existingConfig.appUrl === configInput.appUrl
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Logger } from "@/lib/common/logger";
|
||||||
import type {
|
import type {
|
||||||
TEnvironmentState,
|
TEnvironmentState,
|
||||||
TEnvironmentStateActionClass,
|
TEnvironmentStateActionClass,
|
||||||
@@ -8,7 +9,11 @@ import type {
|
|||||||
TUserState,
|
TUserState,
|
||||||
} from "@/types/config";
|
} from "@/types/config";
|
||||||
import type { Result } from "@/types/error";
|
import type { Result } from "@/types/error";
|
||||||
import { type TActionClassNoCodeConfig, type TActionClassPageUrlRule } from "@/types/survey";
|
import {
|
||||||
|
type TActionClassNoCodeConfig,
|
||||||
|
type TActionClassPageUrlRule,
|
||||||
|
type TTrackProperties,
|
||||||
|
} from "@/types/survey";
|
||||||
|
|
||||||
// Helper function to calculate difference in days between two dates
|
// Helper function to calculate difference in days between two dates
|
||||||
export const diffInDays = (date1: Date, date2: Date): number => {
|
export const diffInDays = (date1: Date, date2: Date): number => {
|
||||||
@@ -225,6 +230,38 @@ export const handleUrlFilters = (urlFilters: TActionClassNoCodeConfig["urlFilter
|
|||||||
return isMatch;
|
return isMatch;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleHiddenFields = (
|
||||||
|
hiddenFieldsConfig: TEnvironmentStateSurvey["hiddenFields"],
|
||||||
|
hiddenFields?: TTrackProperties["hiddenFields"]
|
||||||
|
): TTrackProperties["hiddenFields"] => {
|
||||||
|
const logger = Logger.getInstance();
|
||||||
|
const { enabled: enabledHiddenFields, fieldIds: hiddenFieldIds } = hiddenFieldsConfig;
|
||||||
|
|
||||||
|
let hiddenFieldsObject: TTrackProperties["hiddenFields"] = {};
|
||||||
|
|
||||||
|
if (!enabledHiddenFields) {
|
||||||
|
logger.error("Hidden fields are not enabled for this survey");
|
||||||
|
} else if (hiddenFieldIds && hiddenFields) {
|
||||||
|
const unknownHiddenFields: string[] = [];
|
||||||
|
hiddenFieldsObject = Object.keys(hiddenFields).reduce<TTrackProperties["hiddenFields"]>((acc, key) => {
|
||||||
|
if (hiddenFieldIds.includes(key)) {
|
||||||
|
acc[key] = hiddenFields[key];
|
||||||
|
} else {
|
||||||
|
unknownHiddenFields.push(key);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
if (unknownHiddenFields.length > 0) {
|
||||||
|
logger.error(
|
||||||
|
`Unknown hidden fields: ${unknownHiddenFields.join(", ")}. Please add them to the survey hidden fields.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hiddenFieldsObject;
|
||||||
|
};
|
||||||
|
|
||||||
export const evaluateNoCodeConfigClick = (
|
export const evaluateNoCodeConfigClick = (
|
||||||
targetElement: HTMLElement,
|
targetElement: HTMLElement,
|
||||||
action: TEnvironmentStateActionClass
|
action: TEnvironmentStateActionClass
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { FormbricksAPI } from "@formbricks/api";
|
import { FormbricksAPI } from "@formbricks/api";
|
||||||
import { Config } from "@/lib/common/config";
|
import { Config } from "@/lib/common/config";
|
||||||
import { Logger } from "@/lib/common/logger";
|
import { Logger } from "@/lib/common/logger";
|
||||||
import { filterSurveys } from "@/lib/common/utils";
|
import { filterSurveys , getIsDebug } from "@/lib/common/utils";
|
||||||
import type { TConfigInput, TEnvironmentState } from "@/types/config";
|
import type { TConfigInput, TEnvironmentState } from "@/types/config";
|
||||||
import { type ApiErrorResponse, type Result, err, ok } from "@/types/error";
|
import { type ApiErrorResponse, type Result, err, ok } from "@/types/error";
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export const fetchEnvironmentState = async ({
|
|||||||
environmentId,
|
environmentId,
|
||||||
}: TConfigInput): Promise<Result<TEnvironmentState, ApiErrorResponse>> => {
|
}: TConfigInput): Promise<Result<TEnvironmentState, ApiErrorResponse>> => {
|
||||||
const url = `${appUrl}/api/v1/client/${environmentId}/environment`;
|
const url = `${appUrl}/api/v1/client/${environmentId}/environment`;
|
||||||
const api = new FormbricksAPI({ appUrl, environmentId });
|
const api = new FormbricksAPI({ appUrl, environmentId, isDebug: getIsDebug() });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await api.client.environment.getState();
|
const response = await api.client.environment.getState();
|
||||||
|
|||||||
@@ -2,14 +2,20 @@ import { Config } from "@/lib/common/config";
|
|||||||
import { Logger } from "@/lib/common/logger";
|
import { Logger } from "@/lib/common/logger";
|
||||||
import { triggerSurvey } from "@/lib/survey/widget";
|
import { triggerSurvey } from "@/lib/survey/widget";
|
||||||
import { type InvalidCodeError, type NetworkError, type Result, err, okVoid } from "@/types/error";
|
import { type InvalidCodeError, type NetworkError, type Result, err, okVoid } from "@/types/error";
|
||||||
|
import { type TTrackProperties } from "@/types/survey";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks an action name and triggers associated surveys
|
* Tracks an action name and triggers associated surveys
|
||||||
* @param name - The name of the action to track
|
* @param name - The name of the action to track
|
||||||
* @param alias - Optional alias for the action name
|
* @param alias - Optional alias for the action name
|
||||||
|
* @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
|
||||||
* @returns Result indicating success or network error
|
* @returns Result indicating success or network error
|
||||||
*/
|
*/
|
||||||
export const trackAction = async (name: string, alias?: string): Promise<Result<void, NetworkError>> => {
|
export const trackAction = async (
|
||||||
|
name: string,
|
||||||
|
alias?: string,
|
||||||
|
properties?: TTrackProperties
|
||||||
|
): Promise<Result<void, NetworkError>> => {
|
||||||
const logger = Logger.getInstance();
|
const logger = Logger.getInstance();
|
||||||
const appConfig = Config.getInstance();
|
const appConfig = Config.getInstance();
|
||||||
|
|
||||||
@@ -24,7 +30,7 @@ export const trackAction = async (name: string, alias?: string): Promise<Result<
|
|||||||
for (const survey of activeSurveys) {
|
for (const survey of activeSurveys) {
|
||||||
for (const trigger of survey.triggers) {
|
for (const trigger of survey.triggers) {
|
||||||
if (trigger.actionClass.name === name) {
|
if (trigger.actionClass.name === name) {
|
||||||
await triggerSurvey(survey, name);
|
await triggerSurvey(survey, name, properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,10 +44,12 @@ export const trackAction = async (name: string, alias?: string): Promise<Result<
|
|||||||
/**
|
/**
|
||||||
* Tracks an action by its code and triggers associated surveys (used for code actions only)
|
* Tracks an action by its code and triggers associated surveys (used for code actions only)
|
||||||
* @param code - The action code to track
|
* @param code - The action code to track
|
||||||
|
* @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
|
||||||
* @returns Result indicating success, network error, or invalid code error
|
* @returns Result indicating success, network error, or invalid code error
|
||||||
*/
|
*/
|
||||||
export const trackCodeAction = async (
|
export const trackCodeAction = async (
|
||||||
code: string
|
code: string,
|
||||||
|
properties?: TTrackProperties
|
||||||
): Promise<Result<void, NetworkError> | Result<void, InvalidCodeError>> => {
|
): Promise<Result<void, NetworkError> | Result<void, InvalidCodeError>> => {
|
||||||
const appConfig = Config.getInstance();
|
const appConfig = Config.getInstance();
|
||||||
|
|
||||||
@@ -61,7 +69,7 @@ export const trackCodeAction = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return trackAction(actionClass.name, code);
|
return trackAction(actionClass.name, code, properties);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const trackNoCodeAction = (name: string): Promise<Result<void, NetworkError>> => {
|
export const trackNoCodeAction = (name: string): Promise<Result<void, NetworkError>> => {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ vi.mock("@/lib/common/logger", () => ({
|
|||||||
|
|
||||||
vi.mock("@/lib/common/utils", () => ({
|
vi.mock("@/lib/common/utils", () => ({
|
||||||
shouldDisplayBasedOnPercentage: vi.fn(),
|
shouldDisplayBasedOnPercentage: vi.fn(),
|
||||||
|
handleHiddenFields: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/lib/survey/widget", () => ({
|
vi.mock("@/lib/survey/widget", () => ({
|
||||||
@@ -100,10 +101,10 @@ describe("survey/action.ts", () => {
|
|||||||
filteredSurveys: [mockSurvey],
|
filteredSurveys: [mockSurvey],
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await trackAction("testAction");
|
const result = await trackAction("testAction", undefined);
|
||||||
|
|
||||||
expect(result.ok).toBe(true);
|
expect(result.ok).toBe(true);
|
||||||
expect(triggerSurvey).toHaveBeenCalledWith(mockSurvey, "testAction");
|
expect(triggerSurvey).toHaveBeenCalledWith(mockSurvey, "testAction", undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("handles multiple matching surveys", async () => {
|
test("handles multiple matching surveys", async () => {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ vi.mock("@/lib/common/utils", () => ({
|
|||||||
getStyling: vi.fn(),
|
getStyling: vi.fn(),
|
||||||
shouldDisplayBasedOnPercentage: vi.fn(),
|
shouldDisplayBasedOnPercentage: vi.fn(),
|
||||||
wrapThrowsAsync: vi.fn(),
|
wrapThrowsAsync: vi.fn(),
|
||||||
|
handleHiddenFields: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("widget-file", () => {
|
describe("widget-file", () => {
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import {
|
|||||||
filterSurveys,
|
filterSurveys,
|
||||||
getLanguageCode,
|
getLanguageCode,
|
||||||
getStyling,
|
getStyling,
|
||||||
|
handleHiddenFields,
|
||||||
shouldDisplayBasedOnPercentage,
|
shouldDisplayBasedOnPercentage,
|
||||||
} from "@/lib/common/utils";
|
} from "@/lib/common/utils";
|
||||||
import { type TEnvironmentStateSurvey, type TUserState } from "@/types/config";
|
import { type TEnvironmentStateSurvey, type TUserState } from "@/types/config";
|
||||||
|
import { type TTrackProperties } from "@/types/survey";
|
||||||
|
|
||||||
let isSurveyRunning = false;
|
let isSurveyRunning = false;
|
||||||
|
|
||||||
@@ -17,7 +19,11 @@ export const setIsSurveyRunning = (value: boolean): void => {
|
|||||||
isSurveyRunning = value;
|
isSurveyRunning = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const triggerSurvey = async (survey: TEnvironmentStateSurvey, action?: string): Promise<void> => {
|
export const triggerSurvey = async (
|
||||||
|
survey: TEnvironmentStateSurvey,
|
||||||
|
action?: string,
|
||||||
|
properties?: TTrackProperties
|
||||||
|
): Promise<void> => {
|
||||||
const logger = Logger.getInstance();
|
const logger = Logger.getInstance();
|
||||||
|
|
||||||
// Check if the survey should be displayed based on displayPercentage
|
// Check if the survey should be displayed based on displayPercentage
|
||||||
@@ -29,10 +35,19 @@ export const triggerSurvey = async (survey: TEnvironmentStateSurvey, action?: st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await renderWidget(survey, action);
|
const hiddenFieldsObject: TTrackProperties["hiddenFields"] = handleHiddenFields(
|
||||||
|
survey.hiddenFields,
|
||||||
|
properties?.hiddenFields
|
||||||
|
);
|
||||||
|
|
||||||
|
await renderWidget(survey, action, hiddenFieldsObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderWidget = async (survey: TEnvironmentStateSurvey, action?: string): Promise<void> => {
|
export const renderWidget = async (
|
||||||
|
survey: TEnvironmentStateSurvey,
|
||||||
|
action?: string,
|
||||||
|
hiddenFieldsObject?: TTrackProperties["hiddenFields"]
|
||||||
|
): Promise<void> => {
|
||||||
const logger = Logger.getInstance();
|
const logger = Logger.getInstance();
|
||||||
const config = Config.getInstance();
|
const config = Config.getInstance();
|
||||||
const timeoutStack = TimeoutStack.getInstance();
|
const timeoutStack = TimeoutStack.getInstance();
|
||||||
@@ -87,6 +102,7 @@ export const renderWidget = async (survey: TEnvironmentStateSurvey, action?: str
|
|||||||
languageCode,
|
languageCode,
|
||||||
placement,
|
placement,
|
||||||
styling: getStyling(project, survey),
|
styling: getStyling(project, survey),
|
||||||
|
hiddenFieldsRecord: hiddenFieldsObject,
|
||||||
onDisplayCreated: () => {
|
onDisplayCreated: () => {
|
||||||
const existingDisplays = config.get().user.data.displays;
|
const existingDisplays = config.get().user.data.displays;
|
||||||
const newDisplay = { surveyId: survey.id, createdAt: new Date() };
|
const newDisplay = { surveyId: survey.id, createdAt: new Date() };
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { FormbricksAPI } from "@formbricks/api";
|
import { FormbricksAPI } from "@formbricks/api";
|
||||||
import { Config } from "@/lib/common/config";
|
import { Config } from "@/lib/common/config";
|
||||||
import { Logger } from "@/lib/common/logger";
|
import { Logger } from "@/lib/common/logger";
|
||||||
import { filterSurveys } from "@/lib/common/utils";
|
import { filterSurveys , getIsDebug } from "@/lib/common/utils";
|
||||||
import { type TUpdates, type TUserState } from "@/types/config";
|
import { type TUpdates, type TUserState } from "@/types/config";
|
||||||
import { type ApiErrorResponse, type Result, type ResultError, err, ok, okVoid } from "@/types/error";
|
import { type ApiErrorResponse, type Result, type ResultError, err, ok, okVoid } from "@/types/error";
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ export const sendUpdatesToBackend = async ({
|
|||||||
const url = `${appUrl}/api/v1/client/${environmentId}/user`;
|
const url = `${appUrl}/api/v1/client/${environmentId}/user`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const api = new FormbricksAPI({ appUrl, environmentId });
|
const api = new FormbricksAPI({ appUrl, environmentId, isDebug: getIsDebug() });
|
||||||
|
|
||||||
const response = await api.client.user.createOrUpdate({
|
const response = await api.client.user.createOrUpdate({
|
||||||
userId: updates.userId,
|
userId: updates.userId,
|
||||||
|
|||||||
@@ -75,3 +75,7 @@ export type TActionClassNoCodeConfig =
|
|||||||
rule: TActionClassPageUrlRule;
|
rule: TActionClassPageUrlRule;
|
||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface TTrackProperties {
|
||||||
|
hiddenFields: Record<string, string | number | string[]>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@formbricks/js",
|
"name": "@formbricks/js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"version": "4.0.0",
|
"version": "4.1.0",
|
||||||
"description": "Formbricks-js allows you to connect your index to Formbricks, display surveys and trigger events.",
|
"description": "Formbricks-js allows you to connect your index to Formbricks, display surveys and trigger events.",
|
||||||
"homepage": "https://formbricks.com",
|
"homepage": "https://formbricks.com",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
Reference in New Issue
Block a user