diff --git a/front/app/components/Form/Components/Controls/visibilityUtils.ts b/front/app/components/Form/Components/Controls/visibilityUtils.ts index 06ede5b184e6..dc63e1f53687 100644 --- a/front/app/components/Form/Components/Controls/visibilityUtils.ts +++ b/front/app/components/Form/Components/Controls/visibilityUtils.ts @@ -15,6 +15,7 @@ import { Scopable, composeWithUi, resolveData, + JsonSchema, } from '@jsonforms/core'; import Ajv from 'ajv'; import { has } from 'lodash-es'; @@ -73,6 +74,30 @@ const getConditionScope = (condition: Scopable, path: string): string => { return composeWithUi(condition, path); }; +/** + * Validates a schema condition. + * Handles both single values and arrays of values. + * + * @param value - The resolved value to validate (single or array). + * @param schema - The JSON schema to validate against. + * @param ajv - The AJV instance for validation. + * @returns {boolean} - True if the value or any array item matches the schema. + */ +const validateSchemaCondition = ( + value: any, + schema: JsonSchema, + ajv: Ajv +): boolean => { + if (Array.isArray(value)) { + // For arrays, check if at least one element passes validation. Important for multi-select and image-select + return value.some((val) => ajv.validate(schema, val)); + } else if (schema.enum?.includes('no_answer') && value === undefined) { + return true; + } + + return ajv.validate(schema, value); +}; + const evaluateCondition = ( data: any, condition: Condition, @@ -93,11 +118,9 @@ const evaluateCondition = ( const value = resolveData(data, getConditionScope(condition, path)); return value === condition.expectedValue; } else if (isSchemaCondition(condition)) { + // Schema condition: validates the resolved value(s) against the schema const value = resolveData(data, getConditionScope(condition, path)); - if (condition.schema.enum?.includes('no_answer') && value === undefined) { - return true; - } - return ajv.validate(condition.schema, value); + return validateSchemaCondition(value, condition.schema, ajv); } else { // unknown condition return true; diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx index 1bd16c829876..a948f29296ef 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx @@ -75,6 +75,7 @@ export const QuestionRuleInput = ({ goto_page_id: page.value.toString(), }; setRuleIsInvalid(!isRuleValid(newRule, fieldId, fields)); + if (logic.rules) { const newRulesArray = logic.rules; newRulesArray.push(newRule); @@ -82,7 +83,6 @@ export const QuestionRuleInput = ({ } else { logic.rules = [newRule]; } - setValue(name, { ...field, logic }, { shouldDirty: true }); trigger(); } diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx index 344f6651d7ff..ccc189575cc8 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx @@ -65,6 +65,7 @@ export const LogicSettings = ({ label: option.title_multiloc[locale]?.toString(), })) : undefined; + // For Linear Scale Field if (field.input_type === 'linear_scale') { const linearScaleOptionArray = Array.from( diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx index 7503366e17c0..aa14dc2617c6 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx @@ -87,9 +87,13 @@ const FormBuilderSettings = ({ const tabNotActiveBorder = `1px solid ${colors.grey400}`; const tabActiveBorder = `4px solid ${colors.primary}`; const fieldType = watch(`customFields.${field.index}.input_type`); - const showTabbedSettings = ['linear_scale', 'select', 'page'].includes( - fieldType - ); + const showTabbedSettings = [ + 'multiselect', + 'multiselect_image', + 'linear_scale', + 'select', + 'page', + ].includes(fieldType); return ( - {field.input_type === 'select' && + {['select', 'multiselect', 'multiselect_image'].includes( + field.input_type + ) && field.options && field.options.map((option) => { const optionRule = getOptionRule(option, field); diff --git a/front/app/components/FormBuilder/edit/index.tsx b/front/app/components/FormBuilder/edit/index.tsx index 0819ad06f088..8b6f300b666a 100644 --- a/front/app/components/FormBuilder/edit/index.tsx +++ b/front/app/components/FormBuilder/edit/index.tsx @@ -193,7 +193,13 @@ const FormEdit = ({ ...(field.input_type === 'page' && { temp_id: field.temp_id, }), - ...(['linear_scale', 'select', 'page'].includes(field.input_type) + ...([ + 'multiselect', + 'linear_scale', + 'select', + 'page', + 'multiselect_image', + ].includes(field.input_type) ? { logic: field.logic, } diff --git a/front/app/containers/Admin/projects/project/phaseSetup/components/PhaseParticipationConfig/components/inputs/IdeationInputs/index.tsx b/front/app/containers/Admin/projects/project/phaseSetup/components/PhaseParticipationConfig/components/inputs/IdeationInputs/index.tsx index de04498f6cd0..d042c0648c32 100644 --- a/front/app/containers/Admin/projects/project/phaseSetup/components/PhaseParticipationConfig/components/inputs/IdeationInputs/index.tsx +++ b/front/app/containers/Admin/projects/project/phaseSetup/components/PhaseParticipationConfig/components/inputs/IdeationInputs/index.tsx @@ -93,6 +93,10 @@ const IdeationInputs = ({ prescreening_enabled, togglePrescreeningEnabled, }: Props) => { + const prescreeningIdeationEnabled = useFeatureFlag({ + name: 'prescreening_ideation', + }); + return ( <> { + const prescreeningFeatureAllowed = useFeatureFlag({ + name: 'prescreening', + onlyCheckAllowed: true, + }); + return ( <> { logic: object(), }) .when('input_type', (input_type: string, schema) => { - if (['select', 'linear_scale'].includes(input_type)) { + if ( + ['multiselect', 'multiselect_image', 'select', 'linear_scale'].includes( + input_type + ) + ) { return schema.test( 'rules reference prior pages', message,