mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-30 11:41:05 -05:00
311 lines
7.4 KiB
TypeScript
311 lines
7.4 KiB
TypeScript
import { createId } from "@paralleldrive/cuid2";
|
|
import type { TFunction } from "i18next";
|
|
import type { TSurveyBlock, TSurveyBlockLogic } from "@formbricks/types/surveys/blocks";
|
|
import type {
|
|
TSurveyCTAElement,
|
|
TSurveyConsentElement,
|
|
TSurveyElement,
|
|
TSurveyMultipleChoiceElement,
|
|
TSurveyNPSElement,
|
|
TSurveyOpenTextElement,
|
|
TSurveyOpenTextElementInputType,
|
|
TSurveyRatingElement,
|
|
} from "@formbricks/types/surveys/elements";
|
|
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements";
|
|
import type { TShuffleOption } from "@formbricks/types/surveys/types";
|
|
import { createI18nString } from "@/lib/i18n/utils";
|
|
|
|
const getDefaultButtonLabel = (label: string | undefined, t: TFunction) =>
|
|
createI18nString(label || t("common.next"), []);
|
|
|
|
const getDefaultBackButtonLabel = (label: string | undefined, t: TFunction) =>
|
|
createI18nString(label || t("common.back"), []);
|
|
|
|
export const buildMultipleChoiceElement = ({
|
|
id,
|
|
headline,
|
|
type,
|
|
subheader,
|
|
choices,
|
|
choiceIds,
|
|
shuffleOption,
|
|
required,
|
|
containsOther = false,
|
|
}: {
|
|
id?: string;
|
|
headline: string;
|
|
type: TSurveyElementTypeEnum.MultipleChoiceMulti | TSurveyElementTypeEnum.MultipleChoiceSingle;
|
|
subheader?: string;
|
|
choices: string[];
|
|
choiceIds?: string[];
|
|
shuffleOption?: TShuffleOption;
|
|
required?: boolean;
|
|
containsOther?: boolean;
|
|
}): TSurveyMultipleChoiceElement => {
|
|
return {
|
|
id: id ?? createId(),
|
|
type,
|
|
subheader: subheader ? createI18nString(subheader, []) : undefined,
|
|
headline: createI18nString(headline, []),
|
|
choices: choices.map((choice, index) => {
|
|
const isLastIndex = index === choices.length - 1;
|
|
let choiceId: string;
|
|
if (containsOther && isLastIndex) {
|
|
choiceId = "other";
|
|
} else if (choiceIds) {
|
|
choiceId = choiceIds[index];
|
|
} else {
|
|
choiceId = createId();
|
|
}
|
|
return { id: choiceId, label: createI18nString(choice, []) };
|
|
}),
|
|
shuffleOption: shuffleOption || "none",
|
|
required: required ?? false,
|
|
};
|
|
};
|
|
|
|
export const buildOpenTextElement = ({
|
|
id,
|
|
headline,
|
|
subheader,
|
|
placeholder,
|
|
inputType,
|
|
required,
|
|
longAnswer,
|
|
}: {
|
|
id?: string;
|
|
headline: string;
|
|
subheader?: string;
|
|
placeholder?: string;
|
|
required?: boolean;
|
|
inputType: TSurveyOpenTextElementInputType;
|
|
longAnswer?: boolean;
|
|
}): TSurveyOpenTextElement => {
|
|
return {
|
|
id: id ?? createId(),
|
|
type: TSurveyElementTypeEnum.OpenText,
|
|
inputType,
|
|
subheader: subheader ? createI18nString(subheader, []) : undefined,
|
|
placeholder: placeholder ? createI18nString(placeholder, []) : undefined,
|
|
headline: createI18nString(headline, []),
|
|
required: required ?? false,
|
|
longAnswer,
|
|
charLimit: {
|
|
enabled: false,
|
|
},
|
|
};
|
|
};
|
|
|
|
export const buildRatingElement = ({
|
|
id,
|
|
headline,
|
|
subheader,
|
|
scale,
|
|
range,
|
|
lowerLabel,
|
|
upperLabel,
|
|
required,
|
|
isColorCodingEnabled = false,
|
|
}: {
|
|
id?: string;
|
|
headline: string;
|
|
scale: TSurveyRatingElement["scale"];
|
|
range: TSurveyRatingElement["range"];
|
|
lowerLabel?: string;
|
|
upperLabel?: string;
|
|
subheader?: string;
|
|
required?: boolean;
|
|
isColorCodingEnabled?: boolean;
|
|
}): TSurveyRatingElement => {
|
|
return {
|
|
id: id ?? createId(),
|
|
type: TSurveyElementTypeEnum.Rating,
|
|
subheader: subheader ? createI18nString(subheader, []) : undefined,
|
|
headline: createI18nString(headline, []),
|
|
scale,
|
|
range,
|
|
required: required ?? false,
|
|
isColorCodingEnabled,
|
|
lowerLabel: lowerLabel ? createI18nString(lowerLabel, []) : undefined,
|
|
upperLabel: upperLabel ? createI18nString(upperLabel, []) : undefined,
|
|
};
|
|
};
|
|
|
|
export const buildConsentElement = ({
|
|
id,
|
|
headline,
|
|
subheader,
|
|
label,
|
|
required,
|
|
}: {
|
|
id?: string;
|
|
headline: string;
|
|
subheader: string;
|
|
required?: boolean;
|
|
label: string;
|
|
}): TSurveyConsentElement => {
|
|
return {
|
|
id: id ?? createId(),
|
|
type: TSurveyElementTypeEnum.Consent,
|
|
subheader: createI18nString(subheader, []),
|
|
headline: createI18nString(headline, []),
|
|
required: required ?? false,
|
|
label: createI18nString(label, []),
|
|
};
|
|
};
|
|
|
|
export const buildCTAElement = ({
|
|
id,
|
|
headline,
|
|
subheader,
|
|
buttonExternal,
|
|
required,
|
|
ctaButtonLabel,
|
|
buttonUrl,
|
|
}: {
|
|
id?: string;
|
|
headline: string;
|
|
buttonExternal?: boolean;
|
|
subheader: string;
|
|
required?: boolean;
|
|
ctaButtonLabel?: string;
|
|
buttonUrl?: string;
|
|
}): TSurveyCTAElement => {
|
|
return {
|
|
id: id ?? createId(),
|
|
type: TSurveyElementTypeEnum.CTA,
|
|
subheader: createI18nString(subheader, []),
|
|
headline: createI18nString(headline, []),
|
|
ctaButtonLabel: ctaButtonLabel ? createI18nString(ctaButtonLabel, []) : undefined,
|
|
required: required ?? false,
|
|
buttonExternal: buttonExternal ?? false,
|
|
buttonUrl,
|
|
};
|
|
};
|
|
|
|
export const buildNPSElement = ({
|
|
id,
|
|
headline,
|
|
subheader,
|
|
lowerLabel,
|
|
upperLabel,
|
|
required,
|
|
isColorCodingEnabled = false,
|
|
}: {
|
|
id?: string;
|
|
headline: string;
|
|
subheader?: string;
|
|
lowerLabel?: string;
|
|
upperLabel?: string;
|
|
required?: boolean;
|
|
isColorCodingEnabled?: boolean;
|
|
}): TSurveyNPSElement => {
|
|
return {
|
|
id: id ?? createId(),
|
|
type: TSurveyElementTypeEnum.NPS,
|
|
subheader: subheader ? createI18nString(subheader, []) : undefined,
|
|
headline: createI18nString(headline, []),
|
|
required: required ?? false,
|
|
isColorCodingEnabled,
|
|
lowerLabel: lowerLabel ? createI18nString(lowerLabel, []) : undefined,
|
|
upperLabel: upperLabel ? createI18nString(upperLabel, []) : undefined,
|
|
};
|
|
};
|
|
|
|
// Helper function to create block-level jump logic based on operator
|
|
export const createBlockJumpLogic = (
|
|
sourceElementId: string,
|
|
targetBlockId: string,
|
|
operator: "isSkipped" | "isSubmitted" | "isClicked"
|
|
): TSurveyBlockLogic => ({
|
|
id: createId(),
|
|
conditions: {
|
|
id: createId(),
|
|
connector: "and",
|
|
conditions: [
|
|
{
|
|
id: createId(),
|
|
leftOperand: {
|
|
value: sourceElementId,
|
|
type: "element",
|
|
},
|
|
operator: operator,
|
|
},
|
|
],
|
|
},
|
|
actions: [
|
|
{
|
|
id: createId(),
|
|
objective: "jumpToBlock",
|
|
target: targetBlockId,
|
|
},
|
|
],
|
|
});
|
|
|
|
// Helper function to create block-level jump logic based on choice selection
|
|
export const createBlockChoiceJumpLogic = (
|
|
sourceElementId: string,
|
|
choiceId: string | number,
|
|
targetBlockId: string
|
|
): TSurveyBlockLogic => ({
|
|
id: createId(),
|
|
conditions: {
|
|
id: createId(),
|
|
connector: "and",
|
|
conditions: [
|
|
{
|
|
id: createId(),
|
|
leftOperand: {
|
|
value: sourceElementId,
|
|
type: "element",
|
|
},
|
|
operator: "equals",
|
|
rightOperand: {
|
|
type: "static",
|
|
value: choiceId,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
actions: [
|
|
{
|
|
id: createId(),
|
|
objective: "jumpToBlock",
|
|
target: targetBlockId,
|
|
},
|
|
],
|
|
});
|
|
|
|
// Block builder function
|
|
export const buildBlock = ({
|
|
id,
|
|
name,
|
|
elements,
|
|
logic,
|
|
logicFallback,
|
|
buttonLabel,
|
|
backButtonLabel,
|
|
t,
|
|
}: {
|
|
id?: string;
|
|
name: string;
|
|
elements: TSurveyElement[];
|
|
logic?: TSurveyBlockLogic[];
|
|
logicFallback?: string;
|
|
buttonLabel?: string;
|
|
backButtonLabel?: string;
|
|
t: TFunction;
|
|
}): TSurveyBlock => {
|
|
return {
|
|
id: id ?? createId(),
|
|
name,
|
|
elements,
|
|
logic,
|
|
logicFallback,
|
|
buttonLabel: buttonLabel ? getDefaultButtonLabel(buttonLabel, t) : createI18nString(t(""), []),
|
|
backButtonLabel: backButtonLabel
|
|
? getDefaultBackButtonLabel(backButtonLabel, t)
|
|
: createI18nString(t(""), []),
|
|
};
|
|
};
|