diff --git a/app/components/dossiers/invalid_eligibilite_rules_component.rb b/app/components/dossiers/invalid_eligibilite_rules_component.rb new file mode 100644 index 00000000000..c5d02b3b90f --- /dev/null +++ b/app/components/dossiers/invalid_eligibilite_rules_component.rb @@ -0,0 +1,24 @@ +class Dossiers::InvalidEligibiliteRulesComponent < ApplicationComponent + delegate :can_passer_en_construction?, to: :@dossier + + def initialize(dossier:) + @dossier = dossier + @revision = dossier.revision + end + + def render? + eligibilite_rules_computable? && !can_passer_en_construction? + end + + def error_message + @dossier.revision.eligibilite_message + end + + private + + def eligibilite_rules_computable? + @revision.eligibilite_enabled && + @revision.eligibilite_message.present? && + @revision&.eligibilite_rules&.computable?(@dossier.champs) + end +end diff --git a/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.en.yml b/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.en.yml new file mode 100644 index 00000000000..1a377763c1b --- /dev/null +++ b/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.en.yml @@ -0,0 +1,6 @@ +fr: + modal: + title: "Your file does not match submission criteria" + close: "Close" + close_alt: "Close this modal" + body: "The procedure « %{procedure_libelle} » have submission criteria, unfortunately your file does not match them. You can not submit your file" \ No newline at end of file diff --git a/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.fr.yml b/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.fr.yml new file mode 100644 index 00000000000..d191f03d4db --- /dev/null +++ b/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.fr.yml @@ -0,0 +1,5 @@ +fr: + modal: + title: "Vous ne pouvez pas déposer votre dossier" + close: "Fermer" + close_alt: "Fermer la fenêtre modale" \ No newline at end of file diff --git a/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.html.haml b/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.html.haml new file mode 100644 index 00000000000..b4c0a57f810 --- /dev/null +++ b/app/components/dossiers/invalid_eligibilite_rules_component/invalid_eligibilite_rules_component.html.haml @@ -0,0 +1,16 @@ +%div{ id: dom_id(@dossier, :eligibilite_rules_broken), data: { controller: 'invalid-eligibilite-rules', turbo_force: :server } } + %button.fr-sr-only{ aria: {controls: 'modal-eligibilite-rules-dialog' }, data: {'fr-opened': "false" } } + show modal + + %dialog.fr-modal{ "aria-labelledby" => "fr-modal-title-modal-1", role: "dialog", id: 'modal-eligibilite-rules-dialog', data: { 'invalid-eligibilite-rules-target' => 'dialog' } } + .fr-container.fr-container--fluid.fr-container-md + .fr-grid-row.fr-grid-row--center + .fr-col-12.fr-col-md-8.fr-col-lg-6 + .fr-modal__body + .fr-modal__header + %button.fr-btn--close.fr-btn{ aria: { controls: 'modal-eligibilite-rules-dialog' }, title: t('.modal.close_alt') }= t('.modal.close') + .fr-modal__content + %h1#fr-modal-title-modal-1.fr-modal__title + %span.fr-icon-arrow-right-line.fr-icon--lg> + = t('.modal.title') + %p= error_message diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 921a06013d5..7464edca287 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -301,8 +301,12 @@ def submit_en_construction def update @dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier @dossier = dossier_with_champs(pj_template: false) + @eligibilite_rules_was_computable = dossier.revision&.eligibilite_rules&.computable?(dossier.champs) + @can_passer_en_construction_was = @dossier.can_passer_en_construction? @errors = update_dossier_and_compute_errors - + @eligibilite_rules_is_computable = dossier.revision&.eligibilite_rules&.computable?(dossier.champs) + @can_passer_en_construction_is = @dossier.can_passer_en_construction? + @eligibilite_rules_computable_changed = !@eligibilite_rules_was_computable && @eligibilite_rules_is_computable respond_to do |format| format.turbo_stream do @to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_attributes_params, dossier.champs.filter(&:public?)) diff --git a/app/javascript/controllers/invalid_eligibilite_rules_controller.ts b/app/javascript/controllers/invalid_eligibilite_rules_controller.ts new file mode 100644 index 00000000000..242811896d2 --- /dev/null +++ b/app/javascript/controllers/invalid_eligibilite_rules_controller.ts @@ -0,0 +1,19 @@ +import { ApplicationController } from './application_controller'; +declare interface modal { + disclose: () => void; +} +declare interface dsfr { + modal: modal; +} +declare const window: Window & + typeof globalThis & { dsfr: (elem: HTMLElement) => dsfr }; + +export class InvalidEligibiliteRulesController extends ApplicationController { + static targets = ['dialog']; + + declare dialogTarget: HTMLElement; + + connect() { + setTimeout(() => window.dsfr(this.dialogTarget).modal.disclose(), 100); + } +} diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index d5fff32626e..534a73f07dd 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -25,4 +25,6 @@ = render Dossiers::PendingCorrectionCheckboxComponent.new(dossier: dossier) + = render Dossiers::InvalidEligibiliteRulesComponent.new(dossier: dossier) + = render Dossiers::EditFooterComponent.new(dossier: dossier_for_editing, annotation: false) diff --git a/app/views/users/dossiers/update.turbo_stream.haml b/app/views/users/dossiers/update.turbo_stream.haml index 91a898ab0e4..1f5e5cdb9c5 100644 --- a/app/views/users/dossiers/update.turbo_stream.haml +++ b/app/views/users/dossiers/update.turbo_stream.haml @@ -1 +1,8 @@ = render partial: 'shared/dossiers/update_champs', locals: { to_show: @to_show, to_hide: @to_hide, to_update: @to_update, dossier: @dossier } + +- if !params.key?(:validate) + - if @eligibilite_rules_is_computable + = turbo_stream.remove(dom_id(@dossier, :eligibilite_rules_broken)) + + - if (@eligibilite_rules_computable_changed && !@can_passer_en_construction_is) || (@can_passer_en_construction_was && !@can_passer_en_construction_is) + = turbo_stream.append('contenu', render(Dossiers::InvalidEligibiliteRulesComponent.new(dossier: @dossier))) diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index ad99422a433..ef23ab1e7af 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -660,7 +660,8 @@ describe '#update brouillon' do before { sign_in(user) } - let(:procedure) { create(:procedure, :published, types_de_champ_public: [{}, { type: :piece_justificative }]) } + let(:procedure) { create(:procedure, :published, types_de_champ_public:) } + let(:types_de_champ_public) { [{}, { type: :piece_justificative }] } let(:dossier) { create(:dossier, user:, procedure:) } let(:first_champ) { dossier.champs_public.first } let(:piece_justificative_champ) { dossier.champs_public.last } @@ -770,6 +771,64 @@ end end end + + context 'having eligibilite_rules setup' do + include Logic + render_views + + let(:types_de_champ_public) { [{ type: :text }, { type: :integer_number }] } + let(:text_champ) { dossier.champs_public.first } + let(:number_champ) { dossier.champs_public.last } + let(:submit_payload) do + { + id: dossier.id, + dossier: { + groupe_instructeur_id: dossier.groupe_instructeur_id, + champs_public_attributes: { + text_champ.public_id => { + with_public_id: true, + value: "hello world" + }, + number_champ.public_id => { + with_public_id: true, + value: + } + } + } + } + end + let(:must_be_greater_than) { 10 } + + before do + procedure.published_revision.eligibilite_rules = greater_than(champ_value(number_champ.stable_id), constant(must_be_greater_than)) + procedure.published_revision.save! + end + render_views + + context 'when it pass from undefined to true' do + let(:value) { must_be_greater_than / 2 } + + it 'raises popup' do + subject + dossier.reload + expect(dossier.can_passer_en_construction?).to be_falsey + expect(assigns(:eligibilite_rules_was_computable)).to eq(false) + expect(assigns(:eligibilite_rules_is_computable)).to eq(true) + expect(response.body).to match(ActionView::RecordIdentifier.dom_id(dossier, :eligibilite_rules_broken)) + end + end + context 'when it pass from undefined to false' do + let(:value) { must_be_greater_than * 2 } + it 'does nothing' do + subject + dossier.reload + expect(dossier.can_passer_en_construction?).to be_truthy + expect(assigns(:eligibilite_rules_was_computable)).to eq(false) + expect(assigns(:eligibilite_rules_is_computable)).to eq(true) + expect(response.body).not_to have_selector("##{ActionView::RecordIdentifier.dom_id(dossier, :eligibilite_rules_broken)}") + end + end + end end describe '#update en_construction' do diff --git a/spec/views/shared/dossiers/_edit.html.haml_spec.rb b/spec/views/shared/dossiers/_edit.html.haml_spec.rb index c242f3ec8c3..859d3995cb7 100644 --- a/spec/views/shared/dossiers/_edit.html.haml_spec.rb +++ b/spec/views/shared/dossiers/_edit.html.haml_spec.rb @@ -149,4 +149,18 @@ end end end + + context 'when dossier transitions rules are computable and passer_en_construction is false' do + let(:types_de_champ_public) { [] } + let(:dossier) { create(:dossier, procedure:) } + + before do + allow_any_instance_of(Dossiers::InvalidEligibiliteRulesComponent).to receive(:eligibilite_rules_computable?).and_return(true) + allow(dossier).to receive(:can_passer_en_construction?).and_return(false) + end + + it 'renders broken transitions rules dialog' do + expect(subject).to have_selector("##{ActionView::RecordIdentifier.dom_id(dossier, :eligibilite_rules_broken)}") + end + end end