add condition_component
This commit is contained in:
parent
61839ef1ac
commit
e2a236b73d
6 changed files with 408 additions and 1 deletions
62
app/assets/stylesheets/conditions_component.scss
Normal file
62
app/assets/stylesheets/conditions_component.scss
Normal file
|
@ -0,0 +1,62 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
.conditionnel {
|
||||
|
||||
.condition-table {
|
||||
table-layout: fixed;
|
||||
|
||||
.far-left {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.target {
|
||||
width: 350px;
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.operator {
|
||||
width: 250px;
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.delete-column {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: $default-spacer;
|
||||
|
||||
}
|
||||
|
||||
td {
|
||||
padding: $default-spacer;
|
||||
|
||||
input,
|
||||
select {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input.alert,
|
||||
select.alert {
|
||||
border-color: $dark-red;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -93,7 +93,7 @@
|
|||
|
||||
.flex {
|
||||
&.section {
|
||||
padding: 10px 10px 0 10px;
|
||||
padding: $default-spacer $default-spacer 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
|
|
151
app/components/types_de_champ/conditions_component.rb
Normal file
151
app/components/types_de_champ/conditions_component.rb
Normal file
|
@ -0,0 +1,151 @@
|
|||
class TypesDeChamp::ConditionsComponent < ApplicationComponent
|
||||
include Logic
|
||||
|
||||
def initialize(tdc:, upper_tdcs:, procedure_id:)
|
||||
@tdc, @condition, @upper_tdcs = tdc, tdc.condition, upper_tdcs
|
||||
@procedure_id = procedure_id
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rows
|
||||
condition_per_row.map { |c| Logic.split_condition(c) }
|
||||
end
|
||||
|
||||
def condition_per_row
|
||||
if [And, Or].include?(@condition.class)
|
||||
@condition.operands
|
||||
else
|
||||
[@condition].compact
|
||||
end
|
||||
end
|
||||
|
||||
def logic_conditionnel_button
|
||||
if @condition.nil?
|
||||
submit_tag('cliquer pour activer', formaction: add_row_admin_procedure_condition_path(@procedure_id, @tdc.id))
|
||||
else
|
||||
submit_tag(
|
||||
'cliquer pour désactiver',
|
||||
formmethod: 'delete',
|
||||
formnovalidate: true,
|
||||
data: { confirm: "La logique conditionnelle appliquée à ce champ sera désactivé.\nVoulez-vous continuer ?" }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def far_left_tag(row_number)
|
||||
if row_number == 0
|
||||
'Afficher si'
|
||||
elsif row_number == 1
|
||||
select_tag(
|
||||
"#{input_prefix}[top_operator_name]",
|
||||
options_for_select([['Et', And.name], ['Ou', Or.name]], @condition.class.name)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def left_operand_tag(targeted_champ, row_index)
|
||||
select_tag(
|
||||
input_name_for('targeted_champ'),
|
||||
options_for_select(available_targets, targeted_champ.to_json),
|
||||
onchange: "this.form.action = this.form.action + '/change_targeted_champ?row_index=#{row_index}'",
|
||||
id: input_id_for('targeted_champ', row_index)
|
||||
)
|
||||
end
|
||||
|
||||
def available_targets
|
||||
targets = @upper_tdcs
|
||||
.filter { |tdc| ChampValue::MANAGED_TYPE_DE_CHAMP.values.include?(tdc.type_champ) }
|
||||
.map do |tdc|
|
||||
[tdc.libelle, champ_value(tdc.stable_id).to_json]
|
||||
end
|
||||
|
||||
if targets.present?
|
||||
targets.unshift(['Sélectionner', empty.to_json])
|
||||
end
|
||||
|
||||
targets
|
||||
end
|
||||
|
||||
def operator_tag(operator_name, targeted_champ, row_index)
|
||||
select_tag(
|
||||
input_name_for('operator_name'),
|
||||
options_for_select(available_operators(targeted_champ), operator_name),
|
||||
id: input_id_for('operator_name', row_index)
|
||||
)
|
||||
end
|
||||
|
||||
def available_operators(left)
|
||||
case left.type
|
||||
when ChampValue::CHAMP_VALUE_TYPE.fetch(:boolean)
|
||||
[
|
||||
['Est', Eq.name]
|
||||
]
|
||||
when ChampValue::CHAMP_VALUE_TYPE.fetch(:empty)
|
||||
[
|
||||
['Est', Eq.name]
|
||||
]
|
||||
when ChampValue::CHAMP_VALUE_TYPE.fetch(:enum)
|
||||
[
|
||||
['Est', Eq.name]
|
||||
]
|
||||
when ChampValue::CHAMP_VALUE_TYPE.fetch(:number)
|
||||
[Eq, LessThan, GreaterThan, LessThanEq, GreaterThanEq]
|
||||
.map(&:name)
|
||||
.map { |name| [t(name, scope: 'logic.operators'), name] }
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def right_operand_tag(left, right, row_index)
|
||||
case left.type
|
||||
when :boolean
|
||||
select_tag(
|
||||
input_name_for('value'),
|
||||
options_for_select([['Oui', constant(true).to_json], ['Non', constant(false).to_json]], right.to_json),
|
||||
id: input_id_for('value', row_index)
|
||||
)
|
||||
when :empty
|
||||
select_tag(
|
||||
input_name_for('value'),
|
||||
options_for_select([['Sélectionner', empty.to_json]]),
|
||||
id: input_id_for('value', row_index)
|
||||
)
|
||||
when :enum
|
||||
select_tag(
|
||||
input_name_for('value'),
|
||||
options_for_select(left.options, right.value),
|
||||
id: input_id_for('value', row_index)
|
||||
)
|
||||
when :number
|
||||
number_field_tag(input_name_for('value'), right.value, required: true, id: input_id_for('value', row_index))
|
||||
else
|
||||
number_field_tag(input_name_for('value'), '', id: input_id_for('value', row_index))
|
||||
end
|
||||
end
|
||||
|
||||
def add_condition_tag
|
||||
submit_tag('Ajouter une condition', formaction: add_row_condition_path(@tdc.id))
|
||||
end
|
||||
|
||||
def delete_condition_tag(row_index)
|
||||
submit_tag('X', formaction: delete_row_condition_path(@tdc.id, row_index: row_index))
|
||||
end
|
||||
|
||||
def render?
|
||||
@condition.present? || available_targets.any?
|
||||
end
|
||||
|
||||
def input_name_for(name)
|
||||
"#{input_prefix}[rows][][#{name}]"
|
||||
end
|
||||
|
||||
def input_id_for(name, row_index)
|
||||
"#{@tdc.id}-#{name}-#{row_index}"
|
||||
end
|
||||
|
||||
def input_prefix
|
||||
'type_de_champ[condition_form]'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
.flex.justify-start.section{ id: dom_id(@tdc, :conditions) }
|
||||
= form_with url: admin_procedure_condition_path(@procedure_id, @tdc), method: :patch, class: 'form width-100' do |f|
|
||||
.conditionnel.mt-2.width-100
|
||||
.flex
|
||||
%p.mr-2 Logique conditionnelle
|
||||
= logic_conditionnel_button
|
||||
|
||||
- if @condition.present?
|
||||
%table.condition-table.mt-2.width-100
|
||||
%thead
|
||||
%tr
|
||||
%th.far-left
|
||||
%th.target Champ Cible
|
||||
%th.operator Opérateur
|
||||
%th.value Valeur
|
||||
%th.delete-column
|
||||
%tbody
|
||||
- rows.each.with_index do |(targeted_champ, operator_name, value), row_index|
|
||||
%tr
|
||||
%td.far-left= far_left_tag(row_index)
|
||||
%td.target= left_operand_tag(targeted_champ, row_index)
|
||||
%td.operator= operator_tag(operator_name, targeted_champ, row_index)
|
||||
%td.value= right_operand_tag(targeted_champ, value, row_index)
|
||||
%td.delete-column= delete_condition_tag(row_index)
|
||||
|
||||
.flex.justify-end.mt-2= add_condition_tag
|
|
@ -1,3 +1,9 @@
|
|||
fr:
|
||||
logic:
|
||||
empty: un membre vide
|
||||
operators:
|
||||
'Logic::LessThan': Inférieur à
|
||||
'Logic::LessThanEq': Inférieur ou égal à
|
||||
'Logic::Eq': Égal à
|
||||
'Logic::GreaterThan': Supérieur à
|
||||
'Logic::GreaterThanEq': Supérieur ou égal à
|
||||
|
|
162
spec/components/types_de_champ/conditions_component_spec.rb
Normal file
162
spec/components/types_de_champ/conditions_component_spec.rb
Normal file
|
@ -0,0 +1,162 @@
|
|||
describe TypesDeChamp::ConditionsComponent, type: :component do
|
||||
include Logic
|
||||
|
||||
describe 'render' do
|
||||
let(:tdc) { create(:type_de_champ, condition: condition) }
|
||||
let(:condition) { nil }
|
||||
let(:upper_tdcs) { [] }
|
||||
|
||||
before { render_inline(described_class.new(tdc: tdc, upper_tdcs: upper_tdcs, procedure_id: 123)) }
|
||||
|
||||
context 'when there are no upper tdc' do
|
||||
it { expect(page).not_to have_text('Logique conditionnelle') }
|
||||
end
|
||||
|
||||
context 'when there are upper tdcs but not managed' do
|
||||
let(:upper_tdcs) { [build(:type_de_champ_address)] }
|
||||
|
||||
it { expect(page).not_to have_text('Logique conditionnelle') }
|
||||
end
|
||||
|
||||
context 'when there are upper tdc but no condition to display' do
|
||||
let(:upper_tdcs) { [build(:type_de_champ_integer_number)] }
|
||||
|
||||
it do
|
||||
expect(page).to have_text('Logique conditionnelle')
|
||||
expect(page).to have_button('cliquer pour activer')
|
||||
expect(page).not_to have_selector('table')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are upper tdc and a condition' do
|
||||
let(:upper_tdc) { create(:type_de_champ_number) }
|
||||
let(:upper_tdcs) { [upper_tdc] }
|
||||
|
||||
context 'and one condition' do
|
||||
let(:condition) { ds_eq(champ_value(upper_tdc.stable_id), constant(1)) }
|
||||
|
||||
it do
|
||||
expect(page).to have_button('cliquer pour désactiver')
|
||||
expect(page).to have_selector('table')
|
||||
expect(page).to have_selector('tbody > tr', count: 1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'focus one row' do
|
||||
context 'empty' do
|
||||
let(:condition) { empty_operator(empty, empty) }
|
||||
|
||||
it do
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][operator_name]', options: ['Est'])
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][value]', options: ['Sélectionner'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'number' do
|
||||
let(:condition) { empty_operator(constant(1), constant(0)) }
|
||||
|
||||
it do
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][operator_name]', with_options: ['Égal à'])
|
||||
expect(page).to have_selector('input[name="type_de_champ[condition_form][rows][][value]"][value=0]')
|
||||
end
|
||||
end
|
||||
|
||||
context 'boolean' do
|
||||
let(:condition) { empty_operator(constant(true), constant(true)) }
|
||||
|
||||
it do
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][operator_name]', with_options: ['Est'])
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][value]', options: ['Oui', 'Non'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'enum' do
|
||||
let(:drop_down) { create(:type_de_champ_drop_down_list) }
|
||||
let(:upper_tdcs) { [drop_down] }
|
||||
let(:condition) { empty_operator(champ_value(drop_down.stable_id), constant(true)) }
|
||||
|
||||
it do
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][operator_name]', with_options: ['Est'])
|
||||
expect(page).to have_select('type_de_champ[condition_form][rows][][value]', options: ['val1', 'val2', 'val3'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'and 2 conditions' do
|
||||
let(:condition) { ds_and([empty_operator(empty, empty), empty_operator(empty, empty)]) }
|
||||
|
||||
it do
|
||||
expect(page).to have_selector('tbody > tr', count: 2)
|
||||
expect(page).to have_select("type_de_champ_condition_form_top_operator_name", selected: "Et", options: ['Et', 'Ou'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are 3 conditions' do
|
||||
let(:upper_tdc) { create(:type_de_champ_number) }
|
||||
let(:upper_tdcs) { [upper_tdc] }
|
||||
|
||||
let(:condition) do
|
||||
ds_or([
|
||||
ds_eq(champ_value(upper_tdc.stable_id), constant(1)),
|
||||
ds_eq(champ_value(upper_tdc.stable_id), empty),
|
||||
greater_than(champ_value(upper_tdc.stable_id), constant(3))
|
||||
])
|
||||
end
|
||||
|
||||
it do
|
||||
expect(page).to have_selector('tbody > tr', count: 3)
|
||||
expect(page).to have_select("type_de_champ_condition_form_top_operator_name", selected: "Ou", options: ['Et', 'Ou'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.rows' do
|
||||
let(:tdc) { build(:type_de_champ, condition: condition) }
|
||||
let(:condition) { nil }
|
||||
|
||||
subject { described_class.new(tdc: tdc, upper_tdcs: [], procedure_id: 123).send(:rows) }
|
||||
|
||||
context 'when there is one condition' do
|
||||
let(:condition) { ds_eq(empty, constant(1)) }
|
||||
|
||||
it { is_expected.to eq([[empty, Logic::Eq.name, constant(1)]]) }
|
||||
end
|
||||
|
||||
context 'when there are 2 conditions' do
|
||||
let(:condition) { ds_and([ds_eq(empty, constant(1)), ds_eq(empty, empty)]) }
|
||||
|
||||
let(:expected) do
|
||||
[
|
||||
[empty, Logic::Eq.name, constant(1)],
|
||||
[empty, Logic::Eq.name, empty]
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to eq(expected) }
|
||||
end
|
||||
|
||||
context 'when there are 3 conditions' do
|
||||
let(:upper_tdc) { create(:type_de_champ_number) }
|
||||
let(:upper_tdcs) { [upper_tdc] }
|
||||
|
||||
let(:condition) do
|
||||
ds_or([
|
||||
ds_eq(champ_value(upper_tdc.stable_id), constant(1)),
|
||||
ds_eq(champ_value(upper_tdc.stable_id), empty),
|
||||
greater_than(champ_value(upper_tdc.stable_id), constant(3))
|
||||
])
|
||||
end
|
||||
|
||||
let(:expected) do
|
||||
[
|
||||
[champ_value(upper_tdc.stable_id), Logic::Eq.name, constant(1)],
|
||||
[champ_value(upper_tdc.stable_id), Logic::Eq.name, empty],
|
||||
[champ_value(upper_tdc.stable_id), Logic::GreaterThan.name, constant(3)]
|
||||
]
|
||||
end
|
||||
|
||||
it { is_expected.to eq(expected) }
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue