From bed78716f0463bda7c9dfdc6db79f810b5e431b1 Mon Sep 17 00:00:00 2001 From: Johannes <72809645+jobenjada@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:36:09 -0800 Subject: [PATCH] fix: add validation for variable name conflicts with hidden fields (#7148) --- apps/web/i18n.lock | 1 + apps/web/locales/de-DE.json | 1 + apps/web/locales/en-US.json | 1 + apps/web/locales/es-ES.json | 1 + apps/web/locales/fr-FR.json | 1 + apps/web/locales/ja-JP.json | 1 + apps/web/locales/nl-NL.json | 1 + apps/web/locales/pt-BR.json | 1 + apps/web/locales/pt-PT.json | 1 + apps/web/locales/ro-RO.json | 1 + apps/web/locales/ru-RU.json | 1 + apps/web/locales/sv-SE.json | 1 + apps/web/locales/zh-Hans-CN.json | 1 + apps/web/locales/zh-Hant-TW.json | 1 + .../survey/editor/components/hidden-fields-card.tsx | 4 +++- .../editor/components/survey-variables-card-item.tsx | 4 ++++ packages/types/surveys/validation.ts | 12 +++++++++--- 17 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/web/i18n.lock b/apps/web/i18n.lock index 0e7bfb3033..065f61aef4 100644 --- a/apps/web/i18n.lock +++ b/apps/web/i18n.lock @@ -1567,6 +1567,7 @@ checksums: environments/surveys/edit/validation_rules_description: a0a7cee05e18efd462148698e3a93399 environments/surveys/edit/variable_is_used_in_logic_of_question_please_remove_it_from_logic_first: bd9d9c7cf0be671c4e8cf67e2ae6659e environments/surveys/edit/variable_is_used_in_quota_please_remove_it_from_quota_first: 0d36e5b2713f5450fe346e0af0aaa29c + environments/surveys/edit/variable_name_conflicts_with_hidden_field: fe2f6a711d5b663790bdd5780ad77bf2 environments/surveys/edit/variable_name_is_already_taken_please_choose_another: 6da42fe8733c6379158bce9a176f76d7 environments/surveys/edit/variable_name_must_start_with_a_letter: f7abbdecf1ba7b822ccabb16981ebcb5 environments/surveys/edit/variable_used_in_recall: 1c9c354a1233408cc42922eefaa8ce23 diff --git a/apps/web/locales/de-DE.json b/apps/web/locales/de-DE.json index c249091546..325da25f1c 100644 --- a/apps/web/locales/de-DE.json +++ b/apps/web/locales/de-DE.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Nur Antworten akzeptieren, die die folgenden Kriterien erfüllen", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne es zuerst aus der Logik.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variable \"{variableName}\" wird in der \"{quotaName}\" Quote verwendet", + "variable_name_conflicts_with_hidden_field": "Der Variablenname steht im Konflikt mit einer vorhandenen Hidden-Field-ID.", "variable_name_is_already_taken_please_choose_another": "Variablenname ist bereits vergeben, bitte wähle einen anderen.", "variable_name_must_start_with_a_letter": "Variablenname muss mit einem Buchstaben beginnen.", "variable_used_in_recall": "Variable \"{variable}\" wird in Frage {questionIndex} abgerufen.", diff --git a/apps/web/locales/en-US.json b/apps/web/locales/en-US.json index 829518fc5e..e2a5b90b9a 100644 --- a/apps/web/locales/en-US.json +++ b/apps/web/locales/en-US.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Only accept responses that meet the following criteria", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} is used in logic of question {questionIndex}. Please remove it from logic first.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variable \"{variableName}\" is being used in \"{quotaName}\" quota", + "variable_name_conflicts_with_hidden_field": "Variable name conflicts with an existing hidden field ID.", "variable_name_is_already_taken_please_choose_another": "Variable name is already taken, please choose another.", "variable_name_must_start_with_a_letter": "Variable name must start with a letter.", "variable_used_in_recall": "Variable \"{variable}\" is being recalled in question {questionIndex}.", diff --git a/apps/web/locales/es-ES.json b/apps/web/locales/es-ES.json index 5a979e7c44..02549eb702 100644 --- a/apps/web/locales/es-ES.json +++ b/apps/web/locales/es-ES.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Solo aceptar respuestas que cumplan los siguientes criterios", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} se usa en la lógica de la pregunta {questionIndex}. Por favor, elimínala primero de la lógica.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "La variable \"{variableName}\" se está utilizando en la cuota \"{quotaName}\"", + "variable_name_conflicts_with_hidden_field": "El nombre de la variable entra en conflicto con un ID de campo oculto existente.", "variable_name_is_already_taken_please_choose_another": "El nombre de la variable ya está en uso, por favor elige otro.", "variable_name_must_start_with_a_letter": "El nombre de la variable debe comenzar con una letra.", "variable_used_in_recall": "La variable \"{variable}\" se está recuperando en la pregunta {questionIndex}.", diff --git a/apps/web/locales/fr-FR.json b/apps/web/locales/fr-FR.json index 58405d3181..2fa8d162db 100644 --- a/apps/web/locales/fr-FR.json +++ b/apps/web/locales/fr-FR.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Accepter uniquement les réponses qui répondent aux critères suivants", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} est utilisé dans la logique de la question {questionIndex}. Veuillez d'abord le supprimer de la logique.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "La variable \"{variableName}\" est utilisée dans le quota \"{quotaName}\"", + "variable_name_conflicts_with_hidden_field": "Le nom de la variable est en conflit avec un ID de champ masqué existant.", "variable_name_is_already_taken_please_choose_another": "Le nom de la variable est déjà pris, veuillez en choisir un autre.", "variable_name_must_start_with_a_letter": "Le nom de la variable doit commencer par une lettre.", "variable_used_in_recall": "La variable \"{variable}\" est rappelée dans la question {questionIndex}.", diff --git a/apps/web/locales/ja-JP.json b/apps/web/locales/ja-JP.json index e50c36592e..ab4f5aabb1 100644 --- a/apps/web/locales/ja-JP.json +++ b/apps/web/locales/ja-JP.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "次の条件を満たす回答のみを受け付ける", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。", "variable_is_used_in_quota_please_remove_it_from_quota_first": "変数 \"{variableName}\" は \"{quotaName}\" クォータ で使用されています", + "variable_name_conflicts_with_hidden_field": "変数名が既存の非表示フィールドIDと競合しています。", "variable_name_is_already_taken_please_choose_another": "変数名はすでに使用されています。別の名前を選択してください。", "variable_name_must_start_with_a_letter": "変数名はアルファベットで始まらなければなりません。", "variable_used_in_recall": "変数 \"{variable}\" が 質問 {questionIndex} で 呼び出され て います 。", diff --git a/apps/web/locales/nl-NL.json b/apps/web/locales/nl-NL.json index 7496190299..a1e7de6299 100644 --- a/apps/web/locales/nl-NL.json +++ b/apps/web/locales/nl-NL.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Accepteer alleen antwoorden die voldoen aan de volgende criteria", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabele \"{variableName}\" wordt gebruikt in het \"{quotaName}\" quotum", + "variable_name_conflicts_with_hidden_field": "Variabelenaam conflicteert met een bestaande verborgen veld-ID.", "variable_name_is_already_taken_please_choose_another": "Variabelenaam is al in gebruik, kies een andere.", "variable_name_must_start_with_a_letter": "Variabelenaam moet beginnen met een letter.", "variable_used_in_recall": "Variabele \"{variable}\" wordt opgeroepen in vraag {questionIndex}.", diff --git a/apps/web/locales/pt-BR.json b/apps/web/locales/pt-BR.json index 4443a8d0ee..1044b084ce 100644 --- a/apps/web/locales/pt-BR.json +++ b/apps/web/locales/pt-BR.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Aceitar apenas respostas que atendam aos seguintes critérios", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} está sendo usado na lógica da pergunta {questionIndex}. Por favor, remova-o da lógica primeiro.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variável \"{variableName}\" está sendo usada na cota \"{quotaName}\"", + "variable_name_conflicts_with_hidden_field": "O nome da variável está em conflito com um ID de campo oculto existente.", "variable_name_is_already_taken_please_choose_another": "O nome da variável já está em uso, por favor escolha outro.", "variable_name_must_start_with_a_letter": "O nome da variável deve começar com uma letra.", "variable_used_in_recall": "Variável \"{variable}\" está sendo recordada na pergunta {questionIndex}.", diff --git a/apps/web/locales/pt-PT.json b/apps/web/locales/pt-PT.json index 2a8590c528..9360da23cf 100644 --- a/apps/web/locales/pt-PT.json +++ b/apps/web/locales/pt-PT.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Aceitar apenas respostas que cumpram os seguintes critérios", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variável \"{variableName}\" está a ser utilizada na quota \"{quotaName}\"", + "variable_name_conflicts_with_hidden_field": "O nome da variável está em conflito com um ID de campo oculto existente.", "variable_name_is_already_taken_please_choose_another": "O nome da variável já está em uso, por favor escolha outro.", "variable_name_must_start_with_a_letter": "O nome da variável deve começar com uma letra.", "variable_used_in_recall": "Variável \"{variable}\" está a ser recordada na pergunta {questionIndex}.", diff --git a/apps/web/locales/ro-RO.json b/apps/web/locales/ro-RO.json index ac911ca36a..fa668b6b07 100644 --- a/apps/web/locales/ro-RO.json +++ b/apps/web/locales/ro-RO.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Acceptă doar răspunsurile care îndeplinesc următoarele criterii", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} este folosit în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabila \"{variableName}\" este folosită în cota \"{quotaName}\"", + "variable_name_conflicts_with_hidden_field": "Numele variabilei intră în conflict cu un ID de câmp ascuns existent.", "variable_name_is_already_taken_please_choose_another": "Numele variabilei este deja utilizat, vă rugăm să alegeți altul.", "variable_name_must_start_with_a_letter": "Numele variabilei trebuie să înceapă cu o literă.", "variable_used_in_recall": "Variabila \"{variable}\" este reamintită în întrebarea {questionIndex}.", diff --git a/apps/web/locales/ru-RU.json b/apps/web/locales/ru-RU.json index 96662c2c8f..a89d85c85f 100644 --- a/apps/web/locales/ru-RU.json +++ b/apps/web/locales/ru-RU.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Принимать только ответы, соответствующие следующим критериям", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите его из логики.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Переменная «{variableName}» используется в квоте «{quotaName}»", + "variable_name_conflicts_with_hidden_field": "Имя переменной конфликтует с существующим ID скрытого поля.", "variable_name_is_already_taken_please_choose_another": "Это имя переменной уже занято, выберите другое.", "variable_name_must_start_with_a_letter": "Имя переменной должно начинаться с буквы.", "variable_used_in_recall": "Переменная «{variable}» используется в вопросе {questionIndex}.", diff --git a/apps/web/locales/sv-SE.json b/apps/web/locales/sv-SE.json index e47ae8881e..bdd4896150 100644 --- a/apps/web/locales/sv-SE.json +++ b/apps/web/locales/sv-SE.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "Acceptera endast svar som uppfyller följande kriterier", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.", "variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabel \"{variableName}\" används i kvoten \"{quotaName}\"", + "variable_name_conflicts_with_hidden_field": "Variabelnamnet krockar med ett befintligt dolt fält-ID.", "variable_name_is_already_taken_please_choose_another": "Variabelnamnet är redan taget, vänligen välj ett annat.", "variable_name_must_start_with_a_letter": "Variabelnamnet måste börja med en bokstav.", "variable_used_in_recall": "Variabel \"{variable}\" återkallas i fråga {questionIndex}.", diff --git a/apps/web/locales/zh-Hans-CN.json b/apps/web/locales/zh-Hans-CN.json index dd8babaa7a..be23deaca9 100644 --- a/apps/web/locales/zh-Hans-CN.json +++ b/apps/web/locales/zh-Hans-CN.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "仅接受符合以下条件的回复", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "\"{variable} 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"", "variable_is_used_in_quota_please_remove_it_from_quota_first": "变量 \"{variableName}\" 正在 被 \"{quotaName}\" 配额 使用", + "variable_name_conflicts_with_hidden_field": "变量名与已有的隐藏字段 ID 冲突。", "variable_name_is_already_taken_please_choose_another": "变量名已被占用,请选择其他。", "variable_name_must_start_with_a_letter": "变量名 必须 以字母开头。", "variable_used_in_recall": "变量 \"{variable}\" 正在召回于问题 {questionIndex}。", diff --git a/apps/web/locales/zh-Hant-TW.json b/apps/web/locales/zh-Hant-TW.json index 385466c4e8..2afb27cb57 100644 --- a/apps/web/locales/zh-Hant-TW.json +++ b/apps/web/locales/zh-Hant-TW.json @@ -1642,6 +1642,7 @@ "validation_rules_description": "僅接受符合下列條件的回應", "variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "'{'variable'}' 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。", "variable_is_used_in_quota_please_remove_it_from_quota_first": "變數 \"{variableName}\" 正被使用於 \"{quotaName}\" 配額中", + "variable_name_conflicts_with_hidden_field": "變數名稱與現有的隱藏欄位 ID 衝突。", "variable_name_is_already_taken_please_choose_another": "已使用此變數名稱,請選擇另一個名稱。", "variable_name_must_start_with_a_letter": "變數名稱必須以字母開頭。", "variable_used_in_recall": "變數 \"{variable}\" 於問題 {questionIndex} 中被召回。", diff --git a/apps/web/modules/survey/editor/components/hidden-fields-card.tsx b/apps/web/modules/survey/editor/components/hidden-fields-card.tsx index 94b92970fb..c5d30c42df 100644 --- a/apps/web/modules/survey/editor/components/hidden-fields-card.tsx +++ b/apps/web/modules/survey/editor/components/hidden-fields-card.tsx @@ -203,12 +203,14 @@ export const HiddenFieldsCard = ({ const existingElementIds = elements.map((element) => element.id); const existingEndingCardIds = localSurvey.endings.map((ending) => ending.id); const existingHiddenFieldIds = localSurvey.hiddenFields.fieldIds ?? []; + const existingVariableNames = localSurvey.variables.map((v) => v.name); const validateIdError = validateId( "Hidden field", hiddenField, existingElementIds, existingEndingCardIds, - existingHiddenFieldIds + existingHiddenFieldIds, + existingVariableNames ); if (validateIdError) { diff --git a/apps/web/modules/survey/editor/components/survey-variables-card-item.tsx b/apps/web/modules/survey/editor/components/survey-variables-card-item.tsx index b855f94a03..498b312006 100644 --- a/apps/web/modules/survey/editor/components/survey-variables-card-item.tsx +++ b/apps/web/modules/survey/editor/components/survey-variables-card-item.tsx @@ -186,6 +186,10 @@ export const SurveyVariablesCardItem = ({ if (!/^[a-z]/.test(value)) { return t("environments.surveys.edit.variable_name_must_start_with_a_letter"); } + const hiddenFieldIds = localSurvey.hiddenFields?.fieldIds ?? []; + if (hiddenFieldIds.some((id) => id.toLowerCase() === value.toLowerCase())) { + return t("environments.surveys.edit.variable_name_conflicts_with_hidden_field"); + } }, }} render={({ field }) => ( diff --git a/packages/types/surveys/validation.ts b/packages/types/surveys/validation.ts index dc3a3ab39b..a3e8e423e1 100644 --- a/packages/types/surveys/validation.ts +++ b/packages/types/surveys/validation.ts @@ -304,16 +304,22 @@ export const validateId = ( field: string, existingElementIds: string[], existingEndingCardIds: string[], - existingHiddenFieldIds: string[] + existingHiddenFieldIds: string[], + existingVariableNames: string[] = [] ): string | null => { if (field.trim() === "") { return `Please enter a ${type} Id.`; } - const combinedIds = [...existingElementIds, ...existingHiddenFieldIds, ...existingEndingCardIds]; + const combinedIds = [ + ...existingElementIds, + ...existingHiddenFieldIds, + ...existingEndingCardIds, + ...existingVariableNames, + ]; if (combinedIds.findIndex((id) => id.toLowerCase() === field.toLowerCase()) !== -1) { - return `${type} ID already exists in questions or hidden fields`; + return `${type} ID already exists in questions, hidden fields, or variables`; } if (FORBIDDEN_IDS.includes(field)) {