From 53a9b218bc5a4fc4a038a705ffbca70206657b0a Mon Sep 17 00:00:00 2001 From: pandeymangg Date: Wed, 5 Nov 2025 10:26:20 +0530 Subject: [PATCH] fixes coderabbit feedback --- apps/web/app/lib/templates.ts | 2 +- apps/web/i18n.lock | 1 - apps/web/locales/de-DE.json | 1 + apps/web/locales/en-US.json | 1 + apps/web/locales/fr-FR.json | 1 + apps/web/locales/ja-JP.json | 1 + apps/web/locales/pt-BR.json | 1 + apps/web/locales/pt-PT.json | 1 + apps/web/locales/ro-RO.json | 1 + apps/web/locales/zh-Hans-CN.json | 1 + apps/web/locales/zh-Hant-TW.json | 1 + .../ee/quotas/components/quota-modal.tsx | 1 - .../components/question-form-input/utils.ts | 3 +- .../survey/editor/components/logic-editor.tsx | 23 +++++++++-- apps/web/modules/survey/editor/lib/utils.tsx | 29 +++++++++---- .../surveys/src/components/general/survey.tsx | 3 +- packages/surveys/src/lib/logic.test.ts | 41 +++++++++---------- 17 files changed, 74 insertions(+), 38 deletions(-) diff --git a/apps/web/app/lib/templates.ts b/apps/web/app/lib/templates.ts index f382483004..7fece1af06 100644 --- a/apps/web/app/lib/templates.ts +++ b/apps/web/app/lib/templates.ts @@ -3603,7 +3603,7 @@ export const customSurveyTemplate = (t: TFunction): TTemplate => { blocks: [ { id: createId(), - name: "Block 1", + name: t("templates.custom_survey_block_1_name"), elements: [ { id: createId(), diff --git a/apps/web/i18n.lock b/apps/web/i18n.lock index b925c06b78..4bfc4e0972 100644 --- a/apps/web/i18n.lock +++ b/apps/web/i18n.lock @@ -1352,7 +1352,6 @@ checksums: environments/surveys/edit/is_skipped: 9fb90b6578f603cca37d4e6c912bb401 environments/surveys/edit/is_submitted: 13e774a97ad5f5609555e6f99514e70f environments/surveys/edit/italic: 555c60fb1d12ae305136202afa6deb3d - environments/surveys/edit/jump_to_block: 2fc00bd725c44f98861051c57bb2c392 environments/surveys/edit/jump_to_question: 742aabed8845190825418aa429f01b2d environments/surveys/edit/keep_current_order: a7c944ad6b3515f2c4f83a2c81f8fc26 environments/surveys/edit/keep_showing_while_conditions_match: 2574802d87bd6da151c9145aacce7281 diff --git a/apps/web/locales/de-DE.json b/apps/web/locales/de-DE.json index acce49db09..aed5639baf 100644 --- a/apps/web/locales/de-DE.json +++ b/apps/web/locales/de-DE.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "Ugh, sorry! Können wir irgendwas tun, um deine Erfahrung zu verbessern?", "csat_survey_question_3_placeholder": "Tippe deine Antwort hier...", "cta_description": "Information anzeigen und Benutzer auffordern, eine bestimmte Aktion auszuführen", + "custom_survey_block_1_name": "Block 1", "custom_survey_description": "Erstelle eine Umfrage ohne Vorlage.", "custom_survey_name": "Eigene Umfrage erstellen", "custom_survey_question_1_headline": "Was möchtest Du wissen?", diff --git a/apps/web/locales/en-US.json b/apps/web/locales/en-US.json index d337759bf0..90f7be88aa 100644 --- a/apps/web/locales/en-US.json +++ b/apps/web/locales/en-US.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "Ugh, sorry! Is there anything we can do to improve your experience?", "csat_survey_question_3_placeholder": "Type your answer here...", "cta_description": "Display information and prompt users to take a specific action", + "custom_survey_block_1_name": "Block 1", "custom_survey_description": "Create a survey without template.", "custom_survey_name": "Start from scratch", "custom_survey_question_1_headline": "What would you like to know?", diff --git a/apps/web/locales/fr-FR.json b/apps/web/locales/fr-FR.json index fa97edc177..07f050bf0a 100644 --- a/apps/web/locales/fr-FR.json +++ b/apps/web/locales/fr-FR.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "Ah, désolé ! Y a-t-il quelque chose que nous puissions faire pour améliorer votre expérience ?", "csat_survey_question_3_placeholder": "Entrez votre réponse ici...", "cta_description": "Afficher des informations et inciter les utilisateurs à effectuer une action spécifique", + "custom_survey_block_1_name": "Bloc 1", "custom_survey_description": "Créez une enquête sans utiliser de modèle.", "custom_survey_name": "Tout créer moi-même", "custom_survey_question_1_headline": "Que voudriez-vous savoir ?", diff --git a/apps/web/locales/ja-JP.json b/apps/web/locales/ja-JP.json index 4adc056e4e..97bbac5790 100644 --- a/apps/web/locales/ja-JP.json +++ b/apps/web/locales/ja-JP.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "申し訳ありません!体験を改善するために何かできることはありますか?", "csat_survey_question_3_placeholder": "ここに回答を入力してください...", "cta_description": "情報を表示し、特定の行動を促す", + "custom_survey_block_1_name": "ブロック1", "custom_survey_description": "テンプレートを使わずにアンケートを作成する。", "custom_survey_name": "最初から始める", "custom_survey_question_1_headline": "何を知りたいですか?", diff --git a/apps/web/locales/pt-BR.json b/apps/web/locales/pt-BR.json index c5fc905f68..f7cf75df55 100644 --- a/apps/web/locales/pt-BR.json +++ b/apps/web/locales/pt-BR.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "Ah, foi mal! Tem algo que a gente possa fazer pra melhorar sua experiência?", "csat_survey_question_3_placeholder": "Digite sua resposta aqui...", "cta_description": "Mostrar informações e pedir para os usuários tomarem uma ação específica", + "custom_survey_block_1_name": "Bloco 1", "custom_survey_description": "Crie uma pesquisa sem modelo.", "custom_survey_name": "Começar do zero", "custom_survey_question_1_headline": "O que você gostaria de saber?", diff --git a/apps/web/locales/pt-PT.json b/apps/web/locales/pt-PT.json index aefcd62fb9..6b2d1f4cdc 100644 --- a/apps/web/locales/pt-PT.json +++ b/apps/web/locales/pt-PT.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "Oh, desculpe! Há algo que possamos fazer para melhorar a sua experiência?", "csat_survey_question_3_placeholder": "Escreva a sua resposta aqui...", "cta_description": "Exibir informações e solicitar aos utilizadores que tomem uma ação específica", + "custom_survey_block_1_name": "Bloco 1", "custom_survey_description": "Crie um inquérito sem modelo.", "custom_survey_name": "Começar do zero", "custom_survey_question_1_headline": "O que gostaria de saber?", diff --git a/apps/web/locales/ro-RO.json b/apps/web/locales/ro-RO.json index ecfce4a45c..8603bfe8d2 100644 --- a/apps/web/locales/ro-RO.json +++ b/apps/web/locales/ro-RO.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "Of, îmi pare rău! Există ceva ce putem face pentru a-ți îmbunătăți experiența?", "csat_survey_question_3_placeholder": "Tastează răspunsul aici...", "cta_description": "Afișează informații și solicită utilizatorilor să ia o acțiune specifică", + "custom_survey_block_1_name": "Bloc 1", "custom_survey_description": "Creează un sondaj fără șablon.", "custom_survey_name": "Începe de la zero", "custom_survey_question_1_headline": "Ce ați dori să știți?", diff --git a/apps/web/locales/zh-Hans-CN.json b/apps/web/locales/zh-Hans-CN.json index a1cb87a0ee..fd8e6fd90f 100644 --- a/apps/web/locales/zh-Hans-CN.json +++ b/apps/web/locales/zh-Hans-CN.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "糟糕, 对不起!我们可以做些什么来改善您的体验?", "csat_survey_question_3_placeholder": "在此输入您的答案...", "cta_description": "显示 信息 并 提示用户采取 特定行动", + "custom_survey_block_1_name": "模块 1", "custom_survey_description": "创建 一个 没有 模板 的 调查。", "custom_survey_name": "从零开始", "custom_survey_question_1_headline": "你 想 知道 什么?", diff --git a/apps/web/locales/zh-Hant-TW.json b/apps/web/locales/zh-Hant-TW.json index 8a74808616..40f68b2c82 100644 --- a/apps/web/locales/zh-Hant-TW.json +++ b/apps/web/locales/zh-Hant-TW.json @@ -2248,6 +2248,7 @@ "csat_survey_question_3_headline": "唉,抱歉!我們是否有任何可以改善您體驗的地方?", "csat_survey_question_3_placeholder": "在此輸入您的答案...", "cta_description": "顯示資訊並提示使用者採取特定操作", + "custom_survey_block_1_name": "區塊 1", "custom_survey_description": "建立沒有範本的問卷。", "custom_survey_name": "從頭開始", "custom_survey_question_1_headline": "您想瞭解什麼?", diff --git a/apps/web/modules/ee/quotas/components/quota-modal.tsx b/apps/web/modules/ee/quotas/components/quota-modal.tsx index 4dc6f5783d..647a0f8d7a 100644 --- a/apps/web/modules/ee/quotas/components/quota-modal.tsx +++ b/apps/web/modules/ee/quotas/components/quota-modal.tsx @@ -81,7 +81,6 @@ export const QuotaModal = ({ const [openConfirmationModal, setOpenConfirmationModal] = useState(false); const [openConfirmChangesInInclusionCriteria, setOpenConfirmChangesInInclusionCriteria] = useState(false); - // Derive questions from blocks (with fallback to legacy questions) const questions = useMemo(() => { return survey.blocks.flatMap((block) => block.elements); }, [survey.blocks]); diff --git a/apps/web/modules/survey/components/question-form-input/utils.ts b/apps/web/modules/survey/components/question-form-input/utils.ts index 15cecebcd1..01dba22047 100644 --- a/apps/web/modules/survey/components/question-form-input/utils.ts +++ b/apps/web/modules/survey/components/question-form-input/utils.ts @@ -70,11 +70,12 @@ export const determineImageUploaderVisibility = (questionIdx: number, localSurve switch (questionIdx) { case -1: // Welcome Card return false; - default: + default: { // Regular Survey Question - derive questions from blocks const questions = localSurvey.blocks.flatMap((block) => block.elements); const question = questions[questionIdx]; return (!!question && !!question.imageUrl) || (!!question && !!question.videoUrl); + } } }; diff --git a/apps/web/modules/survey/editor/components/logic-editor.tsx b/apps/web/modules/survey/editor/components/logic-editor.tsx index b4759a6857..bd4bfb306e 100644 --- a/apps/web/modules/survey/editor/components/logic-editor.tsx +++ b/apps/web/modules/survey/editor/components/logic-editor.tsx @@ -49,6 +49,7 @@ export function LogicEditor({ const parentBlock = localSurvey.blocks?.find((block) => block.elements.some((element) => element.id === question.id) ); + const blockLogicFallback = parentBlock?.logicFallback; const fallbackOptions = useMemo(() => { @@ -62,15 +63,29 @@ export function LogicEditor({ const allQuestions = localSurvey.blocks.flatMap((b) => b.elements); const blocks = localSurvey.blocks; + // Track which blocks we've already added to avoid duplicates when a block has multiple elements + const addedBlockIds = new Set(); + + // Iterate over the questions AFTER the current question for (let i = questionIdx + 1; i < allQuestions.length; i++) { const ques = allQuestions[i]; - // Find block ID for this question const block = blocks.find((b) => b.elements.some((e) => e.id === ques.id)); + if (!block) continue; + + // Skip if we've already added this block + if (addedBlockIds.has(block.id)) continue; + + addedBlockIds.add(block.id); + + // Use the first element's headline as the block label + const firstElement = block.elements[0]; options.push({ - icon: QUESTIONS_ICON_MAP[ques.type], - label: getTextContent(recallToHeadline(ques.headline, localSurvey, false, "default").default ?? ""), - value: block?.id ?? ques.id, // Block ID if blocks exist, otherwise question ID + icon: QUESTIONS_ICON_MAP[firstElement.type], + label: getTextContent( + recallToHeadline(firstElement.headline, localSurvey, false, "default").default ?? "" + ), + value: block.id, }); } diff --git a/apps/web/modules/survey/editor/lib/utils.tsx b/apps/web/modules/survey/editor/lib/utils.tsx index d70075f6ec..02e2822205 100644 --- a/apps/web/modules/survey/editor/lib/utils.tsx +++ b/apps/web/modules/survey/editor/lib/utils.tsx @@ -953,17 +953,32 @@ export const getActionTargetOptions = ( } // For jumpToBlock, we need block IDs + // Track which blocks we've already added to avoid duplicates when a block has multiple elements const blocks = localSurvey.blocks ?? []; - const questionOptions = questions.map((question) => { + const addedBlockIds = new Set(); + const questionOptions: TComboboxOption[] = []; + + for (const question of questions) { // Find which block this question belongs to const block = blocks.find((b) => b.elements.some((e) => e.id === question.id)); - const processedHeadline = recallToHeadline(question.headline, localSurvey, false, "default"); - return { - icon: getQuestionIconMapping(t)[question.type], + + if (!block) continue; + + // Skip if we've already added this block + if (addedBlockIds.has(block.id)) continue; + + // Mark this block as added + addedBlockIds.add(block.id); + + // Use the first element's headline as the block label + const firstElement = block.elements[0]; + const processedHeadline = recallToHeadline(firstElement.headline, localSurvey, false, "default"); + questionOptions.push({ + icon: getQuestionIconMapping(t)[firstElement.type], label: getTextContent(processedHeadline.default ?? ""), - value: block?.id ?? question.id, // Block ID for jumpToBlock - }; - }); + value: block.id, + }); + } // Ending cards const endingCardOptions = localSurvey.endings.map((ending) => { diff --git a/packages/surveys/src/components/general/survey.tsx b/packages/surveys/src/components/general/survey.tsx index d72c0d7f2c..75d35fe45e 100644 --- a/packages/surveys/src/components/general/survey.tsx +++ b/packages/surveys/src/components/general/survey.tsx @@ -10,6 +10,7 @@ import type { TResponseVariables, } from "@formbricks/types/responses"; import { TUploadFileConfig } from "@formbricks/types/storage"; +import { TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks"; import { type TSurveyQuestionId } from "@formbricks/types/surveys/types"; import { EndingCard } from "@/components/general/ending-card"; import { ErrorComponent } from "@/components/general/error-component"; @@ -426,7 +427,7 @@ export function Survey({ ) { const { jumpTarget, requiredQuestionIds, calculations } = performActions( localSurvey, - logic.actions, + logic.actions as TSurveyBlockLogicAction[], //TODO: Temporary type assertion until the survey editor poc is completed, fix properly later localResponseData, calculationResults ); diff --git a/packages/surveys/src/lib/logic.test.ts b/packages/surveys/src/lib/logic.test.ts index c36a7648dc..0e3ee4ef50 100644 --- a/packages/surveys/src/lib/logic.test.ts +++ b/packages/surveys/src/lib/logic.test.ts @@ -1,12 +1,9 @@ import { describe, expect, test, vi } from "vitest"; import { TJsEnvironmentStateSurvey } from "@formbricks/types/js"; import { TResponseData, TResponseVariables } from "@formbricks/types/responses"; +import { TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks"; import { TConditionGroup, TSingleCondition } from "@formbricks/types/surveys/logic"; -import { - TSurveyLogicAction, - TSurveyQuestionTypeEnum, - TSurveyVariable, -} from "@formbricks/types/surveys/types"; +import { TSurveyQuestionTypeEnum, TSurveyVariable } from "@formbricks/types/surveys/types"; import { evaluateLogic, isConditionGroup, performActions } from "./logic"; // Mock the imported function @@ -362,10 +359,10 @@ describe("Survey Logic", () => { }; test("performs jump action", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var1", - objective: "jumpToQuestion", + objective: "jumpToBlock", target: "q5", }, ]; @@ -377,7 +374,7 @@ describe("Survey Logic", () => { }); test("performs require answer action", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var1", objective: "requireAnswer", @@ -392,7 +389,7 @@ describe("Survey Logic", () => { }); test("performs calculate action - add", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -407,7 +404,7 @@ describe("Survey Logic", () => { }); test("performs calculate action - subtract", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -422,7 +419,7 @@ describe("Survey Logic", () => { }); test("performs calculate action - multiply", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -437,7 +434,7 @@ describe("Survey Logic", () => { }); test("performs calculate action - divide", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -452,7 +449,7 @@ describe("Survey Logic", () => { }); test("handles divide by zero", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -467,7 +464,7 @@ describe("Survey Logic", () => { }); test("performs calculate action - assign", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -482,7 +479,7 @@ describe("Survey Logic", () => { }); test("performs calculate action - concat", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var1", objective: "calculate", @@ -497,7 +494,7 @@ describe("Survey Logic", () => { }); test("performs calculate action with question value", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -512,7 +509,7 @@ describe("Survey Logic", () => { }); test("performs calculate action with variable value", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -527,7 +524,7 @@ describe("Survey Logic", () => { }); test("performs multiple actions in order", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", objective: "calculate", @@ -542,7 +539,7 @@ describe("Survey Logic", () => { }, { id: "var2", - objective: "jumpToQuestion", + objective: "jumpToBlock", target: "q5", }, ]; @@ -554,15 +551,15 @@ describe("Survey Logic", () => { }); test("takes first jump target when multiple jump actions exist", () => { - const actions: TSurveyLogicAction[] = [ + const actions: TSurveyBlockLogicAction[] = [ { id: "var2", - objective: "jumpToQuestion", + objective: "jumpToBlock", target: "q2", }, { id: "var2", - objective: "jumpToQuestion", + objective: "jumpToBlock", target: "q3", }, ];