mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 10:19:51 -06:00
fix: adds data migration script
This commit is contained in:
@@ -133,7 +133,7 @@ export function AdvancedLogicEditorConditions({
|
||||
{connector}
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-2 rounded-lg border border-slate-200 bg-slate-100 p-4">
|
||||
<div className="rounded-lg border border-slate-200 bg-slate-100 p-4">
|
||||
<AdvancedLogicEditorConditions
|
||||
conditions={condition}
|
||||
updateQuestion={updateQuestion}
|
||||
@@ -184,7 +184,7 @@ export function AdvancedLogicEditorConditions({
|
||||
const { show, options, showInput = false, inputType } = getMatchValueProps(condition, localSurvey);
|
||||
|
||||
return (
|
||||
<div key={condition.id} className="mt-2 flex items-center justify-between gap-4">
|
||||
<div key={condition.id} className="flex items-center justify-between gap-4">
|
||||
{index === 0 ? (
|
||||
<div className="text-sm">When</div>
|
||||
) : (
|
||||
|
||||
@@ -292,7 +292,7 @@ export const getMatchValueProps = (
|
||||
label: getLocalizedValue(choice.label, "default"),
|
||||
value: choice.id,
|
||||
meta: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -308,7 +308,7 @@ export const getMatchValueProps = (
|
||||
label: choice.imageUrl.split("/").pop() || `Image ${idx + 1}`,
|
||||
value: choice.id,
|
||||
meta: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -324,7 +324,7 @@ export const getMatchValueProps = (
|
||||
label: `${idx + 1}`,
|
||||
value: idx + 1,
|
||||
meta: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -371,7 +371,7 @@ export const getMatchValueProps = (
|
||||
label: `${idx}`,
|
||||
value: idx,
|
||||
meta: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -1007,7 +1007,7 @@ export const findOptionUsedInLogic = (survey: TSurvey, questionId: string, optio
|
||||
|
||||
const isUsedInOperand = (condition: TSingleCondition): boolean => {
|
||||
if (condition.leftOperand.type === "question" && condition.leftOperand.id === questionId) {
|
||||
if (condition.rightOperand && condition.rightOperand.type === "choice") {
|
||||
if (condition.rightOperand && condition.rightOperand.type === "static") {
|
||||
if (Array.isArray(condition.rightOperand.value)) {
|
||||
return condition.rightOperand.value.includes(optionId);
|
||||
} else {
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions -- string interpolation is allowed in migration scripts */
|
||||
|
||||
/* eslint-disable no-console -- logging is allowed in migration scripts */
|
||||
// !@gupta-piyush19: WIP
|
||||
// Pending:
|
||||
// 1. Options assignment: text to id
|
||||
// 2. have to check and modify data storing in right operand based on the saving pattern of current logic
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import type {
|
||||
TAction,
|
||||
TRightOperand,
|
||||
TSingleCondition,
|
||||
TSurveyAdvancedLogic,
|
||||
TSurveyLogicCondition,
|
||||
} from "@formbricks/types/surveys/logic";
|
||||
import type { TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
import {
|
||||
type TSurveyMultipleChoiceQuestion,
|
||||
type TSurveyQuestion,
|
||||
TSurveyQuestionTypeEnum,
|
||||
} from "@formbricks/types/surveys/types";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -21,6 +24,10 @@ interface TOldLogic {
|
||||
destination: string;
|
||||
}
|
||||
|
||||
const isOldLogic = (logic: TOldLogic | TSurveyAdvancedLogic): logic is TOldLogic => {
|
||||
return Object.keys(logic).some((key) => ["condition", "destination", "value"].includes(key));
|
||||
};
|
||||
|
||||
const doesRightOperandExist = (operator: TSurveyLogicCondition): boolean => {
|
||||
return ![
|
||||
"isAccepted",
|
||||
@@ -33,27 +40,122 @@ const doesRightOperandExist = (operator: TSurveyLogicCondition): boolean => {
|
||||
].includes(operator);
|
||||
};
|
||||
|
||||
// Helper function to convert old logic condition to new format
|
||||
function convertLogicCondition(
|
||||
const getChoiceId = (question: TSurveyMultipleChoiceQuestion, choiceText: string): string | undefined => {
|
||||
const choiceOption = question.choices.find((choice) => choice.label.default === choiceText);
|
||||
if (choiceOption) {
|
||||
return choiceOption.id;
|
||||
}
|
||||
if (question.choices.at(-1)?.id === "other") {
|
||||
return "other";
|
||||
}
|
||||
};
|
||||
|
||||
const getRightOperandValue = (
|
||||
surveyId: string,
|
||||
oldCondition: string,
|
||||
oldValue: string | string[] | undefined,
|
||||
questionId: string
|
||||
): TSingleCondition {
|
||||
question: TSurveyQuestion
|
||||
): TRightOperand | undefined => {
|
||||
if (["lessThan", "lessEqual", "greaterThan", "greaterEqual"].includes(oldCondition)) {
|
||||
return {
|
||||
type: "static",
|
||||
value: parseInt(oldValue as string),
|
||||
};
|
||||
}
|
||||
|
||||
if (["equals", "notEquals"].includes(oldCondition)) {
|
||||
if (["string", "number"].includes(typeof oldValue)) {
|
||||
if (question.type === TSurveyQuestionTypeEnum.Rating || question.type === TSurveyQuestionTypeEnum.NPS) {
|
||||
return {
|
||||
type: "static",
|
||||
value: parseInt(oldValue as string),
|
||||
};
|
||||
} else if (
|
||||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceSingle ||
|
||||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceMulti
|
||||
) {
|
||||
const choiceId = getChoiceId(question, oldValue as string);
|
||||
if (choiceId) {
|
||||
return {
|
||||
type: "static",
|
||||
value: choiceId,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
} else if (question.type === TSurveyQuestionTypeEnum.PictureSelection) {
|
||||
return {
|
||||
type: "static",
|
||||
value: oldValue as string,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Invalid value for 'equals' or 'notEquals' condition in survey ${surveyId}`);
|
||||
}
|
||||
|
||||
if (["includesAll", "includesOne"].includes(oldCondition)) {
|
||||
let choiceIds: string[] = [];
|
||||
|
||||
if (oldValue && Array.isArray(oldValue)) {
|
||||
if (
|
||||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceMulti ||
|
||||
question.type === TSurveyQuestionTypeEnum.MultipleChoiceSingle
|
||||
) {
|
||||
oldValue.forEach((choiceText) => {
|
||||
const choiceId = getChoiceId(question, choiceText);
|
||||
if (choiceId) {
|
||||
choiceIds.push(choiceId);
|
||||
}
|
||||
});
|
||||
|
||||
choiceIds = Array.from(new Set(choiceIds));
|
||||
|
||||
return {
|
||||
type: "static",
|
||||
value: choiceIds,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: "static",
|
||||
value: oldValue,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Invalid value for 'includesAll' or 'includesOne' condition in survey ${surveyId}`);
|
||||
}
|
||||
|
||||
throw new Error(`Invalid condition ${oldCondition} in survey ${surveyId}`);
|
||||
};
|
||||
|
||||
// Helper function to convert old logic condition to new format
|
||||
function convertLogicCondition(
|
||||
surveyId: string,
|
||||
oldCondition: string,
|
||||
oldValue: string | string[] | undefined,
|
||||
question: TSurveyQuestion
|
||||
): TSingleCondition | undefined {
|
||||
const operator = mapOldConditionToNew(oldCondition);
|
||||
|
||||
let rightOperandValue: TRightOperand | undefined;
|
||||
|
||||
const doesRightOperandExistResult = doesRightOperandExist(operator);
|
||||
if (doesRightOperandExistResult) {
|
||||
rightOperandValue = getRightOperandValue(surveyId, oldCondition, oldValue, question);
|
||||
|
||||
if (!rightOperandValue) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const newCondition: TSingleCondition = {
|
||||
id: createId(),
|
||||
leftOperand: {
|
||||
type: "question",
|
||||
id: questionId,
|
||||
id: question.id,
|
||||
},
|
||||
operator,
|
||||
...(doesRightOperandExist(operator) && {
|
||||
rightOperand: {
|
||||
type: "static",
|
||||
value: oldValue ?? "",
|
||||
},
|
||||
}),
|
||||
rightOperand: rightOperandValue,
|
||||
};
|
||||
|
||||
return newCondition;
|
||||
@@ -85,9 +187,16 @@ function mapOldConditionToNew(oldCondition: string): TSurveyLogicCondition {
|
||||
}
|
||||
|
||||
// Helper function to convert old logic to new format
|
||||
function convertLogic(oldLogic: TOldLogic, questionId: string): TSurveyAdvancedLogic {
|
||||
const condition: TSingleCondition = convertLogicCondition(oldLogic.condition, oldLogic.value, questionId);
|
||||
condition.leftOperand.id = questionId;
|
||||
function convertLogic(
|
||||
surveyId: string,
|
||||
oldLogic: TOldLogic,
|
||||
question: TSurveyQuestion
|
||||
): TSurveyAdvancedLogic | undefined {
|
||||
const condition = convertLogicCondition(surveyId, oldLogic.condition, oldLogic.value, question);
|
||||
|
||||
if (!condition) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const action: TAction = {
|
||||
id: createId(),
|
||||
@@ -99,7 +208,7 @@ function convertLogic(oldLogic: TOldLogic, questionId: string): TSurveyAdvancedL
|
||||
id: createId(),
|
||||
conditions: {
|
||||
id: createId(),
|
||||
connector: null,
|
||||
connector: "and",
|
||||
conditions: [condition],
|
||||
},
|
||||
actions: [action],
|
||||
@@ -114,11 +223,6 @@ async function runMigration(): Promise<void> {
|
||||
|
||||
// Get all surveys with questions containing old logic
|
||||
const relevantSurveys = await tx.survey.findMany({
|
||||
where: {
|
||||
questions: {
|
||||
array_contains: [{ logic: { $exists: true } }],
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
questions: true,
|
||||
@@ -126,22 +230,36 @@ async function runMigration(): Promise<void> {
|
||||
});
|
||||
|
||||
// Process each survey
|
||||
const migrationPromises = relevantSurveys.map(async (survey) => {
|
||||
const updatedQuestions = survey.questions.map((question: TSurveyQuestion) => {
|
||||
if (question.logic && Array.isArray(question.logic)) {
|
||||
const newLogic = (question.logic as TOldLogic[]).map((oldLogic) =>
|
||||
convertLogic(oldLogic, question.id)
|
||||
);
|
||||
return { ...question, logic: newLogic };
|
||||
}
|
||||
return question;
|
||||
});
|
||||
const migrationPromises = relevantSurveys
|
||||
.map((survey) => {
|
||||
let doesThisSurveyHasOldLogic = false;
|
||||
const questions: TSurveyQuestion[] = [];
|
||||
|
||||
return tx.survey.update({
|
||||
where: { id: survey.id },
|
||||
data: { questions: updatedQuestions },
|
||||
});
|
||||
});
|
||||
for (const question of survey.questions) {
|
||||
if (question.logic && Array.isArray(question.logic) && question.logic.some(isOldLogic)) {
|
||||
doesThisSurveyHasOldLogic = true;
|
||||
const newLogic = (question.logic as unknown as TOldLogic[])
|
||||
.map((oldLogic) => convertLogic(survey.id, oldLogic, question))
|
||||
.filter((logic) => logic !== undefined);
|
||||
|
||||
questions.push({ ...question, logic: newLogic });
|
||||
} else {
|
||||
questions.push(question);
|
||||
}
|
||||
}
|
||||
|
||||
if (!doesThisSurveyHasOldLogic) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tx.survey.update({
|
||||
where: { id: survey.id },
|
||||
data: { questions },
|
||||
});
|
||||
})
|
||||
.filter((promise) => promise !== null);
|
||||
|
||||
console.log(`Found ${migrationPromises.length} surveys with old logic`);
|
||||
|
||||
await Promise.all(migrationPromises);
|
||||
|
||||
|
||||
@@ -697,7 +697,7 @@ const churnSurvey = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[0],
|
||||
},
|
||||
},
|
||||
@@ -725,7 +725,7 @@ const churnSurvey = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -753,7 +753,7 @@ const churnSurvey = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[2],
|
||||
},
|
||||
},
|
||||
@@ -781,7 +781,7 @@ const churnSurvey = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[3],
|
||||
},
|
||||
},
|
||||
@@ -809,7 +809,7 @@ const churnSurvey = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[4],
|
||||
},
|
||||
},
|
||||
@@ -1020,7 +1020,7 @@ const earnedAdvocacyScore = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -1103,7 +1103,7 @@ const earnedAdvocacyScore = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[3],
|
||||
},
|
||||
},
|
||||
@@ -1180,7 +1180,7 @@ const improveTrialConversion = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[0],
|
||||
},
|
||||
},
|
||||
@@ -1208,7 +1208,7 @@ const improveTrialConversion = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -1236,7 +1236,7 @@ const improveTrialConversion = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[2],
|
||||
},
|
||||
},
|
||||
@@ -1264,7 +1264,7 @@ const improveTrialConversion = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[3],
|
||||
},
|
||||
},
|
||||
@@ -1292,7 +1292,7 @@ const improveTrialConversion = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[4],
|
||||
},
|
||||
},
|
||||
@@ -1688,7 +1688,7 @@ const improveActivationRate = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -1716,7 +1716,7 @@ const improveActivationRate = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[2],
|
||||
},
|
||||
},
|
||||
@@ -1744,7 +1744,7 @@ const improveActivationRate = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[3],
|
||||
},
|
||||
},
|
||||
@@ -1772,7 +1772,7 @@ const improveActivationRate = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[4],
|
||||
},
|
||||
},
|
||||
@@ -2328,7 +2328,7 @@ const feedbackBox = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[0],
|
||||
},
|
||||
},
|
||||
@@ -2356,7 +2356,7 @@ const feedbackBox = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -3497,7 +3497,7 @@ const measureTaskAccomplishment = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -3525,7 +3525,7 @@ const measureTaskAccomplishment = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[0],
|
||||
},
|
||||
},
|
||||
@@ -3553,7 +3553,7 @@ const measureTaskAccomplishment = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[2],
|
||||
},
|
||||
},
|
||||
@@ -3839,7 +3839,7 @@ const identifySignUpBarriers = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[0],
|
||||
},
|
||||
},
|
||||
@@ -3867,7 +3867,7 @@ const identifySignUpBarriers = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -3895,7 +3895,7 @@ const identifySignUpBarriers = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[2],
|
||||
},
|
||||
},
|
||||
@@ -3923,7 +3923,7 @@ const identifySignUpBarriers = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[3],
|
||||
},
|
||||
},
|
||||
@@ -3951,7 +3951,7 @@ const identifySignUpBarriers = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[4],
|
||||
},
|
||||
},
|
||||
@@ -4799,7 +4799,7 @@ const understandLowEngagement = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[0],
|
||||
},
|
||||
},
|
||||
@@ -4827,7 +4827,7 @@ const understandLowEngagement = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[1],
|
||||
},
|
||||
},
|
||||
@@ -4855,7 +4855,7 @@ const understandLowEngagement = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[2],
|
||||
},
|
||||
},
|
||||
@@ -4883,7 +4883,7 @@ const understandLowEngagement = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: reusableOptionIds[3],
|
||||
},
|
||||
},
|
||||
@@ -4911,7 +4911,7 @@ const understandLowEngagement = (): TTemplate => {
|
||||
},
|
||||
operator: "equals",
|
||||
rightOperand: {
|
||||
type: "choice",
|
||||
type: "static",
|
||||
value: "other",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -196,8 +196,6 @@ const getRightOperandValue = (
|
||||
return variableValue || "";
|
||||
case "hiddenField":
|
||||
return data[rightOperand.value];
|
||||
case "choice":
|
||||
return rightOperand.value;
|
||||
case "static":
|
||||
return rightOperand.value;
|
||||
default:
|
||||
|
||||
@@ -189,8 +189,6 @@ const getRightOperandValue = (
|
||||
switch (rightOperand.type) {
|
||||
case "question":
|
||||
return data[rightOperand.value];
|
||||
case "choice":
|
||||
return rightOperand.value;
|
||||
case "variable":
|
||||
const variables = localSurvey.variables || [];
|
||||
const variable = variables.find((v) => v.id === rightOperand.value);
|
||||
|
||||
@@ -354,10 +354,6 @@ export const ZRightOperand = z.discriminatedUnion("type", [
|
||||
type: z.literal("static"),
|
||||
value: z.union([z.string(), z.number(), z.array(z.string())]),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("choice"),
|
||||
value: z.string().cuid2(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("question"),
|
||||
value: z.string().cuid2(),
|
||||
@@ -391,14 +387,14 @@ export type TSingleCondition = z.infer<typeof ZSingleCondition>;
|
||||
|
||||
export interface TConditionGroup {
|
||||
id: string;
|
||||
connector: "and" | "or" | null;
|
||||
connector: "and" | "or";
|
||||
conditions: (TSingleCondition | TConditionGroup)[];
|
||||
}
|
||||
|
||||
const ZConditionGroup: z.ZodType<TConditionGroup> = z.lazy(() =>
|
||||
z.object({
|
||||
id: z.string().cuid2(),
|
||||
connector: z.enum(["and", "or"]).nullable(),
|
||||
connector: z.enum(["and", "or"]),
|
||||
conditions: z.array(z.union([ZSingleCondition, ZConditionGroup])),
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user