diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/surveySummary.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/surveySummary.ts index 36c7a386f3..12cb17c302 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/surveySummary.ts +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/surveySummary.ts @@ -18,6 +18,7 @@ import { TSurveyAddressElement, TSurveyContactInfoElement, TSurveyElement, + TSurveyElementChoice, TSurveyElementTypeEnum, } from "@formbricks/types/surveys/elements"; import { @@ -33,7 +34,6 @@ import { TSurveyElementSummaryRanking, TSurveyElementSummaryRating, TSurveyLanguage, - TSurveyQuestionChoice, TSurveySummary, } from "@formbricks/types/surveys/types"; import { getTextContent } from "@formbricks/types/surveys/validation"; @@ -323,7 +323,7 @@ const checkForI18n = ( // Return the localized value of the choice fo multiSelect single question if (question && "choices" in question) { const choice = question.choices?.find( - (choice: TSurveyQuestionChoice) => choice.label?.[languageCode] === responseData[id] + (choice: TSurveyElementChoice) => choice.label?.[languageCode] === responseData[id] ); return choice && "label" in choice ? getLocalizedValue(choice.label, "default") || responseData[id] diff --git a/apps/web/modules/survey/components/question-form-input/index.tsx b/apps/web/modules/survey/components/question-form-input/index.tsx index 13aa22f2b0..a17b3a469f 100644 --- a/apps/web/modules/survey/components/question-form-input/index.tsx +++ b/apps/web/modules/survey/components/question-form-input/index.tsx @@ -6,13 +6,12 @@ import { ImagePlusIcon, TrashIcon } from "lucide-react"; import { useCallback, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { type TI18nString } from "@formbricks/types/i18n"; -import { TSurveyElement, TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements"; import { - TSurvey, - TSurveyEndScreenCard, - TSurveyQuestionChoice, - TSurveyRedirectUrlCard, -} from "@formbricks/types/surveys/types"; + TSurveyElement, + TSurveyElementChoice, + TSurveyElementTypeEnum, +} from "@formbricks/types/surveys/elements"; +import { TSurvey, TSurveyEndScreenCard, TSurveyRedirectUrlCard } from "@formbricks/types/surveys/types"; import { TUserLocale } from "@formbricks/types/user"; import { createI18nString, extractLanguageCodes } from "@/lib/i18n/utils"; import { useSyncScroll } from "@/lib/utils/hooks/useSyncScroll"; @@ -44,7 +43,7 @@ interface QuestionFormInputProps { questionIdx: number; updateQuestion?: (questionIdx: number, data: Partial) => void; updateSurvey?: (data: Partial | Partial) => void; - updateChoice?: (choiceIdx: number, data: Partial) => void; + updateChoice?: (choiceIdx: number, data: Partial) => void; updateMatrixLabel?: (index: number, type: "row" | "column", matrixLabel: TI18nString) => void; isInvalid: boolean; selectedLanguageCode: string; diff --git a/apps/web/modules/survey/editor/components/add-question-to-block-button.tsx b/apps/web/modules/survey/editor/components/add-question-to-block-button.tsx index b2a4e27b5d..e19d4f4cf0 100644 --- a/apps/web/modules/survey/editor/components/add-question-to-block-button.tsx +++ b/apps/web/modules/survey/editor/components/add-question-to-block-button.tsx @@ -1,7 +1,7 @@ "use client"; import { createId } from "@paralleldrive/cuid2"; -import { Project } from "@prisma/client"; +import { type Project } from "@prisma/client"; import { PlusIcon } from "lucide-react"; import { useState } from "react"; import toast from "react-hot-toast"; diff --git a/apps/web/modules/survey/editor/components/question-option-choice.tsx b/apps/web/modules/survey/editor/components/question-option-choice.tsx index 6e7f4a910a..126a5cf972 100644 --- a/apps/web/modules/survey/editor/components/question-option-choice.tsx +++ b/apps/web/modules/survey/editor/components/question-option-choice.tsx @@ -5,8 +5,12 @@ import { CSS } from "@dnd-kit/utilities"; import { GripVerticalIcon, PlusIcon, TrashIcon } from "lucide-react"; import { useTranslation } from "react-i18next"; import { TI18nString } from "@formbricks/types/i18n"; -import { TSurveyMultipleChoiceElement, TSurveyRankingElement } from "@formbricks/types/surveys/elements"; -import { TSurvey, TSurveyLanguage, TSurveyQuestionChoice } from "@formbricks/types/surveys/types"; +import { + TSurveyElementChoice, + TSurveyMultipleChoiceElement, + TSurveyRankingElement, +} from "@formbricks/types/surveys/elements"; +import { TSurvey, TSurveyLanguage } from "@formbricks/types/surveys/types"; import { TUserLocale } from "@formbricks/types/user"; import { cn } from "@/lib/cn"; import { createI18nString } from "@/lib/i18n/utils"; @@ -16,7 +20,7 @@ import { TooltipRenderer } from "@/modules/ui/components/tooltip"; import { isLabelValidForAllLanguages } from "../lib/validation"; interface ChoiceProps { - choice: TSurveyQuestionChoice; + choice: TSurveyElementChoice; choiceIdx: number; questionIdx: number; updateChoice: (choiceIdx: number, updatedAttributes: { label: TI18nString }) => void; diff --git a/apps/web/modules/survey/link/components/link-survey.tsx b/apps/web/modules/survey/link/components/link-survey.tsx index 1006785ce6..bbb3205d7b 100644 --- a/apps/web/modules/survey/link/components/link-survey.tsx +++ b/apps/web/modules/survey/link/components/link-survey.tsx @@ -12,7 +12,7 @@ import { VerifyEmail } from "@/modules/survey/link/components/verify-email"; import { getPrefillValue } from "@/modules/survey/link/lib/utils"; import { SurveyInline } from "@/modules/ui/components/survey"; -let setQuestionId = (_: string) => {}; +let setBlockId = (_: string) => {}; let setResponseData = (_: TResponseData) => {}; interface LinkSurveyProps { @@ -158,7 +158,11 @@ export const LinkSurvey = ({ }; const handleResetSurvey = () => { - setQuestionId(survey.welcomeCard.enabled ? "start" : questions[0].id); + if (survey.welcomeCard.enabled) { + setBlockId("start"); + } else if (survey.blocks[0]) { + setBlockId(survey.blocks[0].id); + } setResponseData({}); }; @@ -191,8 +195,8 @@ export const LinkSurvey = ({ prefillResponseData={prefillValue} skipPrefilled={skipPrefilled} responseCount={responseCount} - getSetQuestionId={(f: (value: string) => void) => { - setQuestionId = f; + getSetBlockId={(f: (value: string) => void) => { + setBlockId = f; }} getSetResponseData={(f: (value: TResponseData) => void) => { setResponseData = f; diff --git a/packages/js-core/src/types/survey.ts b/packages/js-core/src/types/survey.ts index 011cb0ed14..436f196e85 100644 --- a/packages/js-core/src/types/survey.ts +++ b/packages/js-core/src/types/survey.ts @@ -8,7 +8,6 @@ export interface SurveyBaseProps { isBrandingEnabled: boolean; getSetIsError?: (getSetError: (value: boolean) => void) => void; getSetIsResponseSendingFinished?: (getSetIsResponseSendingFinished: (value: boolean) => void) => void; - getSetQuestionId?: (getSetQuestionId: (value: string) => void) => void; getSetResponseData?: (getSetResponseData: (value: TResponseData) => void) => void; onDisplay?: () => void; onResponse?: (response: TResponseUpdate) => void; diff --git a/packages/surveys/src/components/general/block-conditional.tsx b/packages/surveys/src/components/general/block-conditional.tsx index 5eea9695f6..f6dc6955fc 100644 --- a/packages/surveys/src/components/general/block-conditional.tsx +++ b/packages/surveys/src/components/general/block-conditional.tsx @@ -12,7 +12,6 @@ import { getLocalizedValue } from "@/lib/i18n"; import { cn } from "@/lib/utils"; interface BlockConditionalProps { - // survey: TJsEnvironmentStateSurvey; block: TSurveyBlock; value: TResponseData; onChange: (responseData: TResponseData) => void; @@ -36,7 +35,6 @@ interface BlockConditionalProps { } export function BlockConditional({ - // survey, block, value, onChange, diff --git a/packages/surveys/src/components/general/element-conditional.tsx b/packages/surveys/src/components/general/element-conditional.tsx index 5a8da19e86..16c14971ed 100644 --- a/packages/surveys/src/components/general/element-conditional.tsx +++ b/packages/surveys/src/components/general/element-conditional.tsx @@ -2,8 +2,11 @@ import { useEffect, useRef } from "preact/hooks"; import { type TJsFileUploadParams } from "@formbricks/types/js"; import { type TResponseData, type TResponseDataValue, type TResponseTtc } from "@formbricks/types/responses"; import { type TUploadFileConfig } from "@formbricks/types/storage"; -import { TSurveyElement, TSurveyElementTypeEnum } from "@formbricks/types/surveys/elements"; -import { type TSurveyQuestionChoice } from "@formbricks/types/surveys/types"; +import { + TSurveyElement, + TSurveyElementChoice, + TSurveyElementTypeEnum, +} from "@formbricks/types/surveys/elements"; import { AddressQuestion } from "@/components/questions/address-question"; import { CalQuestion } from "@/components/questions/cal-question"; import { ConsentQuestion } from "@/components/questions/consent-question"; @@ -74,10 +77,7 @@ export function ElementConditional({ } }, [formRef]); - const getResponseValueForRankingQuestion = ( - value: string[], - choices: TSurveyQuestionChoice[] - ): string[] => { + const getResponseValueForRankingQuestion = (value: string[], choices: TSurveyElementChoice[]): string[] => { return value .map((entry) => { // First check if entry is already a valid choice ID @@ -87,7 +87,7 @@ export function ElementConditional({ // Otherwise, treat it as a localized label and find the choice by label return choices.find((choice) => getLocalizedValue(choice.label, languageCode) === entry)?.id; }) - .filter((id): id is TSurveyQuestionChoice["id"] => id !== undefined); + .filter((id): id is TSurveyElementChoice["id"] => id !== undefined); }; useEffect(() => { @@ -99,6 +99,20 @@ export function ElementConditional({ // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to run this only once when the element renders for the first time }, []); + const isRecognizedType = Object.values(TSurveyElementTypeEnum).includes(element.type); + + useEffect(() => { + if (!isRecognizedType) { + console.warn( + `[Formbricks] Unrecognized element type "${element.type}" for element with id "${element.id}". No component will be rendered.` + ); + } + }, [element.type, element.id, isRecognizedType]); + + if (!isRecognizedType) { + return null; + } + return (
{element.type === TSurveyElementTypeEnum.OpenText ? ( diff --git a/packages/surveys/src/components/general/survey.tsx b/packages/surveys/src/components/general/survey.tsx index d4214a5227..ca5892719a 100644 --- a/packages/surveys/src/components/general/survey.tsx +++ b/packages/surveys/src/components/general/survey.tsx @@ -26,7 +26,7 @@ import { evaluateLogic, performActions } from "@/lib/logic"; import { parseRecallInformation } from "@/lib/recall"; import { ResponseQueue } from "@/lib/response-queue"; import { SurveyState } from "@/lib/survey-state"; -import { cn, findBlockByElementId, getDefaultLanguageCode, getElementsFromSurvey } from "@/lib/utils"; +import { cn, findBlockByElementId, getDefaultLanguageCode, getElementsFromSurveyBlocks } from "@/lib/utils"; import { TResponseErrorCodesEnum } from "@/types/response-error-codes"; interface VariableStackEntry { @@ -58,7 +58,6 @@ export function Survey({ languageCode, getSetIsError, getSetIsResponseSendingFinished, - getSetQuestionId, getSetBlockId, getSetResponseData, responseCount, @@ -140,7 +139,7 @@ export function Survey({ return null; }, [appUrl, environmentId, getSetIsError, getSetIsResponseSendingFinished, surveyState]); - const questions = useMemo(() => getElementsFromSurvey(localSurvey), [localSurvey]); + const questions = useMemo(() => getElementsFromSurveyBlocks(localSurvey.blocks), [localSurvey.blocks]); const originalQuestionRequiredStates = useMemo(() => { return questions.reduce>((acc, question) => { @@ -173,7 +172,7 @@ export function Survey({ const [blockId, setBlockId] = useState(() => { if (startAtQuestionId) { // If starting at a specific question, find its parent block - const startBlock = findBlockByElementId(localSurvey, startAtQuestionId); + const startBlock = findBlockByElementId(localSurvey.blocks, startAtQuestionId); return startBlock?.id || localSurvey.blocks[0]?.id; } else if (localSurvey.welcomeCard.enabled) { return "start"; @@ -312,18 +311,6 @@ export function Survey({ } }, [getSetIsError]); - useEffect(() => { - if (getSetQuestionId) { - getSetQuestionId((value: string) => { - // Convert question ID to block ID - const block = findBlockByElementId(survey, value); - if (block) { - setBlockId(block.id); - } - }); - } - }, [getSetQuestionId, survey]); - useEffect(() => { if (getSetBlockId) { getSetBlockId((value: string) => { @@ -770,7 +757,6 @@ export function Survey({ Boolean(block) && ( { - const questions = getElementsFromSurvey(survey); + const questions = getElementsFromSurveyBlocks(survey.blocks); let totalCards = questions.length; if (survey.endings.length > 0) totalCards += 1; let idx = calculateElementIdx(survey, 0, totalCards); diff --git a/packages/surveys/src/components/questions/ranking-question.tsx b/packages/surveys/src/components/questions/ranking-question.tsx index 6b50932d4c..10d0eb2673 100644 --- a/packages/surveys/src/components/questions/ranking-question.tsx +++ b/packages/surveys/src/components/questions/ranking-question.tsx @@ -2,8 +2,7 @@ import { useAutoAnimate } from "@formkit/auto-animate/react"; import { useCallback, useMemo, useState } from "preact/hooks"; import { useTranslation } from "react-i18next"; import { type TResponseData, type TResponseTtc } from "@formbricks/types/responses"; -import type { TSurveyRankingElement } from "@formbricks/types/surveys/elements"; -import type { TSurveyQuestionChoice } from "@formbricks/types/surveys/types"; +import type { TSurveyElementChoice, TSurveyRankingElement } from "@formbricks/types/surveys/elements"; import { Headline } from "@/components/general/headline"; import { QuestionMedia } from "@/components/general/question-media"; import { Subheader } from "@/components/general/subheader"; @@ -55,7 +54,7 @@ export function RankingQuestion({ const sortedItems = useMemo(() => { return localValue .map((id) => question.choices.find((c) => c.id === id)) - .filter((item): item is TSurveyQuestionChoice => item !== undefined); + .filter((item): item is TSurveyElementChoice => item !== undefined); }, [localValue, question.choices]); const unsortedItems = useMemo(() => { @@ -66,7 +65,7 @@ export function RankingQuestion({ }, [question.choices, question.shuffleOption, localValue, sortedItems, shuffledChoicesIds]); const handleItemClick = useCallback( - (item: TSurveyQuestionChoice) => { + (item: TSurveyElementChoice) => { const isAlreadySorted = localValue.includes(item.id); const newLocalValue = isAlreadySorted ? localValue.filter((id) => id !== item.id) @@ -77,7 +76,7 @@ export function RankingQuestion({ // Immediately update parent state with the new ranking const sortedLabels = newLocalValue .map((id) => question.choices.find((c) => c.id === id)) - .filter((item): item is TSurveyQuestionChoice => item !== undefined) + .filter((item): item is TSurveyElementChoice => item !== undefined) .map((item) => getLocalizedValue(item.label, languageCode)); onChange({ [question.id]: sortedLabels }); @@ -101,7 +100,7 @@ export function RankingQuestion({ // Immediately update parent state with the new ranking const sortedLabels = newLocalValue .map((id) => question.choices.find((c) => c.id === id)) - .filter((item): item is TSurveyQuestionChoice => item !== undefined) + .filter((item): item is TSurveyElementChoice => item !== undefined) .map((item) => getLocalizedValue(item.label, languageCode)); onChange({ [question.id]: sortedLabels }); diff --git a/packages/surveys/src/lib/logic.ts b/packages/surveys/src/lib/logic.ts index d49a52f6a8..492f22302f 100644 --- a/packages/surveys/src/lib/logic.ts +++ b/packages/surveys/src/lib/logic.ts @@ -5,7 +5,7 @@ import { TSurveyElement, TSurveyElementTypeEnum } from "@formbricks/types/survey import { TConditionGroup, TSingleCondition } from "@formbricks/types/surveys/logic"; import { TSurveyVariable } from "@formbricks/types/surveys/types"; import { getLocalizedValue } from "@/lib/i18n"; -import { getElementsFromSurvey } from "./utils"; +import { getElementsFromSurveyBlocks } from "./utils"; const getVariableValue = ( variables: TSurveyVariable[], @@ -89,7 +89,7 @@ const getLeftOperandValue = ( ) => { switch (leftOperand.type) { case "question": - const questions = getElementsFromSurvey(localSurvey); + const questions = getElementsFromSurveyBlocks(localSurvey.blocks); const currentQuestion = questions.find((q) => q.id === leftOperand.value); if (!currentQuestion) return undefined; @@ -223,7 +223,7 @@ const evaluateSingleCondition = ( let leftField: TSurveyElement | TSurveyVariable | string; - const questions = getElementsFromSurvey(localSurvey); + const questions = getElementsFromSurveyBlocks(localSurvey.blocks); if (condition.leftOperand?.type === "question") { leftField = questions.find((q) => q.id === condition.leftOperand?.value) ?? ""; } else if (condition.leftOperand?.type === "variable") { diff --git a/packages/surveys/src/lib/utils.test.ts b/packages/surveys/src/lib/utils.test.ts index f935fdb965..d2d7de769b 100644 --- a/packages/surveys/src/lib/utils.test.ts +++ b/packages/surveys/src/lib/utils.test.ts @@ -2,11 +2,11 @@ import { beforeEach, describe, expect, test, vi } from "vitest"; import type { TJsEnvironmentStateSurvey } from "../../../types/js"; import { type TAllowedFileExtension, mimeTypes } from "../../../types/storage"; import { TSurveyElementTypeEnum } from "../../../types/surveys/elements"; -import type { TSurveyLanguage, TSurveyQuestionChoice } from "../../../types/surveys/types"; +import type { TSurveyLanguage } from "../../../types/surveys/types"; import { findBlockByElementId, getDefaultLanguageCode, - getElementsFromSurvey, + getElementsFromSurveyBlocks, getMimeType, getShuffledChoicesIds, getShuffledRowIndices, @@ -140,12 +140,12 @@ describe("getShuffledChoicesIds", () => { mockGetRandomValues.mockReset(); }); - const choicesBase: TSurveyQuestionChoice[] = [ + const choicesBase = [ { id: "c1", label: { en: "Choice 1" } }, { id: "c2", label: { en: "Choice 2" } }, { id: "c3", label: { en: "Choice 3" } }, ]; - const choicesWithOther: TSurveyQuestionChoice[] = [...choicesBase, { id: "other", label: { en: "Other" } }]; + const choicesWithOther = [...choicesBase, { id: "other", label: { en: "Other" } }]; test('should return unshuffled for "none"', () => { expect(getShuffledChoicesIds(choicesBase, "none")).toEqual(["c1", "c2", "c3"]); @@ -225,7 +225,7 @@ describe("getQuestionsFromSurvey", () => { ], }; - const questions = getElementsFromSurvey(survey); + const questions = getElementsFromSurveyBlocks(survey.blocks); expect(questions).toHaveLength(3); expect(questions[0].id).toBe("q1"); expect(questions[1].id).toBe("q2"); @@ -238,7 +238,7 @@ describe("getQuestionsFromSurvey", () => { blocks: [], } as TJsEnvironmentStateSurvey; - expect(getElementsFromSurvey(survey)).toEqual([]); + expect(getElementsFromSurveyBlocks(survey.blocks)).toEqual([]); }); test("should handle blocks with no elements", () => { @@ -263,7 +263,7 @@ describe("getQuestionsFromSurvey", () => { ], }; - const questions = getElementsFromSurvey(survey); + const questions = getElementsFromSurveyBlocks(survey.blocks); expect(questions).toHaveLength(1); expect(questions[0].id).toBe("q1"); }); @@ -313,17 +313,17 @@ describe("findBlockByElementId", () => { }; test("should find block containing the element", () => { - const block = findBlockByElementId(survey, "q1"); + const block = findBlockByElementId(survey.blocks, "q1"); expect(block).toBeDefined(); expect(block?.id).toBe("block1"); - const block2 = findBlockByElementId(survey, "q3"); + const block2 = findBlockByElementId(survey.blocks, "q3"); expect(block2).toBeDefined(); expect(block2?.id).toBe("block2"); }); test("should return undefined for non-existent element", () => { - const block = findBlockByElementId(survey, "nonexistent"); + const block = findBlockByElementId(survey.blocks, "nonexistent"); expect(block).toBeUndefined(); }); }); diff --git a/packages/surveys/src/lib/utils.ts b/packages/surveys/src/lib/utils.ts index 31c893c631..05530d47db 100644 --- a/packages/surveys/src/lib/utils.ts +++ b/packages/surveys/src/lib/utils.ts @@ -2,9 +2,9 @@ import { type Result, err, ok, wrapThrowsAsync } from "@formbricks/types/error-h import { type ApiErrorResponse } from "@formbricks/types/errors"; import { type TJsEnvironmentStateSurvey } from "@formbricks/types/js"; import { TAllowedFileExtension, mimeTypes } from "@formbricks/types/storage"; -import { TSurveyBlockLogic, TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks"; -import { type TSurveyElement } from "@formbricks/types/surveys/elements"; -import { type TShuffleOption, type TSurveyQuestionChoice } from "@formbricks/types/surveys/types"; +import { TSurveyBlock, TSurveyBlockLogic, TSurveyBlockLogicAction } from "@formbricks/types/surveys/blocks"; +import { type TSurveyElement, TSurveyElementChoice } from "@formbricks/types/surveys/elements"; +import { type TShuffleOption } from "@formbricks/types/surveys/types"; import { ApiResponse, ApiSuccessResponse } from "@/types/api"; export const cn = (...classes: string[]) => { @@ -41,7 +41,7 @@ export const getShuffledRowIndices = (n: number, shuffleOption: TShuffleOption): }; export const getShuffledChoicesIds = ( - choices: TSurveyQuestionChoice[], + choices: TSurveyElementChoice[], shuffleOption: TShuffleOption ): string[] => { const otherOption = choices.find((choice) => { @@ -79,10 +79,10 @@ export const calculateElementIdx = ( currentQustionIdx: number, totalCards: number ): number => { - const questions = getElementsFromSurvey(survey); + const questions = getElementsFromSurveyBlocks(survey.blocks); const currentQuestion = questions[currentQustionIdx]; const middleIdx = Math.floor(totalCards / 2); - const possibleNextBlockIds = getPossibleNextBlocks(survey, currentQuestion); + const possibleNextBlockIds = getPossibleNextBlocks(survey.blocks, currentQuestion); const endingCardIds = survey.endings.map((ending) => ending.id); // Convert block IDs to element IDs (get first element of each block) @@ -106,9 +106,9 @@ export const calculateElementIdx = ( return elementIdx; }; -const getPossibleNextBlocks = (survey: TJsEnvironmentStateSurvey, element: TSurveyElement): string[] => { +const getPossibleNextBlocks = (blocks: TSurveyBlock[], element: TSurveyElement): string[] => { // In the blocks model, logic is stored at the block level - const parentBlock = findBlockByElementId(survey, element.id); + const parentBlock = findBlockByElementId(blocks, element.id); if (!parentBlock?.logic) return []; const possibleBlockIds: string[] = []; @@ -197,7 +197,7 @@ export const checkIfSurveyIsRTL = (survey: TJsEnvironmentStateSurvey, languageCo } } - const questions = getElementsFromSurvey(survey); + const questions = getElementsFromSurveyBlocks(survey.blocks); for (const question of questions) { const questionHeadline = question.headline[languageCode]; @@ -212,11 +212,11 @@ export const checkIfSurveyIsRTL = (survey: TJsEnvironmentStateSurvey, languageCo /** * Derives a flat array of elements from the survey's blocks structure. - * @param survey The survey object with blocks + * @param blocks The blocks array * @returns An array of TSurveyElement (pure elements without block-level properties) */ -export const getElementsFromSurvey = (survey: TJsEnvironmentStateSurvey): TSurveyElement[] => - survey.blocks.flatMap((block) => block.elements); +export const getElementsFromSurveyBlocks = (blocks: TSurveyBlock[]): TSurveyElement[] => + blocks.flatMap((block) => block.elements); /** * Finds the parent block that contains the specified element ID. @@ -225,8 +225,8 @@ export const getElementsFromSurvey = (survey: TJsEnvironmentStateSurvey): TSurve * @param elementId The ID of the element to find * @returns The parent block or undefined if not found */ -export const findBlockByElementId = (survey: TJsEnvironmentStateSurvey, elementId: string) => - survey.blocks.find((b) => b.elements.some((e) => e.id === elementId)); +export const findBlockByElementId = (blocks: TSurveyBlock[], elementId: string) => + blocks.find((block) => block.elements.some((e) => e.id === elementId)); /** * Converts a block ID to the first element ID in that block. @@ -242,39 +242,3 @@ export const getFirstElementIdInBlock = ( const block = survey.blocks.find((b) => b.id === blockId); return block?.elements[0]?.id; }; - -/** - * Gets a block by its ID. - * @param survey The survey object - * @param blockId The block ID to find - * @returns The block or undefined if not found - */ -export const getBlockById = (survey: TJsEnvironmentStateSurvey, blockId: string) => { - return survey.blocks.find((b) => b.id === blockId); -}; - -/** - * Gets the next block ID after the current block. - * @param survey The survey object - * @param currentBlockId The current block ID - * @returns The next block ID or undefined if current block is last - */ -export const getNextBlockId = ( - survey: TJsEnvironmentStateSurvey, - currentBlockId: string -): string | undefined => { - const currentIndex = survey.blocks.findIndex((b) => b.id === currentBlockId); - if (currentIndex === -1) return undefined; - return survey.blocks[currentIndex + 1]?.id; -}; - -/** - * Gets all element IDs in a block. - * @param survey The survey object - * @param blockId The block ID - * @returns Array of element IDs in the block - */ -export const getElementIdsInBlock = (survey: TJsEnvironmentStateSurvey, blockId: string): string[] => { - const block = getBlockById(survey, blockId); - return block?.elements.map((e) => e.id) ?? []; -}; diff --git a/packages/types/formbricks-surveys.ts b/packages/types/formbricks-surveys.ts index c9c429f615..e091c6aa66 100644 --- a/packages/types/formbricks-surveys.ts +++ b/packages/types/formbricks-surveys.ts @@ -10,7 +10,6 @@ export interface SurveyBaseProps { isBrandingEnabled: boolean; getSetIsError?: (getSetError: (value: boolean) => void) => void; getSetIsResponseSendingFinished?: (getSetIsResponseSendingFinished: (value: boolean) => void) => void; - getSetQuestionId?: (getSetQuestionId: (value: string) => void) => void; getSetBlockId?: (getSetBlockId: (value: string) => void) => void; getSetResponseData?: (getSetResponseData: (value: TResponseData) => void) => void; onDisplay?: () => Promise; diff --git a/packages/types/surveys/elements.ts b/packages/types/surveys/elements.ts index 8990deed4f..8ce1f49fee 100644 --- a/packages/types/surveys/elements.ts +++ b/packages/types/surveys/elements.ts @@ -126,6 +126,8 @@ export const ZSurveyElementChoice = z.object({ label: ZI18nString, }); +export type TSurveyElementChoice = z.infer; + export const ZShuffleOption = z.enum(["none", "all", "exceptLast"]); export type TShuffleOption = z.infer;