mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-06 09:00:18 -06:00
fix: Let CTA and consent question store nothing if dismissed (#2977)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -104,9 +104,12 @@ export const transformAnswer = (
|
||||
): string | number | string[] => {
|
||||
switch (question.type) {
|
||||
case TSurveyQuestionTypeEnum.OpenText:
|
||||
case TSurveyQuestionTypeEnum.MultipleChoiceSingle:
|
||||
case TSurveyQuestionTypeEnum.MultipleChoiceSingle: {
|
||||
return answer;
|
||||
}
|
||||
case TSurveyQuestionTypeEnum.Consent:
|
||||
case TSurveyQuestionTypeEnum.CTA: {
|
||||
if (answer === "dismissed") return "";
|
||||
return answer;
|
||||
}
|
||||
|
||||
@@ -143,6 +146,6 @@ export const transformAnswer = (
|
||||
}
|
||||
|
||||
default:
|
||||
return "dismissed";
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* eslint-disable no-console -- logging is allowed in migration scripts */
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { type TSurveyQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function runMigration(): Promise<void> {
|
||||
await prisma.$transaction(
|
||||
async (tx) => {
|
||||
const startTime = Date.now();
|
||||
console.log("Starting data migration...");
|
||||
|
||||
// Get all surveys with status not in draft and questions containing cta or consent
|
||||
const relevantSurveys = await tx.survey.findMany({
|
||||
where: {
|
||||
status: {
|
||||
notIn: ["draft"],
|
||||
},
|
||||
OR: [
|
||||
{
|
||||
questions: {
|
||||
array_contains: [{ type: "cta" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
questions: {
|
||||
array_contains: [{ type: "consent" }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
questions: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Process each survey
|
||||
const migrationPromises = relevantSurveys.map(async (survey) => {
|
||||
const ctaOrConsentQuestionIds = survey.questions
|
||||
.filter(
|
||||
(ques: TSurveyQuestion) =>
|
||||
ques.type === TSurveyQuestionTypeEnum.CTA || ques.type === TSurveyQuestionTypeEnum.Consent
|
||||
)
|
||||
.map((ques: TSurveyQuestion) => ques.id);
|
||||
|
||||
// Get all responses for this survey
|
||||
const responses = await prisma.response.findMany({
|
||||
where: {
|
||||
surveyId: survey.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
data: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Update each response
|
||||
return responses.map(async (response) => {
|
||||
const updatedData = { ...response.data };
|
||||
|
||||
ctaOrConsentQuestionIds.forEach((questionId: string) => {
|
||||
if (updatedData[questionId] && updatedData[questionId] === "dismissed") {
|
||||
updatedData[questionId] = "";
|
||||
}
|
||||
});
|
||||
|
||||
return prisma.response.update({
|
||||
where: { id: response.id },
|
||||
data: { data: updatedData },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const migrationPromisesFlat = migrationPromises.flat();
|
||||
await Promise.all(migrationPromisesFlat);
|
||||
|
||||
const endTime = Date.now();
|
||||
console.log(`Data migration completed. Total time: ${((endTime - startTime) / 1000).toString()}s`);
|
||||
},
|
||||
{
|
||||
timeout: 180000, // 3 minutes
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function handleError(error: unknown): void {
|
||||
console.error("An error occurred during migration:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function handleDisconnectError(): void {
|
||||
console.error("Failed to disconnect Prisma client");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
runMigration()
|
||||
.catch(handleError)
|
||||
.finally(() => {
|
||||
prisma.$disconnect().catch(handleDisconnectError);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -45,7 +45,8 @@
|
||||
"data-migration:multiple-endings": "ts-node ./data-migrations/20240801120500_thankYouCard_to_endings/data-migration.ts",
|
||||
"data-migration:simplified-email-verification": "ts-node ./data-migrations/20240726124100_replace_verifyEmail_with_isVerifyEmailEnabled/data-migration.ts",
|
||||
"data-migration:fix-logic-end-destination": "ts-node ./data-migrations/20240806120500_fix-logic-end-destination/data-migration.ts",
|
||||
"data-migration:v2.4": "pnpm data-migration:segments-cleanup && pnpm data-migration:multiple-endings && pnpm data-migration:simplified-email-verification && pnpm data-migration:fix-logic-end-destination"
|
||||
"data-migration:v2.4": "pnpm data-migration:segments-cleanup && pnpm data-migration:multiple-endings && pnpm data-migration:simplified-email-verification && pnpm data-migration:fix-logic-end-destination",
|
||||
"data-migration:remove-dismissed-value-inconsistency": "ts-node ./data-migrations/20240807120500_cta_consent_dismissed_inconsistency/data-migration.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.18.0",
|
||||
|
||||
@@ -205,7 +205,7 @@ export const buildWhereClause = (filterCriteria?: TResponseFilterCriteria) => {
|
||||
},
|
||||
});
|
||||
break;
|
||||
case "skipped": // need to handle dismissed case for CTA type question, that would hinder other ques(eg open text)
|
||||
case "skipped":
|
||||
data.push({
|
||||
OR: [
|
||||
{
|
||||
@@ -214,12 +214,6 @@ export const buildWhereClause = (filterCriteria?: TResponseFilterCriteria) => {
|
||||
equals: Prisma.DbNull,
|
||||
},
|
||||
},
|
||||
{
|
||||
data: {
|
||||
path: [key],
|
||||
equals: "dismissed",
|
||||
},
|
||||
},
|
||||
{
|
||||
data: {
|
||||
path: [key],
|
||||
|
||||
@@ -35,7 +35,7 @@ export const evaluateCondition = (logic: TSurveyLogic, responseValue: any): bool
|
||||
return responseValue === "clicked";
|
||||
case "submitted":
|
||||
if (typeof responseValue === "string") {
|
||||
return responseValue !== "dismissed" && responseValue !== "" && responseValue !== null;
|
||||
return responseValue !== "" && responseValue !== null;
|
||||
} else if (Array.isArray(responseValue)) {
|
||||
return responseValue.length > 0;
|
||||
} else if (typeof responseValue === "number") {
|
||||
@@ -46,8 +46,7 @@ export const evaluateCondition = (logic: TSurveyLogic, responseValue: any): bool
|
||||
return (
|
||||
(Array.isArray(responseValue) && responseValue.length === 0) ||
|
||||
responseValue === "" ||
|
||||
responseValue === null ||
|
||||
responseValue === "dismissed"
|
||||
responseValue === null
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -52,7 +52,7 @@ export const QuestionConditional = ({
|
||||
autoFocusEnabled,
|
||||
currentQuestionId,
|
||||
}: QuestionConditionalProps) => {
|
||||
if (!value && prefilledQuestionValue) {
|
||||
if (!value && (prefilledQuestionValue || prefilledQuestionValue === "")) {
|
||||
if (skipPrefilled) {
|
||||
onSubmit({ [question.id]: prefilledQuestionValue }, { [question.id]: 0 });
|
||||
} else {
|
||||
|
||||
@@ -78,8 +78,8 @@ export const CTAQuestion = ({
|
||||
onClick={() => {
|
||||
const updatedTtcObj = getUpdatedTtc(ttc, question.id, performance.now() - startTime);
|
||||
setTtc(updatedTtcObj);
|
||||
onSubmit({ [question.id]: "dismissed" }, updatedTtcObj);
|
||||
onChange({ [question.id]: "dismissed" });
|
||||
onSubmit({ [question.id]: "" }, updatedTtcObj);
|
||||
onChange({ [question.id]: "" });
|
||||
}}
|
||||
className="fb-text-heading focus:fb-ring-focus fb-mr-4 fb-flex fb-items-center fb-rounded-md fb-px-3 fb-py-3 fb-text-base fb-font-medium fb-leading-4 hover:fb-opacity-90 focus:fb-outline-none focus:fb-ring-2 focus:fb-ring-offset-2">
|
||||
{getLocalizedValue(question.dismissButtonLabel, languageCode) || "Skip"}
|
||||
|
||||
@@ -87,7 +87,7 @@ export const ConsentQuestion = ({
|
||||
if (e.target instanceof HTMLInputElement && e.target.checked) {
|
||||
onChange({ [question.id]: "accepted" });
|
||||
} else {
|
||||
onChange({ [question.id]: "dismissed" });
|
||||
onChange({ [question.id]: "" });
|
||||
}
|
||||
}}
|
||||
checked={value === "accepted"}
|
||||
|
||||
@@ -42,7 +42,7 @@ export const evaluateCondition = (
|
||||
return responseValue === "clicked";
|
||||
case "submitted":
|
||||
if (typeof responseValue === "string") {
|
||||
return responseValue !== "dismissed" && responseValue !== "" && responseValue !== null;
|
||||
return responseValue !== "" && responseValue !== null;
|
||||
} else if (Array.isArray(responseValue)) {
|
||||
return responseValue.length > 0;
|
||||
} else if (typeof responseValue === "number") {
|
||||
@@ -55,7 +55,6 @@ export const evaluateCondition = (
|
||||
responseValue === "" ||
|
||||
responseValue === null ||
|
||||
responseValue === undefined ||
|
||||
responseValue === "dismissed" ||
|
||||
(isObject && Object.entries(responseValue).length === 0)
|
||||
);
|
||||
case "uploaded":
|
||||
|
||||
Reference in New Issue
Block a user