mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-20 11:22:55 -05:00
updates migration script and types to change the question literal to element
This commit is contained in:
@@ -105,7 +105,7 @@ const starRatingSurvey = (t: TFunction): TXMTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isLessThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -195,7 +195,7 @@ const csatSurvey = (t: TFunction): TXMTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isLessThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -316,7 +316,7 @@ const smileysRatingSurvey = (t: TFunction): TXMTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isLessThanOrEqual",
|
||||
rightOperand: {
|
||||
|
||||
@@ -325,7 +325,7 @@ describe("getSurveySummaryDropOff", () => {
|
||||
{
|
||||
id: "c1",
|
||||
leftOperand: {
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
value: "q2",
|
||||
},
|
||||
operator: "equals" as const,
|
||||
|
||||
@@ -227,7 +227,7 @@ export const createBlockJumpLogic = (
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: sourceElementId,
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: operator,
|
||||
},
|
||||
@@ -257,7 +257,7 @@ export const createBlockChoiceJumpLogic = (
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: sourceElementId,
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
|
||||
@@ -27,7 +27,7 @@ export const createJumpLogic = (
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: sourceQuestionId,
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: operator,
|
||||
},
|
||||
@@ -57,7 +57,7 @@ export const createChoiceJumpLogic = (
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: sourceQuestionId,
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
|
||||
@@ -1092,7 +1092,7 @@ const reviewPrompt = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isLessThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -1922,7 +1922,7 @@ const integrationSetupSurvey = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -2367,7 +2367,7 @@ const collectFeedback = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isLessThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -2411,7 +2411,7 @@ const collectFeedback = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[1],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isSubmitted",
|
||||
},
|
||||
@@ -3036,7 +3036,7 @@ const rateCheckoutExperience = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -3131,7 +3131,7 @@ const measureSearchExperience = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -3226,7 +3226,7 @@ const evaluateContentQuality = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -3349,7 +3349,7 @@ const measureTaskAccomplishment = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[1],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: {
|
||||
@@ -3392,7 +3392,7 @@ const measureTaskAccomplishment = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[2],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isSubmitted",
|
||||
},
|
||||
@@ -3400,7 +3400,7 @@ const measureTaskAccomplishment = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[1],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isSkipped",
|
||||
},
|
||||
@@ -3439,7 +3439,7 @@ const measureTaskAccomplishment = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[3],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isSubmitted",
|
||||
},
|
||||
@@ -3447,7 +3447,7 @@ const measureTaskAccomplishment = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[1],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isSkipped",
|
||||
},
|
||||
@@ -3557,7 +3557,7 @@ const identifySignUpBarriers = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[1],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
@@ -3872,7 +3872,7 @@ const improveNewsletterContent = (t: TFunction): TTemplate => {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: reusableElementIds[0],
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator: "isLessThan",
|
||||
rightOperand: {
|
||||
|
||||
@@ -418,7 +418,7 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
conditions: [
|
||||
{
|
||||
id: "swlje0bsnh6lkyk8vqs13oyr",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "blue" },
|
||||
},
|
||||
@@ -434,13 +434,13 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
conditions: [
|
||||
{
|
||||
id: "n74oght3ozqgwm9rifp2fxrr",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "blue" },
|
||||
},
|
||||
{
|
||||
id: "fg4c9dwt9qjy8aba7zxbfdqd",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "pizza" },
|
||||
},
|
||||
@@ -456,13 +456,13 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
conditions: [
|
||||
{
|
||||
id: "tmj7p9d3kpz1v4mcgpguqytw",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "pizza" },
|
||||
},
|
||||
{
|
||||
id: "rs7v5mmoetff7x8lo1gdsgpr",
|
||||
leftOperand: { type: "question", value: "q3" },
|
||||
leftOperand: { type: "element", value: "q3" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "Inception" },
|
||||
},
|
||||
@@ -480,7 +480,7 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
id: "ddhaccfqy7rr3d5jdswl8yl8",
|
||||
leftOperand: { type: "variable", value: "siog1dabtpo3l0a3xoxw2922" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "question", value: "q4" },
|
||||
rightOperand: { type: "element", value: "q4" },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -502,7 +502,7 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
id: "ot894j7nwna24i6jo2zpk59o",
|
||||
leftOperand: { type: "variable", value: "km1srr55owtn2r7lkoh5ny1u" },
|
||||
operator: "isLessThan",
|
||||
rightOperand: { type: "question", value: "q5" },
|
||||
rightOperand: { type: "element", value: "q5" },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -516,7 +516,7 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
conditions: [
|
||||
{
|
||||
id: "rb223vmzuuzo3ag1bp2m3i69",
|
||||
leftOperand: { type: "question", value: "q6" },
|
||||
leftOperand: { type: "element", value: "q6" },
|
||||
operator: "includesOneOf",
|
||||
rightOperand: {
|
||||
type: "static",
|
||||
@@ -525,7 +525,7 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
},
|
||||
{
|
||||
id: "ot894j7nwna24i6jo2zpk59o",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "doesNotEqual",
|
||||
rightOperand: { type: "static", value: "teal" },
|
||||
},
|
||||
@@ -535,7 +535,7 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
conditions: [
|
||||
{
|
||||
id: "gy6xowchkv8bp1qj7ur79jvc",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
operator: "doesNotEqual",
|
||||
rightOperand: { type: "static", value: "pizza" },
|
||||
},
|
||||
@@ -543,13 +543,13 @@ export const mockSurveyWithLogic: TSurvey = {
|
||||
id: "vxyccgwsbq34s3l0syom7y2w",
|
||||
leftOperand: { type: "hiddenField", value: "name" },
|
||||
operator: "contains",
|
||||
rightOperand: { type: "question", value: "q2" },
|
||||
rightOperand: { type: "element", value: "q2" },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "yunz0k9w0xwparogz2n1twoy",
|
||||
leftOperand: { type: "question", value: "q3" },
|
||||
leftOperand: { type: "element", value: "q3" },
|
||||
operator: "doesNotEqual",
|
||||
rightOperand: { type: "static", value: "Inception" },
|
||||
},
|
||||
|
||||
@@ -448,7 +448,7 @@ describe("surveyLogic", () => {
|
||||
mockSurvey,
|
||||
{},
|
||||
vars,
|
||||
group({ ...baseCond("equals", "foo"), leftOperand: { type: "question", value: "notfound" } }),
|
||||
group({ ...baseCond("equals", "foo"), leftOperand: { type: "element", value: "notfound" } }),
|
||||
"en"
|
||||
)
|
||||
).toBe(false);
|
||||
@@ -854,7 +854,7 @@ describe("surveyLogic", () => {
|
||||
// Test number question
|
||||
const numberCondition: TSingleCondition = {
|
||||
id: "numCond",
|
||||
leftOperand: { type: "question", value: "numQuestion" },
|
||||
leftOperand: { type: "element", value: "numQuestion" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: 42 },
|
||||
};
|
||||
@@ -871,7 +871,7 @@ describe("surveyLogic", () => {
|
||||
// Test MC single with recognized choice
|
||||
const mcSingleCondition: TSingleCondition = {
|
||||
id: "mcCond",
|
||||
leftOperand: { type: "question", value: "mcSingle" },
|
||||
leftOperand: { type: "element", value: "mcSingle" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "choice1" },
|
||||
};
|
||||
@@ -888,7 +888,7 @@ describe("surveyLogic", () => {
|
||||
// Test MC multi
|
||||
const mcMultiCondition: TSingleCondition = {
|
||||
id: "mcMultiCond",
|
||||
leftOperand: { type: "question", value: "mcMulti" },
|
||||
leftOperand: { type: "element", value: "mcMulti" },
|
||||
operator: "includesOneOf",
|
||||
rightOperand: { type: "static", value: ["choice1"] },
|
||||
};
|
||||
@@ -905,7 +905,7 @@ describe("surveyLogic", () => {
|
||||
// Test matrix question
|
||||
const matrixCondition: TSingleCondition = {
|
||||
id: "matrixCond",
|
||||
leftOperand: { type: "question", value: "matrixQ", meta: { row: "0" } },
|
||||
leftOperand: { type: "element", value: "matrixQ", meta: { row: "0" } },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "0" },
|
||||
};
|
||||
@@ -939,7 +939,7 @@ describe("surveyLogic", () => {
|
||||
// Test with missing question
|
||||
const missingQuestionCondition: TSingleCondition = {
|
||||
id: "missingCond",
|
||||
leftOperand: { type: "question", value: "nonExistent" },
|
||||
leftOperand: { type: "element", value: "nonExistent" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "foo" },
|
||||
};
|
||||
@@ -973,7 +973,7 @@ describe("surveyLogic", () => {
|
||||
// Test MC single with "other" option
|
||||
const otherCondition: TSingleCondition = {
|
||||
id: "otherCond",
|
||||
leftOperand: { type: "question", value: "mcSingle" },
|
||||
leftOperand: { type: "element", value: "mcSingle" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "Unknown option" },
|
||||
};
|
||||
@@ -990,7 +990,7 @@ describe("surveyLogic", () => {
|
||||
// Test matrix with invalid row index
|
||||
const invalidMatrixCondition: TSingleCondition = {
|
||||
id: "invalidMatrixCond",
|
||||
leftOperand: { type: "question", value: "matrixQ", meta: { row: "999" } },
|
||||
leftOperand: { type: "element", value: "matrixQ", meta: { row: "999" } },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: "0" },
|
||||
};
|
||||
@@ -1049,7 +1049,7 @@ describe("surveyLogic", () => {
|
||||
id: "questionCond",
|
||||
leftOperand: { type: "hiddenField", value: "f" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "question", value: "question1" },
|
||||
rightOperand: { type: "element", value: "question1" },
|
||||
};
|
||||
|
||||
const variableCondition: TSingleCondition = {
|
||||
@@ -1150,7 +1150,7 @@ describe("surveyLogic", () => {
|
||||
objective: "calculate",
|
||||
variableId: "numVar",
|
||||
operator: "add",
|
||||
value: { type: "question", value: "questionNum" },
|
||||
value: { type: "element", value: "questionNum" },
|
||||
};
|
||||
|
||||
// Test with hidden field value
|
||||
@@ -1168,7 +1168,7 @@ describe("surveyLogic", () => {
|
||||
objective: "calculate",
|
||||
variableId: "textVar",
|
||||
operator: "concat",
|
||||
value: { type: "question", value: "questionText" },
|
||||
value: { type: "element", value: "questionText" },
|
||||
};
|
||||
|
||||
// Test with missing variable
|
||||
@@ -1186,7 +1186,7 @@ describe("surveyLogic", () => {
|
||||
objective: "calculate",
|
||||
variableId: "numVar",
|
||||
operator: "add",
|
||||
value: { type: "question", value: "nonExistentQuestion" },
|
||||
value: { type: "element", value: "nonExistentQuestion" },
|
||||
};
|
||||
|
||||
// Test with other math operations
|
||||
@@ -1348,7 +1348,7 @@ describe("surveyLogic", () => {
|
||||
|
||||
const condition: TSingleCondition = {
|
||||
id: "numCond",
|
||||
leftOperand: { type: "question", value: "numQuestion" },
|
||||
leftOperand: { type: "element", value: "numQuestion" },
|
||||
operator: "equals",
|
||||
rightOperand: { type: "static", value: 0 },
|
||||
};
|
||||
|
||||
@@ -272,7 +272,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
let leftField: TSurveyElement | TSurveyVariable | string;
|
||||
|
||||
if (condition.leftOperand?.type === "question") {
|
||||
if (condition.leftOperand?.type === "element") {
|
||||
leftField = questions.find((q) => q.id === condition.leftOperand?.value) ?? "";
|
||||
} else if (condition.leftOperand?.type === "variable") {
|
||||
leftField = localSurvey.variables.find((v) => v.id === condition.leftOperand?.value) as TSurveyVariable;
|
||||
@@ -284,7 +284,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
let rightField: TSurveyElement | TSurveyVariable | string;
|
||||
|
||||
if (condition.rightOperand?.type === "question") {
|
||||
if (condition.rightOperand?.type === "element") {
|
||||
rightField = questions.find((q) => q.id === condition.rightOperand?.value) ?? "";
|
||||
} else if (condition.rightOperand?.type === "variable") {
|
||||
rightField = localSurvey.variables.find(
|
||||
@@ -306,7 +306,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
switch (condition.operator) {
|
||||
case "equals":
|
||||
if (condition.leftOperand.type === "question") {
|
||||
if (condition.leftOperand.type === "element") {
|
||||
if (
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.Date &&
|
||||
typeof leftValue === "string" &&
|
||||
@@ -318,7 +318,7 @@ const evaluateSingleCondition = (
|
||||
}
|
||||
|
||||
// when left value is of openText, hiddenField, variable and right value is of multichoice
|
||||
if (condition.rightOperand?.type === "question") {
|
||||
if (condition.rightOperand?.type === "element") {
|
||||
if ((rightField as TSurveyElement).type === TSurveyElementTypeEnum.MultipleChoiceMulti) {
|
||||
if (Array.isArray(rightValue) && typeof leftValue === "string" && rightValue.length === 1) {
|
||||
return rightValue.includes(leftValue as string);
|
||||
@@ -342,7 +342,7 @@ const evaluateSingleCondition = (
|
||||
case "doesNotEqual":
|
||||
// when left value is of picture selection question and right value is its option
|
||||
if (
|
||||
condition.leftOperand.type === "question" &&
|
||||
condition.leftOperand.type === "element" &&
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.PictureSelection &&
|
||||
Array.isArray(leftValue) &&
|
||||
leftValue.length > 0 &&
|
||||
@@ -353,7 +353,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
// when left value is of date question and right value is string
|
||||
if (
|
||||
condition.leftOperand.type === "question" &&
|
||||
condition.leftOperand.type === "element" &&
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.Date &&
|
||||
typeof leftValue === "string" &&
|
||||
typeof rightValue === "string"
|
||||
@@ -362,7 +362,7 @@ const evaluateSingleCondition = (
|
||||
}
|
||||
|
||||
// when left value is of openText, hiddenField, variable and right value is of multichoice
|
||||
if (condition.rightOperand?.type === "question") {
|
||||
if (condition.rightOperand?.type === "element") {
|
||||
if ((rightField as TSurveyElement).type === TSurveyElementTypeEnum.MultipleChoiceMulti) {
|
||||
if (Array.isArray(rightValue) && typeof leftValue === "string" && rightValue.length === 1) {
|
||||
return !rightValue.includes(leftValue as string);
|
||||
@@ -398,7 +398,7 @@ const evaluateSingleCondition = (
|
||||
case "isSubmitted":
|
||||
if (typeof leftValue === "string") {
|
||||
if (
|
||||
condition.leftOperand.type === "question" &&
|
||||
condition.leftOperand.type === "element" &&
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.FileUpload &&
|
||||
leftValue
|
||||
) {
|
||||
@@ -511,7 +511,7 @@ const getLeftOperandValue = (
|
||||
selectedLanguage: string
|
||||
) => {
|
||||
switch (leftOperand.type) {
|
||||
case "question":
|
||||
case "element":
|
||||
const questions = getElementsFromBlocks(localSurvey.blocks);
|
||||
const currentQuestion = questions.find((q) => q.id === leftOperand.value);
|
||||
if (!currentQuestion) return undefined;
|
||||
@@ -609,7 +609,7 @@ const getRightOperandValue = (
|
||||
if (!rightOperand) return undefined;
|
||||
|
||||
switch (rightOperand.type) {
|
||||
case "question":
|
||||
case "element":
|
||||
return data[rightOperand.value];
|
||||
case "variable":
|
||||
const variables = localSurvey.variables || [];
|
||||
@@ -685,7 +685,7 @@ const performCalculation = (
|
||||
operandValue = value;
|
||||
}
|
||||
break;
|
||||
case "question":
|
||||
case "element":
|
||||
case "hiddenField":
|
||||
const val = data[action.value.value];
|
||||
if (typeof val === "number" || typeof val === "string") {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { TResponseData, TResponseVariables } from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyQuestion, TSurveyRecallItem } from "@formbricks/types/surveys/types";
|
||||
import { TSurvey, TSurveyRecallItem } from "@formbricks/types/surveys/types";
|
||||
import { structuredClone } from "@/lib/pollyfills/structuredClone";
|
||||
import {
|
||||
checkForEmptyFallBackValue,
|
||||
@@ -40,7 +40,7 @@ vi.mock("@/lib/utils/datetime", () => ({
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
formatDateWithOrdinal: vi.fn((date) => {
|
||||
formatDateWithOrdinal: vi.fn(() => {
|
||||
return "January 1st, 2023";
|
||||
}),
|
||||
}));
|
||||
@@ -355,10 +355,10 @@ describe("recall utility functions", () => {
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].id).toBe("id1");
|
||||
expect(result[0].label).toBe("Question One");
|
||||
expect(result[0].type).toBe("question");
|
||||
expect(result[0].type).toBe("element");
|
||||
expect(result[1].id).toBe("id2");
|
||||
expect(result[1].label).toBe("Question Two");
|
||||
expect(result[1].type).toBe("question");
|
||||
expect(result[1].type).toBe("element");
|
||||
});
|
||||
|
||||
test("handles hidden fields in recall items", () => {
|
||||
@@ -422,7 +422,7 @@ describe("recall utility functions", () => {
|
||||
describe("headlineToRecall", () => {
|
||||
test("transforms headlines to recall info", () => {
|
||||
const text = "What do you think of @Product?";
|
||||
const recallItems: TSurveyRecallItem[] = [{ id: "product", label: "Product", type: "question" }];
|
||||
const recallItems: TSurveyRecallItem[] = [{ id: "product", label: "Product", type: "element" }];
|
||||
const fallbacks: fallbacks = {
|
||||
product: "our product",
|
||||
};
|
||||
@@ -434,8 +434,8 @@ describe("recall utility functions", () => {
|
||||
test("transforms multiple headlines", () => {
|
||||
const text = "Rate @Product made by @Company";
|
||||
const recallItems: TSurveyRecallItem[] = [
|
||||
{ id: "product", label: "Product", type: "question" },
|
||||
{ id: "company", label: "Company", type: "question" },
|
||||
{ id: "product", label: "Product", type: "element" },
|
||||
{ id: "company", label: "Company", type: "element" },
|
||||
];
|
||||
const fallbacks: fallbacks = {
|
||||
product: "our product",
|
||||
|
||||
@@ -170,7 +170,7 @@ export const getRecallItems = (text: string, survey: TSurvey, languageCode: stri
|
||||
|
||||
const getRecallItemType = () => {
|
||||
if (isHiddenField) return "hiddenField";
|
||||
if (isSurveyQuestion) return "question";
|
||||
if (isSurveyQuestion) return "element";
|
||||
if (isVariable) return "variable";
|
||||
};
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export const QuotaModal = ({
|
||||
conditions: [
|
||||
{
|
||||
id: createId(),
|
||||
leftOperand: { type: "question", value: firstQuestion?.id },
|
||||
leftOperand: { type: "element", value: firstQuestion?.id },
|
||||
operator: firstQuestion ? getDefaultOperatorForElement(firstQuestion, t) : "equals",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -134,7 +134,7 @@ describe("Quota Evaluation Service", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "c1",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: { type: "static", value: 18 },
|
||||
},
|
||||
|
||||
@@ -126,13 +126,13 @@ describe("Quota Utils", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "c1",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "isGreaterThanOrEqual",
|
||||
rightOperand: { type: "static", value: 18 },
|
||||
},
|
||||
{
|
||||
id: "c2",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "isLessThanOrEqual",
|
||||
rightOperand: { type: "static", value: 25 },
|
||||
},
|
||||
@@ -155,7 +155,7 @@ describe("Quota Utils", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "c3",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
operator: "isGreaterThan",
|
||||
rightOperand: { type: "static", value: 25 },
|
||||
},
|
||||
|
||||
@@ -124,7 +124,7 @@ export const RecallItemSelect = ({
|
||||
);
|
||||
})
|
||||
.map((question) => {
|
||||
return { id: question.id, label: question.headline[selectedLanguageCode], type: "question" as const };
|
||||
return { id: question.id, label: question.headline[selectedLanguageCode], type: "element" as const };
|
||||
});
|
||||
|
||||
return filteredQuestions;
|
||||
@@ -143,7 +143,7 @@ export const RecallItemSelect = ({
|
||||
|
||||
const getRecallItemIcon = (recallItem: TSurveyRecallItem) => {
|
||||
switch (recallItem.type) {
|
||||
case "question":
|
||||
case "element":
|
||||
const question = questions.find((question) => question.id === recallItem.id);
|
||||
if (question) {
|
||||
return questionIconMapping[question?.type as keyof typeof questionIconMapping];
|
||||
|
||||
@@ -75,7 +75,7 @@ export function ConditionalLogic({
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
value: firstElement.id,
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
operator,
|
||||
},
|
||||
|
||||
@@ -140,7 +140,7 @@ export const QuestionsView = ({
|
||||
updatedCondition.leftOperand = { ...condition.leftOperand, value: updatedId };
|
||||
}
|
||||
|
||||
if (condition.rightOperand?.type === "question" && condition.rightOperand?.value === compareId) {
|
||||
if (condition.rightOperand?.type === "element" && condition.rightOperand?.value === compareId) {
|
||||
updatedCondition.rightOperand = { ...condition.rightOperand, value: updatedId };
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ vi.mock("@/lib/surveyLogic/utils", () => ({
|
||||
|
||||
vi.mock("@/modules/survey/editor/lib/utils", () => ({
|
||||
getConditionValueOptions: vi.fn().mockReturnValue([
|
||||
{ value: "question1", label: "Question 1", type: "question" },
|
||||
{ value: "question1", label: "Question 1", type: "element" },
|
||||
{ value: "variable1", label: "Variable 1", type: "variable" },
|
||||
]),
|
||||
getConditionOperatorOptions: vi.fn().mockReturnValue([
|
||||
@@ -88,6 +88,7 @@ vi.mock("@/modules/survey/editor/lib/utils", () => ({
|
||||
{ value: "isEmpty", label: "is empty" },
|
||||
]),
|
||||
getDefaultOperatorForQuestion: vi.fn().mockReturnValue("equals"),
|
||||
getDefaultOperatorForElement: vi.fn().mockReturnValue("equals"),
|
||||
}));
|
||||
|
||||
vi.mock("@paralleldrive/cuid2", () => ({
|
||||
@@ -249,7 +250,7 @@ describe("shared-conditions-factory", () => {
|
||||
test("should call getConditionValueOptions with questionIdx", async () => {
|
||||
const paramsWithQuestionIdx = {
|
||||
...defaultParams,
|
||||
questionIdx: 0,
|
||||
blockIdx: 0,
|
||||
};
|
||||
const result = createSharedConditionsFactory(paramsWithQuestionIdx, defaultCallbacks);
|
||||
|
||||
@@ -263,7 +264,7 @@ describe("shared-conditions-factory", () => {
|
||||
const result = createSharedConditionsFactory(defaultParams, defaultCallbacks);
|
||||
const mockCondition: TSingleCondition = {
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
};
|
||||
|
||||
@@ -276,12 +277,12 @@ describe("shared-conditions-factory", () => {
|
||||
test("should call getMatchValueProps with questionIdx", async () => {
|
||||
const paramsWithQuestionIdx = {
|
||||
...defaultParams,
|
||||
questionIdx: 0,
|
||||
blockIdx: 0,
|
||||
};
|
||||
const result = createSharedConditionsFactory(paramsWithQuestionIdx, defaultCallbacks);
|
||||
const mockCondition: TSingleCondition = {
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
};
|
||||
|
||||
@@ -308,7 +309,7 @@ describe("shared-conditions-factory", () => {
|
||||
const result = createSharedConditionsFactory(defaultParams, defaultCallbacks);
|
||||
const mockCondition: TSingleCondition = {
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
};
|
||||
|
||||
@@ -390,9 +391,9 @@ describe("shared-conditions-factory", () => {
|
||||
const updates = {
|
||||
leftOperand: {
|
||||
value: "matrix-question.row1",
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
meta: {
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -407,7 +408,7 @@ describe("shared-conditions-factory", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "condition1",
|
||||
leftOperand: { value: "matrix-question", type: "question" },
|
||||
leftOperand: { value: "matrix-question", type: "element" },
|
||||
operator: "equals",
|
||||
rightOperand: { value: "x", type: "static" },
|
||||
} as TSingleCondition,
|
||||
@@ -416,7 +417,7 @@ describe("shared-conditions-factory", () => {
|
||||
const updated = updater(structuredClone(initial));
|
||||
expect(updated.conditions[0]).toMatchObject({
|
||||
operator: "isEmpty",
|
||||
leftOperand: { value: "matrix-question", type: "question", meta: { row: "row1" } },
|
||||
leftOperand: { value: "matrix-question", type: "element", meta: { row: "row1" } },
|
||||
rightOperand: undefined,
|
||||
});
|
||||
});
|
||||
@@ -427,9 +428,9 @@ describe("shared-conditions-factory", () => {
|
||||
const updates = {
|
||||
leftOperand: {
|
||||
value: "question1",
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
meta: {
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -445,9 +446,9 @@ describe("shared-conditions-factory", () => {
|
||||
const updates = {
|
||||
leftOperand: {
|
||||
value: "matrix-question",
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
meta: {
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -465,13 +466,13 @@ describe("shared-conditions-factory", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
rightOperand: { value: "test", type: "static" },
|
||||
},
|
||||
{
|
||||
id: "condition2",
|
||||
leftOperand: { value: "question2", type: "question" },
|
||||
leftOperand: { value: "question2", type: "element" },
|
||||
operator: "doesNotEqual",
|
||||
rightOperand: { value: "test2", type: "static" },
|
||||
},
|
||||
@@ -511,7 +512,7 @@ describe("shared-conditions-factory", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
rightOperand: { value: "test", type: "static" },
|
||||
},
|
||||
@@ -519,7 +520,7 @@ describe("shared-conditions-factory", () => {
|
||||
id: "condition2",
|
||||
leftOperand: {
|
||||
value: "matrix-question",
|
||||
type: "question",
|
||||
type: "element",
|
||||
meta: { row: "row1" },
|
||||
},
|
||||
operator: "isEmpty",
|
||||
@@ -534,7 +535,7 @@ describe("shared-conditions-factory", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
rightOperand: { value: "test", type: "static" },
|
||||
},
|
||||
@@ -542,7 +543,7 @@ describe("shared-conditions-factory", () => {
|
||||
id: "condition2",
|
||||
leftOperand: {
|
||||
value: "matrix-question",
|
||||
type: "question",
|
||||
type: "element",
|
||||
meta: { row: "row1" },
|
||||
},
|
||||
operator: "isEmpty",
|
||||
@@ -595,7 +596,7 @@ describe("shared-conditions-factory", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("should preserve meta for question type conditions", () => {
|
||||
test("should preserve meta for element type conditions", () => {
|
||||
const genericConditions: TQuotaConditionGroup = {
|
||||
id: "root",
|
||||
connector: "and",
|
||||
@@ -604,7 +605,7 @@ describe("shared-conditions-factory", () => {
|
||||
id: "condition1",
|
||||
leftOperand: {
|
||||
value: "question1",
|
||||
type: "question" as const,
|
||||
type: "element" as const,
|
||||
meta: { row: "row1", column: "col1" },
|
||||
},
|
||||
operator: "equals",
|
||||
@@ -616,7 +617,7 @@ describe("shared-conditions-factory", () => {
|
||||
const result = genericConditionsToQuota(genericConditions);
|
||||
|
||||
expect(result.conditions[0].leftOperand).toHaveProperty("meta");
|
||||
if (result.conditions[0].leftOperand.type === "question") {
|
||||
if (result.conditions[0].leftOperand.type === "element") {
|
||||
expect(result.conditions[0].leftOperand.meta).toEqual({ row: "row1", column: "col1" });
|
||||
}
|
||||
});
|
||||
@@ -651,7 +652,7 @@ describe("shared-conditions-factory", () => {
|
||||
conditions: [
|
||||
{
|
||||
id: "condition1",
|
||||
leftOperand: { value: "question1", type: "question" },
|
||||
leftOperand: { value: "question1", type: "element" },
|
||||
operator: "equals",
|
||||
rightOperand: { value: "test", type: "static" },
|
||||
},
|
||||
|
||||
@@ -61,7 +61,7 @@ export function createSharedConditionsFactory(
|
||||
|
||||
// Handles special update logic for matrix elements, setting appropriate operators and metadata
|
||||
const handleMatrixElementUpdate = (resourceId: string, updates: Partial<TSingleCondition>): boolean => {
|
||||
if (updates.leftOperand && updates.leftOperand.type === "question") {
|
||||
if (updates.leftOperand && updates.leftOperand.type === "element") {
|
||||
const [elementId, rowId] = updates.leftOperand.value.split(".");
|
||||
const element = elements.find((q) => q.id === elementId);
|
||||
|
||||
@@ -73,7 +73,7 @@ export function createSharedConditionsFactory(
|
||||
updateCondition(conditionsCopy, resourceId, {
|
||||
leftOperand: {
|
||||
value: elementId,
|
||||
type: "question",
|
||||
type: "element",
|
||||
meta: {
|
||||
row: rowId,
|
||||
},
|
||||
@@ -112,7 +112,7 @@ export function createSharedConditionsFactory(
|
||||
const defaultOperator = elements.length > 0 ? getDefaultOperatorForElement(elements[0], t) : "equals";
|
||||
const newCondition: TSingleCondition = {
|
||||
id: createId(),
|
||||
leftOperand: { value: defaultLeftOperandValue, type: "question" },
|
||||
leftOperand: { value: defaultLeftOperandValue, type: "element" },
|
||||
operator: defaultOperator,
|
||||
};
|
||||
|
||||
@@ -149,7 +149,7 @@ export function createSharedConditionsFactory(
|
||||
}
|
||||
|
||||
// Check if the operator is correct for the element
|
||||
if (updates.leftOperand?.type === "question" && updates.operator) {
|
||||
if (updates.leftOperand?.type === "element" && updates.operator) {
|
||||
const elementId = updates.leftOperand.value.split(".")[0];
|
||||
const element = elements.find((q) => q.id === elementId);
|
||||
|
||||
@@ -219,7 +219,7 @@ export const genericConditionsToQuota = (genericConditions: TQuotaConditionGroup
|
||||
leftOperand: {
|
||||
type: leftOperand.type,
|
||||
value: leftOperand.value,
|
||||
...(leftOperand.type === "question" && leftOperand.meta && { meta: leftOperand.meta }),
|
||||
...(leftOperand.type === "element" && leftOperand.meta && { meta: leftOperand.meta }),
|
||||
},
|
||||
operator: condition.operator,
|
||||
rightOperand: condition.rightOperand,
|
||||
|
||||
@@ -138,7 +138,7 @@ export const getConditionValueOptions = (
|
||||
label: `${getTextContent(processedLabel.default ?? "")} (${elementHeadline})`,
|
||||
value: `${element.id}.${rowIdx}`,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
rowIdx: rowIdx.toString(),
|
||||
},
|
||||
};
|
||||
@@ -149,7 +149,7 @@ export const getConditionValueOptions = (
|
||||
label: elementHeadline,
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -161,7 +161,7 @@ export const getConditionValueOptions = (
|
||||
label: t("environments.surveys.edit.matrix_all_fields", "All fields"),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -174,7 +174,7 @@ export const getConditionValueOptions = (
|
||||
),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -261,7 +261,7 @@ export const getElementOperatorOptions = (
|
||||
options = getLogicRules(t).question[`openText.${inputType}`].options;
|
||||
} else if (element.type === TSurveyElementTypeEnum.Matrix && condition) {
|
||||
const isMatrixRow =
|
||||
condition.leftOperand.type === "question" && condition.leftOperand?.meta?.row !== undefined;
|
||||
condition.leftOperand.type === "element" && condition.leftOperand?.meta?.row !== undefined;
|
||||
options = getLogicRules(t).question[`matrix${isMatrixRow ? ".row" : ""}`].options;
|
||||
} else {
|
||||
options = getLogicRules(t).question[element.type].options;
|
||||
@@ -284,7 +284,7 @@ export const getDefaultOperatorForElement = (
|
||||
};
|
||||
|
||||
export const getFormatLeftOperandValue = (condition: TSingleCondition, localSurvey: TSurvey): string => {
|
||||
if (condition.leftOperand.type === "question") {
|
||||
if (condition.leftOperand.type === "element") {
|
||||
const questions = getElementsFromBlocks(localSurvey.blocks);
|
||||
const question = questions.find((q) => q.id === condition.leftOperand.value);
|
||||
if (question && question.type === TSurveyElementTypeEnum.Matrix) {
|
||||
@@ -308,7 +308,7 @@ export const getConditionOperatorOptions = (
|
||||
return getLogicRules(t)[`variable.${variableType}`].options;
|
||||
} else if (condition.leftOperand.type === "hiddenField") {
|
||||
return getLogicRules(t).hiddenField.options;
|
||||
} else if (condition.leftOperand.type === "question") {
|
||||
} else if (condition.leftOperand.type === "element") {
|
||||
// Derive questions from blocks
|
||||
const elements = getElementsFromBlocks(localSurvey.blocks);
|
||||
const element = elements.find((question) => {
|
||||
@@ -370,7 +370,7 @@ export const getMatchValueProps = (
|
||||
const selectedElement = elements.find((element) => element.id === condition.leftOperand.value);
|
||||
const selectedVariable = variables.find((variable) => variable.id === condition.leftOperand.value);
|
||||
|
||||
if (condition.leftOperand.type === "question") {
|
||||
if (condition.leftOperand.type === "element") {
|
||||
elements = elements.filter((element) => element.id !== condition.leftOperand.value);
|
||||
} else if (condition.leftOperand.type === "variable") {
|
||||
variables = variables.filter((variable) => variable.id !== condition.leftOperand.value);
|
||||
@@ -378,7 +378,7 @@ export const getMatchValueProps = (
|
||||
hiddenFields = hiddenFields.filter((field) => field !== condition.leftOperand.value);
|
||||
}
|
||||
|
||||
if (condition.leftOperand.type === "question") {
|
||||
if (condition.leftOperand.type === "element") {
|
||||
if (selectedElement?.type === TSurveyElementTypeEnum.OpenText) {
|
||||
const allowedElementTypes = [TSurveyElementTypeEnum.OpenText];
|
||||
|
||||
@@ -406,7 +406,7 @@ export const getMatchValueProps = (
|
||||
),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -623,7 +623,7 @@ export const getMatchValueProps = (
|
||||
),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -721,7 +721,7 @@ export const getMatchValueProps = (
|
||||
label: getTextContent(processedHeadline.default ?? ""),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -796,7 +796,7 @@ export const getMatchValueProps = (
|
||||
label: getTextContent(processedHeadline.default ?? ""),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -877,7 +877,7 @@ export const getMatchValueProps = (
|
||||
label: getTextContent(processedHeadline.default ?? ""),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -1120,7 +1120,7 @@ export const getActionValueOptions = (
|
||||
label: getTextContent(processedHeadline.default ?? ""),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -1178,7 +1178,7 @@ export const getActionValueOptions = (
|
||||
label: getTextContent(getLocalizedValue(element.headline, "default")),
|
||||
value: element.id,
|
||||
meta: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -1230,12 +1230,12 @@ export const getActionValueOptions = (
|
||||
|
||||
const isUsedInLeftOperand = (
|
||||
leftOperand: TLeftOperand,
|
||||
type: "question" | "hiddenField" | "variable",
|
||||
type: "element" | "hiddenField" | "variable",
|
||||
id: string
|
||||
): boolean => {
|
||||
switch (type) {
|
||||
case "question":
|
||||
return leftOperand.type === "question" && leftOperand.value === id;
|
||||
case "element":
|
||||
return leftOperand.type === "element" && leftOperand.value === id;
|
||||
case "hiddenField":
|
||||
return leftOperand.type === "hiddenField" && leftOperand.value === id;
|
||||
case "variable":
|
||||
@@ -1247,12 +1247,12 @@ const isUsedInLeftOperand = (
|
||||
|
||||
const isUsedInRightOperand = (
|
||||
rightOperand: TRightOperand,
|
||||
type: "question" | "hiddenField" | "variable",
|
||||
type: "element" | "hiddenField" | "variable",
|
||||
id: string
|
||||
): boolean => {
|
||||
switch (type) {
|
||||
case "question":
|
||||
return rightOperand.type === "question" && rightOperand.value === id;
|
||||
case "element":
|
||||
return rightOperand.type === "element" && rightOperand.value === id;
|
||||
case "hiddenField":
|
||||
return rightOperand.type === "hiddenField" && rightOperand.value === id;
|
||||
case "variable":
|
||||
@@ -1277,8 +1277,8 @@ export const findQuestionUsedInLogic = (survey: TSurvey, questionId: TSurveyQues
|
||||
} else {
|
||||
// It's a TSingleCondition
|
||||
return (
|
||||
(condition.rightOperand && isUsedInRightOperand(condition.rightOperand, "question", questionId)) ||
|
||||
isUsedInLeftOperand(condition.leftOperand, "question", questionId)
|
||||
(condition.rightOperand && isUsedInRightOperand(condition.rightOperand, "element", questionId)) ||
|
||||
isUsedInLeftOperand(condition.leftOperand, "element", questionId)
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1329,8 +1329,8 @@ export const isUsedInQuota = (
|
||||
if (questionId) {
|
||||
return quota.logic.conditions.some(
|
||||
(condition) =>
|
||||
(condition.rightOperand && isUsedInRightOperand(condition.rightOperand, "question", questionId)) ||
|
||||
isUsedInLeftOperand(condition.leftOperand, "question", questionId)
|
||||
(condition.rightOperand && isUsedInRightOperand(condition.rightOperand, "element", questionId)) ||
|
||||
isUsedInLeftOperand(condition.leftOperand, "element", questionId)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1441,7 +1441,7 @@ export const findOptionUsedInLogic = (
|
||||
};
|
||||
|
||||
const isUsedInOperand = (condition: TSingleCondition): boolean => {
|
||||
if (condition.leftOperand.type === "question" && condition.leftOperand.value === questionId) {
|
||||
if (condition.leftOperand.type === "element" && condition.leftOperand.value === questionId) {
|
||||
if (checkInLeftOperand) {
|
||||
if (condition.leftOperand.meta && Object.entries(condition.leftOperand.meta).length > 0) {
|
||||
const optionIdInMeta = Object.values(condition.leftOperand.meta).some(
|
||||
|
||||
@@ -96,7 +96,7 @@ export class RecallNode extends DecoratorNode<ReactNode> {
|
||||
constructor(payload?: RecallPayload, key?: NodeKey) {
|
||||
super(key);
|
||||
const defaultPayload: RecallPayload = {
|
||||
recallItem: { id: "", label: "", type: "question" },
|
||||
recallItem: { id: "", label: "", type: "element" },
|
||||
fallbackValue: "",
|
||||
};
|
||||
const actualPayload = payload || defaultPayload;
|
||||
|
||||
@@ -20,18 +20,37 @@ interface SurveyQuestion {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface Condition {
|
||||
// Single condition type (leaf node)
|
||||
interface SingleCondition {
|
||||
id: string;
|
||||
leftOperand?: { value: string; type: string; meta?: Record<string, unknown> };
|
||||
operator?: string;
|
||||
leftOperand: { value: string; type: string; meta?: Record<string, unknown> };
|
||||
operator: string;
|
||||
rightOperand?: { type: string; value: string | number | string[] };
|
||||
conditions?: Condition[];
|
||||
connector?: string;
|
||||
connector?: undefined; // Single conditions don't have connectors
|
||||
}
|
||||
|
||||
// Condition group type (has nested conditions)
|
||||
interface ConditionGroup {
|
||||
id: string;
|
||||
connector: "and" | "or";
|
||||
conditions: Condition[];
|
||||
}
|
||||
|
||||
// Union type for both
|
||||
type Condition = SingleCondition | ConditionGroup;
|
||||
|
||||
// Type guards
|
||||
const isSingleCondition = (condition: Condition): condition is SingleCondition => {
|
||||
return "leftOperand" in condition && "operator" in condition;
|
||||
};
|
||||
|
||||
const isConditionGroup = (condition: Condition): condition is ConditionGroup => {
|
||||
return "conditions" in condition && "connector" in condition;
|
||||
};
|
||||
|
||||
interface SurveyLogic {
|
||||
id: string;
|
||||
conditions: Condition;
|
||||
conditions: ConditionGroup; // Logic always starts with a condition group
|
||||
actions: LogicAction[];
|
||||
}
|
||||
|
||||
@@ -74,6 +93,7 @@ interface CTAMigrationStats {
|
||||
|
||||
/**
|
||||
* Check if a condition references a CTA element with a specific operator
|
||||
* Can handle both SingleCondition and ConditionGroup
|
||||
*/
|
||||
const conditionReferencesCTA = (
|
||||
condition: Condition | null | undefined,
|
||||
@@ -82,14 +102,19 @@ const conditionReferencesCTA = (
|
||||
): boolean => {
|
||||
if (!condition) return false;
|
||||
|
||||
if (condition.leftOperand?.value === ctaElementId) {
|
||||
if (operator) {
|
||||
return condition.operator === operator;
|
||||
// Check if it's a single condition
|
||||
if (isSingleCondition(condition)) {
|
||||
if (condition.leftOperand.value === ctaElementId) {
|
||||
if (operator) {
|
||||
return condition.operator === operator;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (condition.conditions && Array.isArray(condition.conditions)) {
|
||||
// It's a condition group - check nested conditions
|
||||
if (isConditionGroup(condition)) {
|
||||
return condition.conditions.some((c) => conditionReferencesCTA(c, ctaElementId, operator));
|
||||
}
|
||||
|
||||
@@ -100,23 +125,28 @@ const conditionReferencesCTA = (
|
||||
* Remove conditions that reference a CTA element with specific operators
|
||||
*/
|
||||
const removeCtaConditions = (
|
||||
conditionGroup: Condition,
|
||||
conditionGroup: ConditionGroup,
|
||||
ctaElementId: string,
|
||||
operatorsToRemove: string[]
|
||||
): Condition | null => {
|
||||
if (!conditionGroup.conditions) return conditionGroup;
|
||||
|
||||
): ConditionGroup | null => {
|
||||
const filteredConditions = conditionGroup.conditions.filter((condition) => {
|
||||
if (condition.leftOperand?.value === ctaElementId && condition.operator) {
|
||||
return !operatorsToRemove.includes(condition.operator);
|
||||
// Check if it's a single condition referencing the CTA
|
||||
if (isSingleCondition(condition)) {
|
||||
if (condition.leftOperand.value === ctaElementId) {
|
||||
return !operatorsToRemove.includes(condition.operator);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (condition.conditions && Array.isArray(condition.conditions)) {
|
||||
// It's a condition group - recurse
|
||||
if (isConditionGroup(condition)) {
|
||||
const cleaned = removeCtaConditions(condition, ctaElementId, operatorsToRemove);
|
||||
if (!cleaned?.conditions || cleaned.conditions.length === 0) {
|
||||
if (!cleaned || cleaned.conditions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
// Replace the condition with the cleaned version
|
||||
Object.assign(condition, cleaned);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -333,6 +363,53 @@ const updateLogicFallback = (
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert logic operand types from "question" to "element" recursively (immutable)
|
||||
* @param condition - Condition or condition group to convert
|
||||
* @returns New condition object with "element" type instead of "question"
|
||||
*/
|
||||
const convertQuestionToElementType = (condition: Condition | null | undefined): Condition | null => {
|
||||
if (!condition) return null;
|
||||
|
||||
// Handle single condition
|
||||
if (isSingleCondition(condition)) {
|
||||
const newCondition: SingleCondition = { ...condition };
|
||||
|
||||
// Update leftOperand if it's of type "question"
|
||||
if (condition.leftOperand.type === "question") {
|
||||
newCondition.leftOperand = {
|
||||
...condition.leftOperand,
|
||||
type: "element",
|
||||
};
|
||||
}
|
||||
|
||||
// Update rightOperand if it exists and is of type "question"
|
||||
if (condition.rightOperand && condition.rightOperand.type === "question") {
|
||||
newCondition.rightOperand = {
|
||||
...condition.rightOperand,
|
||||
type: "element",
|
||||
};
|
||||
}
|
||||
|
||||
return newCondition;
|
||||
}
|
||||
|
||||
// Handle condition group
|
||||
if (isConditionGroup(condition)) {
|
||||
const newConditionGroup: ConditionGroup = {
|
||||
...condition,
|
||||
conditions: condition.conditions.map((nestedCondition) => {
|
||||
const converted = convertQuestionToElementType(nestedCondition);
|
||||
return converted ?? nestedCondition;
|
||||
}),
|
||||
};
|
||||
|
||||
return newConditionGroup;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Migrate a survey from questions to blocks structure
|
||||
* Each question becomes a block with a single element
|
||||
@@ -384,10 +461,22 @@ const migrateQuestionsSurveyToBlocks = (
|
||||
// Phase 2: Update all logic references
|
||||
for (const block of blocks) {
|
||||
if (block.logic && block.logic.length > 0) {
|
||||
block.logic = block.logic.map((item) => ({
|
||||
...item,
|
||||
actions: updateLogicActions(item.actions, questionIdToBlockId, endingIds),
|
||||
}));
|
||||
block.logic = block.logic.map((item) => {
|
||||
// Convert "question" type to "element" type in conditions (immutably)
|
||||
const updatedConditions = convertQuestionToElementType(item.conditions);
|
||||
|
||||
// Since item.conditions is always a ConditionGroup, the result should be too
|
||||
if (!updatedConditions || !isConditionGroup(updatedConditions)) {
|
||||
// This should never happen, but if it does, keep the original
|
||||
return item;
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
conditions: updatedConditions,
|
||||
actions: updateLogicActions(item.actions, questionIdToBlockId, endingIds),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (block.logicFallback) {
|
||||
|
||||
@@ -166,7 +166,7 @@ describe("Survey Logic", () => {
|
||||
const singleCondition: TSingleCondition = {
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test" },
|
||||
};
|
||||
expect(isConditionGroup(singleCondition)).toBe(false);
|
||||
@@ -199,13 +199,13 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test answer" },
|
||||
},
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: 42 },
|
||||
},
|
||||
],
|
||||
@@ -222,13 +222,13 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "wrong answer" },
|
||||
},
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: 42 },
|
||||
},
|
||||
],
|
||||
@@ -245,7 +245,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test answer" },
|
||||
},
|
||||
{
|
||||
@@ -255,7 +255,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "wrong" },
|
||||
},
|
||||
{
|
||||
@@ -280,13 +280,13 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test answer" },
|
||||
},
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "wrong value" },
|
||||
},
|
||||
],
|
||||
@@ -303,13 +303,13 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "wrong answer" },
|
||||
},
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "wrong value" },
|
||||
},
|
||||
],
|
||||
@@ -508,7 +508,7 @@ describe("Survey Logic", () => {
|
||||
objective: "calculate",
|
||||
variableId: "var2",
|
||||
operator: "add",
|
||||
value: { type: "question", value: "q2" },
|
||||
value: { type: "element", value: "q2" },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -609,7 +609,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "contains",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test" },
|
||||
},
|
||||
],
|
||||
@@ -623,7 +623,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "doesNotContain",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "invalid" },
|
||||
},
|
||||
],
|
||||
@@ -639,7 +639,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition3",
|
||||
operator: "startsWith",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test" },
|
||||
},
|
||||
],
|
||||
@@ -655,7 +655,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition4",
|
||||
operator: "doesNotStartWith",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "invalid" },
|
||||
},
|
||||
],
|
||||
@@ -671,7 +671,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition5",
|
||||
operator: "endsWith",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "answer" },
|
||||
},
|
||||
],
|
||||
@@ -685,7 +685,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition6",
|
||||
operator: "doesNotEndWith",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "invalid" },
|
||||
},
|
||||
],
|
||||
@@ -704,7 +704,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isGreaterThan",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "30" },
|
||||
},
|
||||
],
|
||||
@@ -720,7 +720,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isLessThan",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "50" },
|
||||
},
|
||||
],
|
||||
@@ -734,7 +734,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition3",
|
||||
operator: "isGreaterThanOrEqual",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "42" },
|
||||
},
|
||||
],
|
||||
@@ -750,7 +750,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition4",
|
||||
operator: "isLessThanOrEqual",
|
||||
leftOperand: { type: "question", value: "q2" },
|
||||
leftOperand: { type: "element", value: "q2" },
|
||||
rightOperand: { type: "static", value: "42" },
|
||||
},
|
||||
],
|
||||
@@ -769,7 +769,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isAfter",
|
||||
leftOperand: { type: "question", value: "q5" },
|
||||
leftOperand: { type: "element", value: "q5" },
|
||||
rightOperand: { type: "static", value: "2022-12-31" },
|
||||
},
|
||||
],
|
||||
@@ -783,7 +783,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isBefore",
|
||||
leftOperand: { type: "question", value: "q5" },
|
||||
leftOperand: { type: "element", value: "q5" },
|
||||
rightOperand: { type: "static", value: "2023-01-02" },
|
||||
},
|
||||
],
|
||||
@@ -797,7 +797,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition3",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q5" },
|
||||
leftOperand: { type: "element", value: "q5" },
|
||||
rightOperand: { type: "static", value: "2023-01-01" },
|
||||
},
|
||||
],
|
||||
@@ -816,7 +816,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "includesAllOf",
|
||||
leftOperand: { type: "question", value: "q4" },
|
||||
leftOperand: { type: "element", value: "q4" },
|
||||
rightOperand: { type: "static", value: ["opt1", "opt2"] },
|
||||
},
|
||||
],
|
||||
@@ -832,7 +832,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "includesOneOf",
|
||||
leftOperand: { type: "question", value: "q4" },
|
||||
leftOperand: { type: "element", value: "q4" },
|
||||
rightOperand: { type: "static", value: ["opt1", "Invalid Option"] },
|
||||
},
|
||||
],
|
||||
@@ -848,7 +848,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition3",
|
||||
operator: "doesNotIncludeAllOf",
|
||||
leftOperand: { type: "question", value: "q4" },
|
||||
leftOperand: { type: "element", value: "q4" },
|
||||
rightOperand: { type: "static", value: ["Invalid 1", "Invalid 2"] },
|
||||
},
|
||||
],
|
||||
@@ -864,7 +864,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition4",
|
||||
operator: "doesNotIncludeOneOf",
|
||||
leftOperand: { type: "question", value: "q4" },
|
||||
leftOperand: { type: "element", value: "q4" },
|
||||
rightOperand: { type: "static", value: ["opt3", "Invalid Option"] },
|
||||
},
|
||||
],
|
||||
@@ -883,7 +883,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isSubmitted",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -898,7 +898,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isSkipped",
|
||||
leftOperand: { type: "question", value: "emptyField" },
|
||||
leftOperand: { type: "element", value: "emptyField" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -913,7 +913,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition3",
|
||||
operator: "isBooked",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -929,7 +929,7 @@ describe("Survey Logic", () => {
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
value: "q8",
|
||||
meta: { row: "0" },
|
||||
},
|
||||
@@ -948,7 +948,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isSubmitted",
|
||||
leftOperand: { type: "question", value: "q6" },
|
||||
leftOperand: { type: "element", value: "q6" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -963,7 +963,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isSkipped",
|
||||
leftOperand: { type: "question", value: "skippedUpload" },
|
||||
leftOperand: { type: "element", value: "skippedUpload" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -984,7 +984,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isPartiallySubmitted",
|
||||
leftOperand: { type: "question", value: "q8" },
|
||||
leftOperand: { type: "element", value: "q8" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1009,7 +1009,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isCompletelySubmitted",
|
||||
leftOperand: { type: "question", value: "q8" },
|
||||
leftOperand: { type: "element", value: "q8" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1034,7 +1034,7 @@ describe("Survey Logic", () => {
|
||||
id: "condition1",
|
||||
// @ts-ignore - intentionally using invalid operator for test
|
||||
operator: "invalidOperator",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test" },
|
||||
},
|
||||
],
|
||||
@@ -1049,7 +1049,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "nonExistentId" },
|
||||
leftOperand: { type: "element", value: "nonExistentId" },
|
||||
rightOperand: { type: "static", value: "test" },
|
||||
},
|
||||
],
|
||||
@@ -1092,7 +1092,7 @@ describe("Survey Logic", () => {
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
value: "q8",
|
||||
meta: { row: "invalid-row" },
|
||||
},
|
||||
@@ -1112,7 +1112,7 @@ describe("Survey Logic", () => {
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
value: "q8",
|
||||
meta: { row: "99" }, // Invalid row index
|
||||
},
|
||||
@@ -1136,7 +1136,7 @@ describe("Survey Logic", () => {
|
||||
id: "condition1",
|
||||
operator: "isEmpty",
|
||||
leftOperand: {
|
||||
type: "question",
|
||||
type: "element",
|
||||
value: "q8",
|
||||
meta: { row: "0" },
|
||||
},
|
||||
@@ -1156,7 +1156,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "doesNotEqual",
|
||||
leftOperand: { type: "question", value: "q7" },
|
||||
leftOperand: { type: "element", value: "q7" },
|
||||
rightOperand: { type: "static", value: "option2" },
|
||||
},
|
||||
],
|
||||
@@ -1179,8 +1179,8 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "dateQ1" },
|
||||
rightOperand: { type: "question", value: "dateQ2" },
|
||||
leftOperand: { type: "element", value: "dateQ1" },
|
||||
rightOperand: { type: "element", value: "dateQ2" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1225,8 +1225,8 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "doesNotEqual",
|
||||
leftOperand: { type: "question", value: "dateQ1" },
|
||||
rightOperand: { type: "question", value: "dateQ2" },
|
||||
leftOperand: { type: "element", value: "dateQ1" },
|
||||
rightOperand: { type: "element", value: "dateQ2" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1273,7 +1273,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "multiValue" },
|
||||
leftOperand: { type: "element", value: "multiValue" },
|
||||
rightOperand: { type: "static", value: "option1" },
|
||||
},
|
||||
],
|
||||
@@ -1290,8 +1290,8 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
rightOperand: { type: "question", value: "multiQ" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "element", value: "multiQ" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1312,7 +1312,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isEmpty",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1328,7 +1328,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isNotEmpty",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1345,7 +1345,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "isAnyOf",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: ["wrong answer", "test answer", "another answer"] },
|
||||
},
|
||||
],
|
||||
@@ -1360,7 +1360,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "isAnyOf",
|
||||
leftOperand: { type: "question", value: "q1" },
|
||||
leftOperand: { type: "element", value: "q1" },
|
||||
rightOperand: { type: "static", value: "test answer" },
|
||||
},
|
||||
],
|
||||
@@ -1402,7 +1402,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition1",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "multiChoiceWithOther" },
|
||||
leftOperand: { type: "element", value: "multiChoiceWithOther" },
|
||||
rightOperand: { type: "static", value: "Custom Option" },
|
||||
},
|
||||
],
|
||||
@@ -1423,7 +1423,7 @@ describe("Survey Logic", () => {
|
||||
{
|
||||
id: "condition2",
|
||||
operator: "equals",
|
||||
leftOperand: { type: "question", value: "multiChoiceWithOther" },
|
||||
leftOperand: { type: "element", value: "multiChoiceWithOther" },
|
||||
rightOperand: { type: "static", value: "opt1" },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -88,7 +88,7 @@ const getLeftOperandValue = (
|
||||
selectedLanguage: string
|
||||
) => {
|
||||
switch (leftOperand.type) {
|
||||
case "question":
|
||||
case "element":
|
||||
const questions = getElementsFromSurvey(localSurvey);
|
||||
const currentQuestion = questions.find((q) => q.id === leftOperand.value);
|
||||
if (!currentQuestion) return undefined;
|
||||
@@ -188,7 +188,7 @@ const getRightOperandValue = (
|
||||
if (!rightOperand) return undefined;
|
||||
|
||||
switch (rightOperand.type) {
|
||||
case "question":
|
||||
case "element":
|
||||
return data[rightOperand.value];
|
||||
case "variable":
|
||||
const variables = localSurvey.variables || [];
|
||||
@@ -224,7 +224,7 @@ const evaluateSingleCondition = (
|
||||
let leftField: TSurveyElement | TSurveyVariable | string;
|
||||
|
||||
const questions = getElementsFromSurvey(localSurvey);
|
||||
if (condition.leftOperand?.type === "question") {
|
||||
if (condition.leftOperand?.type === "element") {
|
||||
leftField = questions.find((q) => q.id === condition.leftOperand?.value) ?? "";
|
||||
} else if (condition.leftOperand?.type === "variable") {
|
||||
leftField = localSurvey.variables.find((v) => v.id === condition.leftOperand?.value) as TSurveyVariable;
|
||||
@@ -236,7 +236,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
let rightField: TSurveyElement | TSurveyVariable | string;
|
||||
|
||||
if (condition.rightOperand?.type === "question") {
|
||||
if (condition.rightOperand?.type === "element") {
|
||||
rightField = questions.find((q) => q.id === condition.rightOperand?.value) ?? "";
|
||||
} else if (condition.rightOperand?.type === "variable") {
|
||||
rightField = localSurvey.variables.find(
|
||||
@@ -258,7 +258,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
switch (condition.operator) {
|
||||
case "equals":
|
||||
if (condition.leftOperand.type === "question") {
|
||||
if (condition.leftOperand.type === "element") {
|
||||
if (
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.Date &&
|
||||
typeof leftValue === "string" &&
|
||||
@@ -270,7 +270,7 @@ const evaluateSingleCondition = (
|
||||
}
|
||||
|
||||
// when left value is of openText, hiddenField, variable and right value is of multichoice
|
||||
if (condition.rightOperand?.type === "question") {
|
||||
if (condition.rightOperand?.type === "element") {
|
||||
if ((rightField as TSurveyElement).type === TSurveyElementTypeEnum.MultipleChoiceMulti) {
|
||||
if (Array.isArray(rightValue) && typeof leftValue === "string" && rightValue.length === 1) {
|
||||
return rightValue.includes(leftValue as string);
|
||||
@@ -294,7 +294,7 @@ const evaluateSingleCondition = (
|
||||
case "doesNotEqual":
|
||||
// when left value is of picture selection question and right value is its option
|
||||
if (
|
||||
condition.leftOperand.type === "question" &&
|
||||
condition.leftOperand.type === "element" &&
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.PictureSelection &&
|
||||
Array.isArray(leftValue) &&
|
||||
leftValue.length > 0 &&
|
||||
@@ -305,7 +305,7 @@ const evaluateSingleCondition = (
|
||||
|
||||
// when left value is of date question and right value is string
|
||||
if (
|
||||
condition.leftOperand.type === "question" &&
|
||||
condition.leftOperand.type === "element" &&
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.Date &&
|
||||
typeof leftValue === "string" &&
|
||||
typeof rightValue === "string"
|
||||
@@ -314,7 +314,7 @@ const evaluateSingleCondition = (
|
||||
}
|
||||
|
||||
// when left value is of openText, hiddenField, variable and right value is of multichoice
|
||||
if (condition.rightOperand?.type === "question") {
|
||||
if (condition.rightOperand?.type === "element") {
|
||||
if ((rightField as TSurveyElement).type === TSurveyElementTypeEnum.MultipleChoiceMulti) {
|
||||
if (Array.isArray(rightValue) && typeof leftValue === "string" && rightValue.length === 1) {
|
||||
return !rightValue.includes(leftValue as string);
|
||||
@@ -350,7 +350,7 @@ const evaluateSingleCondition = (
|
||||
case "isSubmitted":
|
||||
if (typeof leftValue === "string") {
|
||||
if (
|
||||
condition.leftOperand.type === "question" &&
|
||||
condition.leftOperand.type === "element" &&
|
||||
(leftField as TSurveyElement).type === TSurveyElementTypeEnum.FileUpload &&
|
||||
leftValue
|
||||
) {
|
||||
@@ -472,7 +472,7 @@ const performCalculation = (
|
||||
operandValue = value;
|
||||
}
|
||||
break;
|
||||
case "question":
|
||||
case "element":
|
||||
case "hiddenField":
|
||||
const val = data[action.value.value];
|
||||
if (typeof val === "number" || typeof val === "string") {
|
||||
|
||||
@@ -56,9 +56,9 @@ export const ZConnector = z.enum(["and", "or"]);
|
||||
export type TConnector = z.infer<typeof ZConnector>;
|
||||
|
||||
// Dynamic field types for conditions
|
||||
const ZDynamicQuestion = z.object({
|
||||
type: z.literal("question"),
|
||||
value: z.string().min(1, "Conditional Logic: Question id cannot be empty"),
|
||||
const ZDynamicElement = z.object({
|
||||
type: z.literal("element"),
|
||||
value: z.string().min(1, "Conditional Logic: Element id cannot be empty"),
|
||||
meta: z.record(z.string()).optional(),
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ const ZDynamicHiddenField = z.object({
|
||||
value: z.string().min(1, "Conditional Logic: Hidden field id cannot be empty"),
|
||||
});
|
||||
|
||||
export const ZDynamicLogicFieldValue = z.union([ZDynamicQuestion, ZDynamicVariable, ZDynamicHiddenField], {
|
||||
export const ZDynamicLogicFieldValue = z.union([ZDynamicElement, ZDynamicVariable, ZDynamicHiddenField], {
|
||||
message: "Conditional Logic: Invalid dynamic field value",
|
||||
});
|
||||
|
||||
|
||||
@@ -2000,7 +2000,7 @@ const validateConditions = (
|
||||
const { leftOperand, operator, rightOperand } = condition;
|
||||
|
||||
// Validate left operand
|
||||
if (leftOperand.type === "question") {
|
||||
if (leftOperand.type === "element") {
|
||||
const questionId = leftOperand.value;
|
||||
const questionIdx = survey.questions.findIndex((q) => q.id === questionId);
|
||||
const question = questionIdx !== -1 ? survey.questions[questionIdx] : undefined;
|
||||
@@ -2057,7 +2057,7 @@ const validateConditions = (
|
||||
|
||||
if (question.type === TSurveyQuestionTypeEnum.OpenText) {
|
||||
// Validate right operand
|
||||
if (rightOperand?.type === "question") {
|
||||
if (rightOperand?.type === "element") {
|
||||
const quesId = rightOperand.value;
|
||||
const ques = survey.questions.find((q) => q.id === quesId);
|
||||
|
||||
@@ -2289,7 +2289,7 @@ const validateConditions = (
|
||||
});
|
||||
}
|
||||
} else if (question.type === TSurveyQuestionTypeEnum.Date) {
|
||||
if (rightOperand?.type === "question") {
|
||||
if (rightOperand?.type === "element") {
|
||||
const quesId = rightOperand.value;
|
||||
const ques = survey.questions.find((q) => q.id === quesId);
|
||||
|
||||
@@ -2419,7 +2419,7 @@ const validateConditions = (
|
||||
}
|
||||
|
||||
// Validate right operand
|
||||
if (rightOperand?.type === "question") {
|
||||
if (rightOperand?.type === "element") {
|
||||
const questionId = rightOperand.value;
|
||||
const question = survey.questions.find((q) => q.id === questionId);
|
||||
|
||||
@@ -2516,7 +2516,7 @@ const validateConditions = (
|
||||
}
|
||||
|
||||
// Validate right operand
|
||||
if (rightOperand?.type === "question") {
|
||||
if (rightOperand?.type === "element") {
|
||||
const questionId = rightOperand.value;
|
||||
const question = survey.questions.find((q) => q.id === questionId);
|
||||
|
||||
@@ -2638,7 +2638,7 @@ const validateActions = (
|
||||
};
|
||||
}
|
||||
|
||||
if (action.value.type === "question") {
|
||||
if (action.value.type === "element") {
|
||||
const allowedQuestions = [
|
||||
TSurveyQuestionTypeEnum.OpenText,
|
||||
TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
@@ -2670,7 +2670,7 @@ const validateActions = (
|
||||
};
|
||||
}
|
||||
|
||||
if (action.value.type === "question") {
|
||||
if (action.value.type === "element") {
|
||||
const allowedQuestions = [TSurveyQuestionTypeEnum.Rating, TSurveyQuestionTypeEnum.NPS];
|
||||
|
||||
const selectedQuestion = previousQuestions.find((q) => q.id === action.value.value);
|
||||
@@ -2957,7 +2957,7 @@ const validateBlockConditions = (
|
||||
const { leftOperand, operator, rightOperand } = condition;
|
||||
|
||||
// Validate left operand
|
||||
if (leftOperand.type === "question") {
|
||||
if (leftOperand.type === "element") {
|
||||
const elementId = leftOperand.value;
|
||||
const elementInfo = allElements.get(elementId);
|
||||
|
||||
@@ -3015,7 +3015,7 @@ const validateBlockConditions = (
|
||||
|
||||
if (element.type === TSurveyElementTypeEnum.OpenText) {
|
||||
// Validate right operand
|
||||
if (rightOperand?.type === "question") {
|
||||
if (rightOperand?.type === "element") {
|
||||
const elemId = rightOperand.value;
|
||||
const elem = allElements.get(elemId);
|
||||
|
||||
@@ -3263,7 +3263,7 @@ const validateBlockActions = (
|
||||
}
|
||||
|
||||
if (variable.type === "text") {
|
||||
if (action.value.type === "question") {
|
||||
if (action.value.type === "element") {
|
||||
const allowedElements = [
|
||||
TSurveyElementTypeEnum.OpenText,
|
||||
TSurveyElementTypeEnum.MultipleChoiceSingle,
|
||||
@@ -3286,7 +3286,7 @@ const validateBlockActions = (
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (action.value.type === "question") {
|
||||
if (action.value.type === "element") {
|
||||
const allowedElements = [TSurveyElementTypeEnum.Rating, TSurveyElementTypeEnum.NPS];
|
||||
|
||||
const selectedElement = allElements.get(action.value.value);
|
||||
@@ -3969,7 +3969,7 @@ export type TSortOption = z.infer<typeof ZSortOption>;
|
||||
export const ZSurveyRecallItem = z.object({
|
||||
id: z.string(),
|
||||
label: z.string(),
|
||||
type: z.enum(["question", "hiddenField", "attributeClass", "variable"]),
|
||||
type: z.enum(["element", "hiddenField", "attributeClass", "variable"]),
|
||||
});
|
||||
|
||||
export type TSurveyRecallItem = z.infer<typeof ZSurveyRecallItem>;
|
||||
|
||||
Reference in New Issue
Block a user