From cf0e1915c22ff795fd710aae8138405a7b72e0f8 Mon Sep 17 00:00:00 2001 From: brentguf Date: Wed, 8 Jan 2025 10:21:49 +0100 Subject: [PATCH 01/12] Put hooks above return for code readability --- .../components/inputs/IdeationInputs/index.tsx | 8 +++++--- .../components/inputs/ProposalsInputs/index.tsx | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) 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 ( <> Date: Wed, 8 Jan 2025 15:54:53 +0300 Subject: [PATCH 02/12] Show logic tab for multiselect --- .../FormBuilder/components/FormBuilderSettings/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx index 7503366e17c0..9c990dd96bc0 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx @@ -87,9 +87,12 @@ 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', + 'linear_scale', + 'select', + 'page', + ].includes(fieldType); return ( Date: Wed, 8 Jan 2025 15:55:18 +0300 Subject: [PATCH 03/12] Cleanup --- .../FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx | 2 ++ .../components/FormBuilderSettings/LogicSettings/index.tsx | 1 + 2 files changed, 3 insertions(+) diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx index f2f00bc28db7..b65a3d71c148 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/QuestionRuleInput.tsx @@ -77,6 +77,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); @@ -84,6 +85,7 @@ export const QuestionRuleInput = ({ } else { logic.rules = [newRule]; } + // Update rule variable const required = // TODO: Fix this the next time the file is edited. diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx index a76e63cbca08..4884f8c7d236 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/LogicSettings/index.tsx @@ -64,6 +64,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( From 73a9dc47be6282625eddf6f23c546c0cb78131a6 Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Wed, 8 Jan 2025 15:55:45 +0300 Subject: [PATCH 04/12] Save logic options for multiselect fields --- front/app/components/FormBuilder/edit/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/front/app/components/FormBuilder/edit/index.tsx b/front/app/components/FormBuilder/edit/index.tsx index 0819ad06f088..cc4261889c1e 100644 --- a/front/app/components/FormBuilder/edit/index.tsx +++ b/front/app/components/FormBuilder/edit/index.tsx @@ -193,7 +193,9 @@ const FormEdit = ({ ...(field.input_type === 'page' && { temp_id: field.temp_id, }), - ...(['linear_scale', 'select', 'page'].includes(field.input_type) + ...(['multiselect', 'linear_scale', 'select', 'page'].includes( + field.input_type + ) ? { logic: field.logic, } From 8b6817952fd5885391426f0f6008d470ea9a9c2c Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Wed, 8 Jan 2025 15:56:14 +0300 Subject: [PATCH 05/12] Show logic options for multi select on Form fields --- .../FormBuilder/components/FormFields/FormField/Logic/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx b/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx index c4a6a2b6d67d..dbbfb7b8109b 100644 --- a/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx +++ b/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx @@ -45,7 +45,7 @@ const Logic = ({ return ( - {field.input_type === 'select' && + {['select', 'multiselect'].includes(field.input_type) && field.options && field.options.map((option) => { const optionRule = getOptionRule(option, field); From a13d1e38021740fd2fbcf13f453013a6ad17631d Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Wed, 8 Jan 2025 15:56:47 +0300 Subject: [PATCH 06/12] Add multi select field to logic validations --- front/app/utils/yup/validateLogic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/app/utils/yup/validateLogic.ts b/front/app/utils/yup/validateLogic.ts index f21af4e8ecd3..2efc25af2094 100644 --- a/front/app/utils/yup/validateLogic.ts +++ b/front/app/utils/yup/validateLogic.ts @@ -54,7 +54,7 @@ const validateLogic = (message: string) => { logic: object(), }) .when('input_type', (input_type: string, schema) => { - if (['select', 'linear_scale'].includes(input_type)) { + if (['multiselect', 'select', 'linear_scale'].includes(input_type)) { return schema.test( 'rules reference prior pages', message, From 8c2d91ffaea933c51b8e2de4e7683f3ae10a7f1d Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Fri, 10 Jan 2025 11:19:04 +0300 Subject: [PATCH 07/12] Support multiselect input type in logic rules --- back/app/services/form_logic_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/back/app/services/form_logic_service.rb b/back/app/services/form_logic_service.rb index eff6d75c2cc9..ab74d7b5df40 100644 --- a/back/app/services/form_logic_service.rb +++ b/back/app/services/form_logic_service.rb @@ -186,7 +186,7 @@ def add_only_page_allowed_as_target_error(field, target_id) end def ui_schema_hide_rule_for(field, value) - if field.input_type == 'select' + if field.input_type == 'select' || field.input_type == 'multiselect' value = option_index[value].key end { @@ -255,7 +255,7 @@ def add_rules_for_field(field, index, next_page_id, rules_accu) # Then apply page-level logic if no question-level logic is present. if next_page_id case field.input_type - when 'select' + when 'select', 'multiselect' field.options.each do |option| value = option.id next if logic.key?(value) From 59c5f2c38459208ca4921a0366fb1fc6eb3019ba Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Fri, 10 Jan 2025 11:19:18 +0300 Subject: [PATCH 08/12] Add schema validation for single and array values in visibility conditions --- .../Components/Controls/visibilityUtils.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/front/app/components/Form/Components/Controls/visibilityUtils.ts b/front/app/components/Form/Components/Controls/visibilityUtils.ts index 1bb4a2250922..99a775bc755e 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,28 @@ 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)); + } + // For single values, validate directly + return ajv.validate(schema, value); +}; + const evaluateCondition = ( data: any, condition: Condition, @@ -93,8 +116,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)); - return ajv.validate(condition.schema, value); + return validateSchemaCondition(value, condition.schema, ajv); } else { // unknown condition return true; From b049c2fe7bd43edc0edad6e5dfb0282969496821 Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Fri, 10 Jan 2025 12:03:40 +0300 Subject: [PATCH 09/12] Add support for 'multiselect_image' input type in logic validation --- front/app/utils/yup/validateLogic.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/front/app/utils/yup/validateLogic.ts b/front/app/utils/yup/validateLogic.ts index 2efc25af2094..84296c5951bc 100644 --- a/front/app/utils/yup/validateLogic.ts +++ b/front/app/utils/yup/validateLogic.ts @@ -54,7 +54,11 @@ const validateLogic = (message: string) => { logic: object(), }) .when('input_type', (input_type: string, schema) => { - if (['multiselect', 'select', 'linear_scale'].includes(input_type)) { + if ( + ['multiselect', 'multiselect_image', 'select', 'linear_scale'].includes( + input_type + ) + ) { return schema.test( 'rules reference prior pages', message, From b643d3d397779ea43da736e1f39f1de98c8eb1b2 Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Fri, 10 Jan 2025 12:04:09 +0300 Subject: [PATCH 10/12] Add 'multiselect_image' input type support in form field handling --- front/app/components/FormBuilder/edit/index.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/front/app/components/FormBuilder/edit/index.tsx b/front/app/components/FormBuilder/edit/index.tsx index cc4261889c1e..8b6f300b666a 100644 --- a/front/app/components/FormBuilder/edit/index.tsx +++ b/front/app/components/FormBuilder/edit/index.tsx @@ -193,9 +193,13 @@ const FormEdit = ({ ...(field.input_type === 'page' && { temp_id: field.temp_id, }), - ...(['multiselect', 'linear_scale', 'select', 'page'].includes( - field.input_type - ) + ...([ + 'multiselect', + 'linear_scale', + 'select', + 'page', + 'multiselect_image', + ].includes(field.input_type) ? { logic: field.logic, } From cf80c392a54e3d857a59ff195393906b35410bce Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Fri, 10 Jan 2025 12:04:46 +0300 Subject: [PATCH 11/12] Add support for 'multiselect_image' input type in logic rendering --- .../components/FormFields/FormField/Logic/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx b/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx index 911039832039..4aee4d9a59e8 100644 --- a/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx +++ b/front/app/components/FormBuilder/components/FormFields/FormField/Logic/index.tsx @@ -57,7 +57,9 @@ const Logic = ({ return ( - {['select', 'multiselect'].includes(field.input_type) && + {['select', 'multiselect', 'multiselect_image'].includes( + field.input_type + ) && field.options && field.options.map((option) => { const optionRule = getOptionRule(option, field); From bd8385f8baae26e31973c755461a9f16890b804b Mon Sep 17 00:00:00 2001 From: EdwinKato Date: Fri, 10 Jan 2025 12:05:44 +0300 Subject: [PATCH 12/12] Show logic tab for image choice --- .../FormBuilder/components/FormBuilderSettings/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx b/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx index 9c990dd96bc0..aa14dc2617c6 100644 --- a/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx +++ b/front/app/components/FormBuilder/components/FormBuilderSettings/index.tsx @@ -89,6 +89,7 @@ const FormBuilderSettings = ({ const fieldType = watch(`customFields.${field.index}.input_type`); const showTabbedSettings = [ 'multiselect', + 'multiselect_image', 'linear_scale', 'select', 'page',