feat(Users/Dossiers#update): track changes live and pop modal when ineligibilite_rules matches
This commit is contained in:
parent
2210db3b81
commit
be5f580237
10 changed files with 164 additions and 24 deletions
|
@ -0,0 +1,16 @@
|
|||
class Dossiers::InvalidIneligibiliteRulesComponent < ApplicationComponent
|
||||
delegate :can_passer_en_construction?, :ineligibilite_rules_computable?, to: :@dossier
|
||||
|
||||
def initialize(dossier:)
|
||||
@dossier = dossier
|
||||
@revision = dossier.revision
|
||||
end
|
||||
|
||||
def render?
|
||||
ineligibilite_rules_computable? && !can_passer_en_construction?
|
||||
end
|
||||
|
||||
def error_message
|
||||
@dossier.revision.ineligibilite_message
|
||||
end
|
||||
end
|
|
@ -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"
|
|
@ -0,0 +1,5 @@
|
|||
fr:
|
||||
modal:
|
||||
title: "Vous ne pouvez pas déposer votre dossier"
|
||||
close: "Fermer"
|
||||
close_alt: "Fermer la fenêtre modale"
|
|
@ -0,0 +1,16 @@
|
|||
%div{ id: dom_id(@dossier, :ineligibilite_rules_broken), data: { controller: 'ineligibilite-rules-match', 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: { 'ineligibilite-rules-match-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
|
|
@ -303,10 +303,13 @@ module Users
|
|||
def update
|
||||
@dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
@errors = update_dossier_and_compute_errors
|
||||
|
||||
@dossier.index_search_terms_later if @errors.empty?
|
||||
|
||||
@ineligibilite_rules_was_computable = @dossier.ineligibilite_rules_computable?
|
||||
@can_passer_en_construction_was = @dossier.can_passer_en_construction?
|
||||
update_dossier_and_compute_errors
|
||||
@dossier.index_search_terms_later if @dossier.errors.empty?
|
||||
@ineligibilite_rules_is_computable = @dossier.ineligibilite_rules_computable?
|
||||
@can_passer_en_construction_is = @dossier.can_passer_en_construction?
|
||||
@ineligibilite_rules_computable_changed = !@ineligibilite_rules_was_computable && @ineligibilite_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?))
|
||||
|
|
|
@ -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 InvalidIneligibiliteRulesController extends ApplicationController {
|
||||
static targets = ['dialog'];
|
||||
|
||||
declare dialogTarget: HTMLElement;
|
||||
|
||||
connect() {
|
||||
setTimeout(() => window.dsfr(this.dialogTarget).modal.disclose(), 100);
|
||||
}
|
||||
}
|
|
@ -25,4 +25,6 @@
|
|||
|
||||
= render Dossiers::PendingCorrectionCheckboxComponent.new(dossier: dossier)
|
||||
|
||||
= render Dossiers::InvalidIneligibiliteRulesComponent.new(dossier: dossier)
|
||||
|
||||
= render Dossiers::EditFooterComponent.new(dossier: dossier_for_editing, annotation: false)
|
||||
|
|
|
@ -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 @ineligibilite_rules_is_computable
|
||||
= turbo_stream.remove(dom_id(@dossier, :ineligibilite_rules_broken))
|
||||
|
||||
- if (@ineligibilite_rules_computable_changed && !@can_passer_en_construction_is) || (@can_passer_en_construction_was && !@can_passer_en_construction_is)
|
||||
= turbo_stream.append('contenu', render(Dossiers::InvalidIneligibiliteRulesComponent.new(dossier: @dossier)))
|
||||
|
|
|
@ -398,7 +398,9 @@ describe Users::DossiersController, type: :controller do
|
|||
|
||||
describe '#submit_brouillon' do
|
||||
before { sign_in(user) }
|
||||
let!(:dossier) { create(:dossier, user: user) }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||
let(:types_de_champ_public) { [{ type: :text }] }
|
||||
let!(:dossier) { create(:dossier, user:, procedure:) }
|
||||
let(:first_champ) { dossier.champs_public.first }
|
||||
let(:anchor_to_first_champ) { controller.helpers.link_to first_champ.libelle, brouillon_dossier_path(anchor: first_champ.labelledby_id), class: 'error-anchor' }
|
||||
let(:value) { 'beautiful value' }
|
||||
|
@ -439,9 +441,9 @@ describe Users::DossiersController, type: :controller do
|
|||
render_views
|
||||
let(:error_message) { 'nop' }
|
||||
before do
|
||||
expect_any_instance_of(Dossier).to receive(:validate).and_return(false)
|
||||
expect_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||
[double(inner_error: double(base: first_champ), message: 'nop')]
|
||||
allow_any_instance_of(Dossier).to receive(:validate).and_return(false)
|
||||
allow_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||
[instance_double(ActiveModel::NestedError, inner_error: double(base: first_champ), message: 'nop')]
|
||||
)
|
||||
subject
|
||||
end
|
||||
|
@ -461,11 +463,8 @@ describe Users::DossiersController, type: :controller do
|
|||
render_views
|
||||
|
||||
let(:value) { nil }
|
||||
|
||||
before do
|
||||
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
|
||||
subject
|
||||
end
|
||||
let(:types_de_champ_public) { [{ type: :text, mandatory: true, libelle: 'l' }] }
|
||||
before { subject }
|
||||
|
||||
it { expect(response).to render_template(:brouillon) }
|
||||
it { expect(response.body).to have_link(first_champ.libelle, href: "##{first_champ.labelledby_id}") }
|
||||
|
@ -548,8 +547,8 @@ describe Users::DossiersController, type: :controller do
|
|||
render_views
|
||||
|
||||
before do
|
||||
expect_any_instance_of(Dossier).to receive(:validate).and_return(false)
|
||||
expect_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||
allow_any_instance_of(Dossier).to receive(:validate).and_return(false)
|
||||
allow_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||
[double(inner_error: double(base: first_champ), message: 'nop')]
|
||||
)
|
||||
|
||||
|
@ -661,7 +660,8 @@ describe Users::DossiersController, type: :controller do
|
|||
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 }
|
||||
|
@ -754,13 +754,65 @@ describe Users::DossiersController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
it "debounce search terms indexation" do
|
||||
# dossier creation trigger a first indexation and flag,
|
||||
# so we we have to remove this flag
|
||||
dossier.debounce_index_search_terms_flag.remove
|
||||
context 'having ineligibilite_rules setup' do
|
||||
include Logic
|
||||
render_views
|
||||
|
||||
assert_enqueued_jobs(1, only: DossierIndexSearchTermsJob) do
|
||||
3.times { patch :update, params: payload, format: :turbo_stream }
|
||||
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.update(
|
||||
ineligibilite_enabled: true,
|
||||
ineligibilite_message: 'lol',
|
||||
ineligibilite_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 + 1 }
|
||||
|
||||
it 'raises popup' do
|
||||
subject
|
||||
dossier.reload
|
||||
expect(dossier.can_passer_en_construction?).to be_falsey
|
||||
expect(assigns(:ineligibilite_rules_was_computable)).to eq(false)
|
||||
expect(assigns(:ineligibilite_rules_is_computable)).to eq(true)
|
||||
expect(response.body).to match(ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken))
|
||||
end
|
||||
end
|
||||
context 'when it pass from undefined to false' do
|
||||
let(:value) { must_be_greater_than - 1 }
|
||||
it 'does nothing' do
|
||||
subject
|
||||
dossier.reload
|
||||
expect(dossier.can_passer_en_construction?).to be_truthy
|
||||
expect(assigns(:ineligibilite_rules_was_computable)).to eq(false)
|
||||
expect(assigns(:ineligibilite_rules_is_computable)).to eq(true)
|
||||
expect(response.body).not_to have_selector("##{ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken)}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -868,8 +920,8 @@ describe Users::DossiersController, type: :controller do
|
|||
|
||||
context 'classic error' do
|
||||
before do
|
||||
expect_any_instance_of(Dossier).to receive(:save).and_return(false)
|
||||
expect_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||
allow_any_instance_of(Dossier).to receive(:save).and_return(false)
|
||||
allow_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||
[message: 'nop', inner_error: double(base: first_champ)]
|
||||
)
|
||||
subject
|
||||
|
|
|
@ -149,4 +149,18 @@ describe 'shared/dossiers/edit', type: :view do
|
|||
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::InvalidIneligibiliteRulesComponent).to receive(:ineligibilite_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, :ineligibilite_rules_broken)}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue