chore: remove unused templates functions for questions, fix linting issues

This commit is contained in:
Matti Nannt
2025-11-12 10:54:59 +01:00
parent d9b6b550a9
commit 3ea81dc7c1
18 changed files with 129 additions and 355 deletions
+4 -282
View File
@@ -2,291 +2,16 @@ import { createId } from "@paralleldrive/cuid2";
import type { TFunction } from "i18next";
import type { TSurveyBlock } from "@formbricks/types/surveys/blocks";
import type {
TShuffleOption,
TSurveyCTAQuestion,
TSurveyConsentQuestion,
TSurveyEndScreenCard,
TSurveyEnding,
TSurveyHiddenFields,
TSurveyLanguage,
TSurveyLogic,
TSurveyMultipleChoiceQuestion,
TSurveyNPSQuestion,
TSurveyOpenTextQuestion,
TSurveyOpenTextQuestionInputType,
TSurveyQuestion,
TSurveyRatingQuestion,
TSurveyWelcomeCard,
} from "@formbricks/types/surveys/types";
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
import type { TTemplate, TTemplateRole } from "@formbricks/types/templates";
import { createI18nString, extractLanguageCodes } 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 buildMultipleChoiceQuestion = ({
id,
headline,
type,
subheader,
choices,
choiceIds,
buttonLabel,
backButtonLabel,
shuffleOption,
required,
logic,
containsOther = false,
t,
}: {
id?: string;
headline: string;
type: TSurveyQuestionTypeEnum.MultipleChoiceMulti | TSurveyQuestionTypeEnum.MultipleChoiceSingle;
subheader?: string;
choices: string[];
choiceIds?: string[];
buttonLabel?: string;
backButtonLabel?: string;
shuffleOption?: TShuffleOption;
required?: boolean;
logic?: TSurveyLogic[];
containsOther?: boolean;
t: TFunction;
}): TSurveyMultipleChoiceQuestion => {
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, []) };
}),
buttonLabel: getDefaultButtonLabel(buttonLabel, t),
backButtonLabel: getDefaultBackButtonLabel(backButtonLabel, t),
shuffleOption: shuffleOption || "none",
required: required ?? false,
logic,
};
};
export const buildOpenTextQuestion = ({
id,
headline,
subheader,
placeholder,
inputType,
buttonLabel,
backButtonLabel,
required,
logic,
longAnswer,
t,
}: {
id?: string;
headline: string;
subheader?: string;
placeholder?: string;
buttonLabel?: string;
backButtonLabel?: string;
required?: boolean;
logic?: TSurveyLogic[];
inputType: TSurveyOpenTextQuestionInputType;
longAnswer?: boolean;
t: TFunction;
}): TSurveyOpenTextQuestion => {
return {
id: id ?? createId(),
type: TSurveyQuestionTypeEnum.OpenText,
inputType,
subheader: subheader ? createI18nString(subheader, []) : undefined,
placeholder: placeholder ? createI18nString(placeholder, []) : undefined,
headline: createI18nString(headline, []),
buttonLabel: getDefaultButtonLabel(buttonLabel, t),
backButtonLabel: getDefaultBackButtonLabel(backButtonLabel, t),
required: required ?? false,
longAnswer,
logic,
charLimit: {
enabled: false,
},
};
};
export const buildRatingQuestion = ({
id,
headline,
subheader,
scale,
range,
lowerLabel,
upperLabel,
buttonLabel,
backButtonLabel,
required,
logic,
isColorCodingEnabled = false,
t,
}: {
id?: string;
headline: string;
scale: TSurveyRatingQuestion["scale"];
range: TSurveyRatingQuestion["range"];
lowerLabel?: string;
upperLabel?: string;
subheader?: string;
placeholder?: string;
buttonLabel?: string;
backButtonLabel?: string;
required?: boolean;
logic?: TSurveyLogic[];
isColorCodingEnabled?: boolean;
t: TFunction;
}): TSurveyRatingQuestion => {
return {
id: id ?? createId(),
type: TSurveyQuestionTypeEnum.Rating,
subheader: subheader ? createI18nString(subheader, []) : undefined,
headline: createI18nString(headline, []),
scale,
range,
buttonLabel: getDefaultButtonLabel(buttonLabel, t),
backButtonLabel: getDefaultBackButtonLabel(backButtonLabel, t),
required: required ?? false,
isColorCodingEnabled,
lowerLabel: lowerLabel ? createI18nString(lowerLabel, []) : undefined,
upperLabel: upperLabel ? createI18nString(upperLabel, []) : undefined,
logic,
};
};
export const buildNPSQuestion = ({
id,
headline,
subheader,
lowerLabel,
upperLabel,
buttonLabel,
backButtonLabel,
required,
logic,
isColorCodingEnabled = false,
t,
}: {
id?: string;
headline: string;
lowerLabel?: string;
upperLabel?: string;
subheader?: string;
placeholder?: string;
buttonLabel?: string;
backButtonLabel?: string;
required?: boolean;
logic?: TSurveyLogic[];
isColorCodingEnabled?: boolean;
t: TFunction;
}): TSurveyNPSQuestion => {
return {
id: id ?? createId(),
type: TSurveyQuestionTypeEnum.NPS,
subheader: subheader ? createI18nString(subheader, []) : undefined,
headline: createI18nString(headline, []),
buttonLabel: getDefaultButtonLabel(buttonLabel, t),
backButtonLabel: getDefaultBackButtonLabel(backButtonLabel, t),
required: required ?? false,
isColorCodingEnabled,
lowerLabel: lowerLabel ? createI18nString(lowerLabel, []) : undefined,
upperLabel: upperLabel ? createI18nString(upperLabel, []) : undefined,
logic,
};
};
export const buildConsentQuestion = ({
id,
headline,
subheader,
label,
buttonLabel,
backButtonLabel,
required,
logic,
t,
}: {
id?: string;
headline: string;
subheader: string;
buttonLabel?: string;
backButtonLabel?: string;
required?: boolean;
logic?: TSurveyLogic[];
label: string;
t: TFunction;
}): TSurveyConsentQuestion => {
return {
id: id ?? createId(),
type: TSurveyQuestionTypeEnum.Consent,
subheader: createI18nString(subheader, []),
headline: createI18nString(headline, []),
buttonLabel: getDefaultButtonLabel(buttonLabel, t),
backButtonLabel: getDefaultBackButtonLabel(backButtonLabel, t),
required: required ?? false,
label: createI18nString(label, []),
logic,
};
};
export const buildCTAQuestion = ({
id,
headline,
subheader,
buttonLabel,
buttonExternal,
backButtonLabel,
required,
logic,
dismissButtonLabel,
buttonUrl,
t,
}: {
id?: string;
headline: string;
buttonExternal: boolean;
subheader: string;
buttonLabel?: string;
backButtonLabel?: string;
required?: boolean;
logic?: TSurveyLogic[];
dismissButtonLabel?: string;
buttonUrl?: string;
t: TFunction;
}): TSurveyCTAQuestion => {
return {
id: id ?? createId(),
type: TSurveyQuestionTypeEnum.CTA,
subheader: createI18nString(subheader, []),
headline: createI18nString(headline, []),
buttonLabel: getDefaultButtonLabel(buttonLabel, t),
backButtonLabel: getDefaultBackButtonLabel(backButtonLabel, t),
dismissButtonLabel: dismissButtonLabel ? createI18nString(dismissButtonLabel, []) : undefined,
required: required ?? false,
buttonExternal,
buttonUrl,
logic,
};
};
// Helper function to create standard jump logic based on operator
export const createJumpLogic = (
sourceQuestionId: string,
@@ -385,14 +110,13 @@ export const getDefaultSurveyPreset = (t: TFunction): TTemplate["preset"] => {
welcomeCard: getDefaultWelcomeCard(t),
endings: [getDefaultEndingCard([], t)],
hiddenFields: hiddenFieldsDefault,
questions: [],
blocks: [],
};
};
/**
* Generic builder for survey.
* @param config - The configuration for survey settings and questions/blocks.
* @param config - The configuration for survey settings and blocks.
* @param t - The translation function.
*/
export const buildSurvey = (
@@ -402,10 +126,9 @@ export const buildSurvey = (
channels: ("link" | "app" | "website")[];
role: TTemplateRole;
description: string;
questions?: TSurveyQuestion[];
blocks?: TSurveyBlock[];
endings?: TSurveyEnding[];
hiddenFields?: TSurveyHiddenFields;
blocks: TSurveyBlock[];
endings: TSurveyEnding[];
hiddenFields: TSurveyHiddenFields;
},
t: TFunction
): TTemplate => {
@@ -420,7 +143,6 @@ export const buildSurvey = (
...localSurvey,
name: config.name,
blocks: config.blocks ?? [],
questions: config.questions ?? [],
endings: config.endings ?? localSurvey.endings,
hiddenFields: config.hiddenFields ?? hiddenFieldsDefault,
},
+119 -16
View File
@@ -15,12 +15,7 @@ import {
createBlockChoiceJumpLogic,
createBlockJumpLogic,
} from "@/app/lib/survey-block-builder";
import {
buildSurvey,
getDefaultEndingCard,
getDefaultSurveyPreset,
hiddenFieldsDefault,
} from "@/app/lib/survey-builder";
import { buildSurvey, getDefaultSurveyPreset, hiddenFieldsDefault } from "@/app/lib/survey-builder";
import { createI18nString } from "@/lib/i18n/utils";
const cartAbandonmentSurvey = (t: TFunction): TTemplate => {
@@ -36,6 +31,7 @@ const cartAbandonmentSurvey = (t: TFunction): TTemplate => {
channels: ["app", "website", "link"],
description: t("templates.card_abandonment_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -179,6 +175,7 @@ const siteAbandonmentSurvey = (t: TFunction): TTemplate => {
channels: ["app", "website"],
description: t("templates.site_abandonment_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -319,6 +316,7 @@ const productMarketFitSuperhuman = (t: TFunction): TTemplate => {
channels: ["app", "link"],
description: t("templates.product_market_fit_superhuman_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -417,6 +415,7 @@ const productMarketFitSuperhuman = (t: TFunction): TTemplate => {
};
const onboardingSegmentation = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.onboarding_segmentation"),
@@ -424,6 +423,8 @@ const onboardingSegmentation = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app", "link"],
description: t("templates.onboarding_segmentation_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -508,6 +509,7 @@ const churnSurvey = (t: TFunction): TTemplate => {
channels: ["app", "link"],
description: t("templates.churn_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -633,6 +635,7 @@ const earnedAdvocacyScore = (t: TFunction): TTemplate => {
channels: ["app", "link"],
description: t("templates.earned_advocacy_score_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -727,6 +730,7 @@ const earnedAdvocacyScore = (t: TFunction): TTemplate => {
};
const usabilityScoreRatingSurvey = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.usability_score_name"),
@@ -734,6 +738,8 @@ const usabilityScoreRatingSurvey = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app", "link"],
description: t("templates.usability_rating_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -916,6 +922,7 @@ const improveTrialConversion = (t: TFunction): TTemplate => {
channels: ["link", "app"],
description: t("templates.improve_trial_conversion_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1058,6 +1065,7 @@ const reviewPrompt = (t: TFunction): TTemplate => {
channels: ["link", "app"],
description: t("templates.review_prompt_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1145,6 +1153,7 @@ const reviewPrompt = (t: TFunction): TTemplate => {
};
const interviewPrompt = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.interview_prompt_name"),
@@ -1152,6 +1161,8 @@ const interviewPrompt = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.interview_prompt_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1189,6 +1200,7 @@ const improveActivationRate = (t: TFunction): TTemplate => {
channels: ["link"],
description: t("templates.improve_activation_rate_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1305,6 +1317,7 @@ const improveActivationRate = (t: TFunction): TTemplate => {
};
const employeeSatisfaction = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.employee_satisfaction_name"),
@@ -1312,6 +1325,8 @@ const employeeSatisfaction = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["app", "link"],
description: t("templates.employee_satisfaction_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1413,6 +1428,7 @@ const employeeSatisfaction = (t: TFunction): TTemplate => {
};
const uncoverStrengthsAndWeaknesses = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.uncover_strengths_and_weaknesses_name"),
@@ -1420,6 +1436,8 @@ const uncoverStrengthsAndWeaknesses = (t: TFunction): TTemplate => {
industries: ["saas", "other"],
channels: ["app", "link"],
description: t("templates.uncover_strengths_and_weaknesses_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1481,6 +1499,7 @@ const uncoverStrengthsAndWeaknesses = (t: TFunction): TTemplate => {
};
const productMarketFitShort = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.product_market_fit_short_name"),
@@ -1488,6 +1507,8 @@ const productMarketFitShort = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app", "link"],
description: t("templates.product_market_fit_short_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1527,6 +1548,7 @@ const productMarketFitShort = (t: TFunction): TTemplate => {
};
const marketAttribution = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.market_attribution_name"),
@@ -1534,6 +1556,8 @@ const marketAttribution = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce"],
channels: ["website", "app", "link"],
description: t("templates.market_attribution_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1563,6 +1587,7 @@ const marketAttribution = (t: TFunction): TTemplate => {
};
const changingSubscriptionExperience = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.changing_subscription_experience_name"),
@@ -1570,6 +1595,8 @@ const changingSubscriptionExperience = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.changing_subscription_experience_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1616,6 +1643,7 @@ const changingSubscriptionExperience = (t: TFunction): TTemplate => {
};
const identifyCustomerGoals = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.identify_customer_goals_name"),
@@ -1623,6 +1651,8 @@ const identifyCustomerGoals = (t: TFunction): TTemplate => {
industries: ["saas", "other"],
channels: ["app", "website"],
description: t("templates.identify_customer_goals_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1650,6 +1680,7 @@ const identifyCustomerGoals = (t: TFunction): TTemplate => {
};
const featureChaser = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.feature_chaser_name"),
@@ -1657,6 +1688,8 @@ const featureChaser = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.feature_chaser_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1699,6 +1732,7 @@ const featureChaser = (t: TFunction): TTemplate => {
};
const fakeDoorFollowUp = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.fake_door_follow_up_name"),
@@ -1706,6 +1740,8 @@ const fakeDoorFollowUp = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce"],
channels: ["app", "website"],
description: t("templates.fake_door_follow_up_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1763,6 +1799,7 @@ const feedbackBox = (t: TFunction): TTemplate => {
channels: ["app"],
description: t("templates.feedback_box_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1848,6 +1885,7 @@ const feedbackBox = (t: TFunction): TTemplate => {
const integrationSetupSurvey = (t: TFunction): TTemplate => {
const reusableElementIds = [createId(), createId(), createId()];
const block3Id = createId(); // Pre-generate ID for Block 3 (referenced by Block 1 logic)
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
@@ -1856,6 +1894,8 @@ const integrationSetupSurvey = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.integration_setup_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1938,6 +1978,7 @@ const integrationSetupSurvey = (t: TFunction): TTemplate => {
};
const newIntegrationSurvey = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.new_integration_survey_name"),
@@ -1945,6 +1986,8 @@ const newIntegrationSurvey = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.new_integration_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -1974,6 +2017,7 @@ const newIntegrationSurvey = (t: TFunction): TTemplate => {
};
const docsFeedback = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.docs_feedback_name"),
@@ -1981,6 +2025,8 @@ const docsFeedback = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app", "website", "link"],
description: t("templates.docs_feedback_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2028,6 +2074,7 @@ const docsFeedback = (t: TFunction): TTemplate => {
};
const nps = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.nps_name"),
@@ -2035,6 +2082,8 @@ const nps = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["app", "link", "website"],
description: t("templates.nps_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2067,6 +2116,7 @@ const nps = (t: TFunction): TTemplate => {
};
const customerSatisfactionScore = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.csat_name"),
@@ -2074,6 +2124,8 @@ const customerSatisfactionScore = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["app", "link", "website"],
description: t("templates.csat_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2278,6 +2330,7 @@ const collectFeedback = (t: TFunction): TTemplate => {
];
const block3Id = createId(); // Pre-generate IDs for blocks referenced by logic
const block4Id = createId();
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.collect_feedback_name"),
@@ -2285,6 +2338,8 @@ const collectFeedback = (t: TFunction): TTemplate => {
industries: ["other", "eCommerce"],
channels: ["website", "link"],
description: t("templates.collect_feedback_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2462,6 +2517,7 @@ const collectFeedback = (t: TFunction): TTemplate => {
};
const identifyUpsellOpportunities = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.identify_upsell_opportunities_name"),
@@ -2469,6 +2525,8 @@ const identifyUpsellOpportunities = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app", "link"],
description: t("templates.identify_upsell_opportunities_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2496,6 +2554,7 @@ const identifyUpsellOpportunities = (t: TFunction): TTemplate => {
};
const prioritizeFeatures = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.prioritize_features_name"),
@@ -2503,6 +2562,8 @@ const prioritizeFeatures = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.prioritize_features_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2560,6 +2621,7 @@ const prioritizeFeatures = (t: TFunction): TTemplate => {
};
const gaugeFeatureSatisfaction = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.gauge_feature_satisfaction_name"),
@@ -2567,6 +2629,8 @@ const gaugeFeatureSatisfaction = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.gauge_feature_satisfaction_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2596,14 +2660,13 @@ const gaugeFeatureSatisfaction = (t: TFunction): TTemplate => {
t,
}),
],
endings: [getDefaultEndingCard([], t)],
hiddenFields: hiddenFieldsDefault,
},
t
);
};
const marketSiteClarity = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.market_site_clarity_name"),
@@ -2611,6 +2674,8 @@ const marketSiteClarity = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["website"],
description: t("templates.market_site_clarity_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2661,6 +2726,7 @@ const marketSiteClarity = (t: TFunction): TTemplate => {
};
const customerEffortScore = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.customer_effort_score_name"),
@@ -2668,6 +2734,8 @@ const customerEffortScore = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app"],
description: t("templates.customer_effort_score_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2703,6 +2771,7 @@ const customerEffortScore = (t: TFunction): TTemplate => {
};
const careerDevelopmentSurvey = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.career_development_survey_name"),
@@ -2710,6 +2779,8 @@ const careerDevelopmentSurvey = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.career_development_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2818,6 +2889,7 @@ const careerDevelopmentSurvey = (t: TFunction): TTemplate => {
};
const professionalDevelopmentSurvey = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.professional_development_survey_name"),
@@ -2825,6 +2897,8 @@ const professionalDevelopmentSurvey = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.professional_development_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -2935,6 +3009,7 @@ const rateCheckoutExperience = (t: TFunction): TTemplate => {
channels: ["website", "app"],
description: t("templates.rate_checkout_experience_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3029,6 +3104,7 @@ const measureSearchExperience = (t: TFunction): TTemplate => {
channels: ["app", "website"],
description: t("templates.measure_search_experience_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3123,6 +3199,7 @@ const evaluateContentQuality = (t: TFunction): TTemplate => {
channels: ["website"],
description: t("templates.evaluate_content_quality_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3220,6 +3297,7 @@ const measureTaskAccomplishment = (t: TFunction): TTemplate => {
channels: ["app", "website"],
description: t("templates.measure_task_accomplishment_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3436,6 +3514,7 @@ const identifySignUpBarriers = (t: TFunction): TTemplate => {
channels: ["website"],
description: t("templates.identify_sign_up_barriers_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3631,6 +3710,7 @@ const identifySignUpBarriers = (t: TFunction): TTemplate => {
};
const buildProductRoadmap = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.build_product_roadmap_name"),
@@ -3638,6 +3718,8 @@ const buildProductRoadmap = (t: TFunction): TTemplate => {
industries: ["saas"],
channels: ["app", "link"],
description: t("templates.build_product_roadmap_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3686,6 +3768,7 @@ const understandPurchaseIntention = (t: TFunction): TTemplate => {
channels: ["website", "link", "app"],
description: t("templates.understand_purchase_intention_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3761,6 +3844,7 @@ const improveNewsletterContent = (t: TFunction): TTemplate => {
channels: ["link"],
description: t("templates.improve_newsletter_content_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -3866,6 +3950,7 @@ const evaluateAProductIdea = (t: TFunction): TTemplate => {
const block6Id = createId();
const block7Id = createId();
const block8Id = createId();
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.evaluate_a_product_idea_name"),
@@ -3873,6 +3958,8 @@ const evaluateAProductIdea = (t: TFunction): TTemplate => {
industries: ["saas", "other"],
channels: ["link", "app"],
description: t("templates.evaluate_a_product_idea_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4026,6 +4113,7 @@ const understandLowEngagement = (t: TFunction): TTemplate => {
channels: ["link"],
description: t("templates.understand_low_engagement_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4143,6 +4231,7 @@ const understandLowEngagement = (t: TFunction): TTemplate => {
};
const employeeWellBeing = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.employee_well_being_name"),
@@ -4150,6 +4239,8 @@ const employeeWellBeing = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.employee_well_being_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4213,6 +4304,7 @@ const employeeWellBeing = (t: TFunction): TTemplate => {
};
const longTermRetentionCheckIn = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.long_term_retention_check_in_name"),
@@ -4220,6 +4312,8 @@ const longTermRetentionCheckIn = (t: TFunction): TTemplate => {
industries: ["saas", "other"],
channels: ["app", "link"],
description: t("templates.long_term_retention_check_in_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4373,6 +4467,7 @@ const longTermRetentionCheckIn = (t: TFunction): TTemplate => {
};
const professionalDevelopmentGrowth = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.professional_development_growth_survey_name"),
@@ -4380,6 +4475,8 @@ const professionalDevelopmentGrowth = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.professional_development_growth_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4443,6 +4540,7 @@ const professionalDevelopmentGrowth = (t: TFunction): TTemplate => {
};
const recognitionAndReward = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.recognition_and_reward_survey_name"),
@@ -4450,6 +4548,8 @@ const recognitionAndReward = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.recognition_and_reward_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4513,6 +4613,7 @@ const recognitionAndReward = (t: TFunction): TTemplate => {
};
const alignmentAndEngagement = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.alignment_and_engagement_survey_name"),
@@ -4520,6 +4621,8 @@ const alignmentAndEngagement = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.alignment_and_engagement_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4582,6 +4685,7 @@ const alignmentAndEngagement = (t: TFunction): TTemplate => {
};
const supportiveWorkCulture = (t: TFunction): TTemplate => {
const localSurvey = getDefaultSurveyPreset(t);
return buildSurvey(
{
name: t("templates.supportive_work_culture_survey_name"),
@@ -4589,6 +4693,8 @@ const supportiveWorkCulture = (t: TFunction): TTemplate => {
industries: ["saas", "eCommerce", "other"],
channels: ["link"],
description: t("templates.supportive_work_culture_survey_description"),
endings: localSurvey.endings,
hiddenFields: hiddenFieldsDefault,
blocks: [
buildBlock({
name: "Block 1",
@@ -4710,7 +4816,6 @@ export const customSurveyTemplate = (t: TFunction): TTemplate => {
preset: {
...getDefaultSurveyPreset(t),
name: t("templates.custom_survey_name"),
questions: [],
blocks: [
{
id: createId(),
@@ -4735,18 +4840,17 @@ export const customSurveyTemplate = (t: TFunction): TTemplate => {
};
};
export const previewSurvey = (projectName: string, t: TFunction) => {
export const previewSurvey = (projectName: string, t: TFunction): TSurvey => {
return {
id: "cltxxaa6x0000g8hacxdxejeu",
createdAt: new Date(),
updatedAt: new Date(),
name: t("templates.preview_survey_name"),
type: "link",
type: "link" as const,
environmentId: "cltwumfcz0009echxg02fh7oa",
createdBy: "cltwumfbz0000echxysz6ptvq",
status: "inProgress",
status: "inProgress" as const,
welcomeCard: {
html: createI18nString(t("templates.preview_survey_welcome_card_html"), []),
enabled: false,
headline: createI18nString(t("templates.preview_survey_welcome_card_headline"), []),
timeToFinish: false,
@@ -4754,7 +4858,6 @@ export const previewSurvey = (projectName: string, t: TFunction) => {
},
styling: null,
segment: null,
questions: [],
blocks: [
{
id: createId(),
@@ -4821,7 +4924,6 @@ export const previewSurvey = (projectName: string, t: TFunction) => {
autoComplete: 50,
isVerifyEmailEnabled: false,
isSingleResponsePerEmailEnabled: false,
redirectUrl: null,
projectOverwrites: null,
surveyClosedMessage: null,
singleUse: {
@@ -4835,5 +4937,6 @@ export const previewSurvey = (projectName: string, t: TFunction) => {
followUps: [],
isBackButtonHidden: false,
metadata: {},
} as TSurvey;
questions: [], // Required for build-time type checking (Zod defaults to [] at runtime)
};
};
-1
View File
@@ -2511,7 +2511,6 @@ checksums:
templates/preview_survey_question_2_choice_2_label: 1af148222f327f28cf0db6513de5989e
templates/preview_survey_question_2_headline: 5cfb173d156555227fbc2c97ad921e72
templates/preview_survey_welcome_card_headline: 8778dc41547a2778d0f9482da989fc00
templates/preview_survey_welcome_card_html: 5fc24f7cfeba1af9a3fc3ddb6fb67de4
templates/prioritize_features_description: 1eae41fad0e3947f803d8539081e59ec
templates/prioritize_features_name: 4ca59ff1f9c319aaa68c3106d820fd6a
templates/prioritize_features_question_1_choice_1: 7c0b2da44eacc271073d4f15caaa86c8
-2
View File
@@ -647,8 +647,6 @@ export const performActions = (
requiredQuestionIds.push(action.target);
break;
case "jumpToBlock":
case "jumpToQuestion":
// Backward compatibility: handle old question-level logic (jumpToQuestion)
if (!jumpTarget) {
jumpTarget = action.target;
}
-30
View File
@@ -1,32 +1,9 @@
import type { TProject } from "@formbricks/types/project";
import type { TSurveyElement } from "@formbricks/types/surveys/elements";
import type { TSurveyQuestion } from "@formbricks/types/surveys/types";
import type { TTemplate } from "@formbricks/types/templates";
import { getLocalizedValue } from "@/lib/i18n/utils";
import { structuredClone } from "@/lib/pollyfills/structuredClone";
export const replaceQuestionPresetPlaceholders = (
question: TSurveyQuestion,
project: TProject
): TSurveyQuestion => {
if (!project) return question;
const newQuestion = structuredClone(question);
const defaultLanguageCode = "default";
if (newQuestion.headline) {
newQuestion.headline[defaultLanguageCode] = getLocalizedValue(
newQuestion.headline,
defaultLanguageCode
).replace("$[projectName]", project.name);
}
if (newQuestion.subheader) {
newQuestion.subheader[defaultLanguageCode] = getLocalizedValue(
newQuestion.subheader,
defaultLanguageCode
)?.replace("$[projectName]", project.name);
}
return newQuestion;
};
export const replaceElementPresetPlaceholders = (
element: TSurveyElement,
project: TProject
@@ -65,12 +42,5 @@ export const replacePresetPlaceholders = (template: TTemplate, project: any) =>
}));
}
// Handle questions for backward compatibility
if (preset.questions && preset.questions.length > 0) {
preset.questions = preset.questions.map((question) => {
return replaceQuestionPresetPlaceholders(question, project);
});
}
return { ...template, preset };
};
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "Nein, danke!",
"preview_survey_question_2_headline": "Möchtest Du auf dem Laufenden bleiben?",
"preview_survey_welcome_card_headline": "Willkommen!",
"preview_survey_welcome_card_html": "Danke für dein Feedback - los geht's!",
"prioritize_features_description": "Identifiziere die Funktionen, die deine Nutzer am meisten und am wenigsten brauchen.",
"prioritize_features_name": "Funktionen priorisieren",
"prioritize_features_question_1_choice_1": "Funktion 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "No, thank you!",
"preview_survey_question_2_headline": "Want to stay in the loop?",
"preview_survey_welcome_card_headline": "Welcome!",
"preview_survey_welcome_card_html": "Thanks for providing your feedback - let's go!",
"prioritize_features_description": "Identify features your users need most and least.",
"prioritize_features_name": "Prioritize Features",
"prioritize_features_question_1_choice_1": "Feature 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "Non, merci !",
"preview_survey_question_2_headline": "Souhaitez-vous être informé ?",
"preview_survey_welcome_card_headline": "Bienvenue !",
"preview_survey_welcome_card_html": "Merci pour vos retours - allons-y !",
"prioritize_features_description": "Identifiez les fonctionnalités dont vos utilisateurs ont le plus et le moins besoin.",
"prioritize_features_name": "Prioriser les fonctionnalités",
"prioritize_features_question_1_choice_1": "Fonctionnalité 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "いいえ、結構です!",
"preview_survey_question_2_headline": "最新情報を知りたいですか?",
"preview_survey_welcome_card_headline": "ようこそ!",
"preview_survey_welcome_card_html": "フィードバックありがとうございます - さあ、始めましょう!",
"prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。",
"prioritize_features_name": "機能の優先順位付け",
"prioritize_features_question_1_choice_1": "機能1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
"preview_survey_question_2_headline": "Quer ficar por dentro?",
"preview_survey_welcome_card_headline": "Bem-vindo!",
"preview_survey_welcome_card_html": "Valeu pelo feedback - bora lá!",
"prioritize_features_description": "Identifique os recursos que seus usuários mais e menos precisam.",
"prioritize_features_name": "Priorizar Funcionalidades",
"prioritize_features_question_1_choice_1": "Recurso 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
"preview_survey_question_2_headline": "Quer manter-se atualizado?",
"preview_survey_welcome_card_headline": "Bem-vindo!",
"preview_survey_welcome_card_html": "Obrigado por fornecer o seu feedback - vamos a isso!",
"prioritize_features_description": "Identifique as funcionalidades que os seus utilizadores precisam mais e menos.",
"prioritize_features_name": "Priorizar Funcionalidades",
"prioritize_features_question_1_choice_1": "Funcionalidade 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "Nu, mulţumesc!",
"preview_survey_question_2_headline": "Vrei să fii în temă?",
"preview_survey_welcome_card_headline": "Bun venit!",
"preview_survey_welcome_card_html": "Mulțumesc pentru feedback-ul dvs - să începem!",
"prioritize_features_description": "Identificați caracteristicile de care utilizatorii dumneavoastră au cel mai mult și cel mai puțin nevoie.",
"prioritize_features_name": "Prioritizați caracteristicile",
"prioritize_features_question_1_choice_1": "Caracteristica 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "不,谢谢!",
"preview_survey_question_2_headline": "想 了解 最新信息吗?",
"preview_survey_welcome_card_headline": "欢迎!",
"preview_survey_welcome_card_html": "感谢 提供 您 的 反馈 - 一起 出发!",
"prioritize_features_description": "确定 用户 最 需要 和 最 不 需要 的 功能。",
"prioritize_features_name": "优先 功能",
"prioritize_features_question_1_choice_1": "功能 1",
-1
View File
@@ -2658,7 +2658,6 @@
"preview_survey_question_2_choice_2_label": "不用了,謝謝!",
"preview_survey_question_2_headline": "想要緊跟最新動態嗎?",
"preview_survey_welcome_card_headline": "歡迎!",
"preview_survey_welcome_card_html": "感謝您提供回饋 - 開始吧!",
"prioritize_features_description": "找出您的使用者最需要和最不需要的功能。",
"prioritize_features_name": "優先排序功能",
"prioritize_features_question_1_choice_1": "功能 1",
@@ -109,7 +109,7 @@ export const TemplateTags = ({ template, selectedFilter }: TemplateTagsProps) =>
{channelTag}
</div>
)}
{template.preset.questions.some((question) => question.logic && question.logic.length > 0) && (
{template.preset.blocks.some((block) => block.logic && block.logic.length > 0) && (
<TooltipRenderer
tooltipContent={t("environments.surveys.templates.uses_branching_logic")}
shouldRender={true}>
+2 -2
View File
@@ -38,7 +38,7 @@ import {
TSurveyRatingElement,
} from "@formbricks/types/surveys/elements";
import { createI18nString } from "@/lib/i18n/utils";
import { replaceQuestionPresetPlaceholders } from "@/lib/utils/templates";
import { replaceElementPresetPlaceholders } from "@/lib/utils/templates";
export type TQuestion = {
id: string;
@@ -346,7 +346,7 @@ export const universalQuestionPresets = {
export const getQuestionDefaults = (id: string, project: any, t: TFunction) => {
const questionType = getQuestionTypes(t).find((questionType) => questionType.id === id);
return replaceQuestionPresetPlaceholders(questionType?.preset, project);
return replaceElementPresetPlaceholders(questionType?.preset, project);
};
export const getTSurveyQuestionTypeEnumName = (id: string, t: TFunction) => {
@@ -28,7 +28,7 @@ export const TemplateContainerWithPreview = ({
const initialTemplate = customSurveyTemplate(t);
const [activeTemplate, setActiveTemplate] = useState<TTemplate>(initialTemplate);
const [activeQuestionId, setActiveQuestionId] = useState<string>(
initialTemplate.preset.questions[0]?.id || initialTemplate.preset.blocks[0]?.elements[0]?.id || ""
initialTemplate.preset.blocks[0]?.elements[0]?.id || ""
);
const [templateSearch, setTemplateSearch] = useState<string | null>(null);
@@ -58,9 +58,7 @@ export const TemplateContainerWithPreview = ({
userId={userId}
templateSearch={templateSearch ?? ""}
onTemplateClick={(template) => {
setActiveQuestionId(
template.preset.questions[0]?.id || template.preset.blocks[0]?.elements[0]?.id || ""
);
setActiveQuestionId(template.preset.blocks[0]?.elements[0]?.id || "");
setActiveTemplate(template);
}}
/>
+1 -8
View File
@@ -1,13 +1,7 @@
import { z } from "zod";
import { ZProjectConfigChannel, ZProjectConfigIndustry } from "./project";
import { ZSurveyBlocks } from "./surveys/blocks";
import {
ZSurveyEndings,
ZSurveyHiddenFields,
ZSurveyQuestions,
ZSurveyStyling,
ZSurveyWelcomeCard,
} from "./surveys/types";
import { ZSurveyEndings, ZSurveyHiddenFields, ZSurveyStyling, ZSurveyWelcomeCard } from "./surveys/types";
export const ZTemplateRole = z.enum([
"productManager",
@@ -29,7 +23,6 @@ export const ZTemplate = z.object({
name: z.string(),
welcomeCard: ZSurveyWelcomeCard,
blocks: ZSurveyBlocks.default([]),
questions: ZSurveyQuestions.default([]),
endings: ZSurveyEndings,
hiddenFields: ZSurveyHiddenFields,
}),