diff --git a/app/javascript/elements/field_condition.js b/app/javascript/elements/field_condition.js index 609d9955..b99a8bb3 100644 --- a/app/javascript/elements/field_condition.js +++ b/app/javascript/elements/field_condition.js @@ -64,6 +64,22 @@ export default class extends HTMLElement { if (action === 'empty' || action === 'unchecked') return this.isEmpty(actual) if (action === 'not_empty' || action === 'checked') return !this.isEmpty(actual) + if (['equal', 'not_equal', 'greater_than', 'less_than'].includes(action) && this.sourceEl?.getAttribute('type') === 'number') { + if (this.isEmpty(actual) || this.isEmpty(expected)) return false + + const actualNumber = parseFloat(actual) + const expectedNumber = parseFloat(expected) + + if (Number.isNaN(actualNumber) || Number.isNaN(expectedNumber)) return false + + if (action === 'equal') return Math.abs(actualNumber - expectedNumber) < Number.EPSILON + if (action === 'not_equal') return Math.abs(actualNumber - expectedNumber) > Number.EPSILON + if (action === 'greater_than') return actualNumber > expectedNumber + if (action === 'less_than') return actualNumber < expectedNumber + + return false + } + if (action === 'equal') { const list = Array.isArray(actual) ? actual : [actual] return list.filter((v) => v !== null && v !== undefined).map(String).includes(String(expected)) diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index da939ce4..0f80a3f6 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -1261,7 +1261,7 @@ export default { checkFieldCondition (condition, cache = {}) { const field = this.fieldsUuidIndex[condition.field_uuid] - if (['not_empty', 'checked', 'equal', 'contains'].includes(condition.action) && field && !this.checkFieldConditions(field, cache)) { + if (['not_empty', 'checked', 'equal', 'contains', 'greater_than', 'less_than'].includes(condition.action) && field && !this.checkFieldConditions(field, cache)) { return false } @@ -1271,6 +1271,22 @@ export default { return isEmpty(this.values[condition.field_uuid] ?? defaultValue) } else if (['not_empty', 'checked'].includes(condition.action)) { return !isEmpty(this.values[condition.field_uuid] ?? defaultValue) + } else if (field?.type === 'number' && ['equal', 'not_equal', 'greater_than', 'less_than'].includes(condition.action)) { + const value = this.values[condition.field_uuid] ?? defaultValue + + if (isEmpty(value) || isEmpty(condition.value)) return false + + const actual = parseFloat(value) + const expected = parseFloat(condition.value) + + if (Number.isNaN(actual) || Number.isNaN(expected)) return false + + if (condition.action === 'equal') return Math.abs(actual - expected) < Number.EPSILON + if (condition.action === 'not_equal') return Math.abs(actual - expected) > Number.EPSILON + if (condition.action === 'greater_than') return actual > expected + if (condition.action === 'less_than') return actual < expected + + return false } else if (['equal', 'contains'].includes(condition.action) && field) { if (field.options) { const option = field.options.find((o) => o.uuid === condition.value) diff --git a/app/javascript/template_builder/conditions_modal.vue b/app/javascript/template_builder/conditions_modal.vue index 3c7abf29..d8219ff6 100644 --- a/app/javascript/template_builder/conditions_modal.vue +++ b/app/javascript/template_builder/conditions_modal.vue @@ -124,6 +124,16 @@ {{ option.value || `${t('option')} ${index + 1}` }} + (action) { action == 'equal' && field&.dig('type') == 'number' } + return false if value.blank? || condition['value'].blank? + + (value.to_f - condition['value'].to_f).abs < Float::EPSILON + when ->(action) { action == 'not_equal' && field&.dig('type') == 'number' } + return false if value.blank? || condition['value'].blank? + + (value.to_f - condition['value'].to_f).abs > Float::EPSILON + when 'greater_than' + return false if field.nil? || value.blank? || condition['value'].blank? + value.to_f > condition['value'].to_f + when 'less_than' + return false if field.nil? || value.blank? || condition['value'].blank? + + value.to_f < condition['value'].to_f + when 'equal', 'contains' return true unless field values = Array.wrap(value) @@ -340,8 +356,6 @@ module Submitters values.include?(option['value'].presence || "#{I18n.t('option')} #{field['options'].index(option) + 1}") when 'not_equal', 'does_not_contain' - field = fields_uuid_index[condition['field_uuid']] - return true unless field return false unless field['options'] @@ -356,6 +370,7 @@ module Submitters true end end + # rubocop:enable Metrics def replace_default_variables(value, attrs, submission, with_time: false) return value if value.in?([true, false]) || value.is_a?(Numeric) || value.is_a?(Array)