Merge pull request #11131 from colinux/fix-edit-footer-logic
ETQ usager corrige le fait d'être informé d'un dossier inéligible dans certaines conditions
This commit is contained in:
commit
1f8325b172
18 changed files with 122 additions and 78 deletions
|
@ -5,7 +5,8 @@ en:
|
|||
confirmation: Draft saved
|
||||
error: Impossible to save the draft
|
||||
en_construction:
|
||||
explanation: Your modifications are automatically saved. Submit them when you’re done.
|
||||
explanation: Your modifications are automatically saved.
|
||||
submit_them: Submit them when you’re done.
|
||||
confirmation: Modifications saved
|
||||
error: Impossible to save the modifications.
|
||||
annotations:
|
||||
|
|
|
@ -5,7 +5,8 @@ fr:
|
|||
confirmation: Brouillon enregistré
|
||||
error: Impossible d’enregistrer le brouillon
|
||||
en_construction:
|
||||
explanation: Vos modifications sont automatiquement enregistrées. Déposez-les quand vous aurez terminé.
|
||||
explanation: Vos modifications sont automatiquement enregistrées.
|
||||
submit_them: Déposez-les quand vous aurez terminé.
|
||||
confirmation: Modifications enregistrées.
|
||||
error: Impossible d’enregistrer les modifications
|
||||
annotations:
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
= t('.annotations.explanation')
|
||||
- elsif dossier.editing_fork?
|
||||
= t('.en_construction.explanation')
|
||||
- if dossier.can_passer_en_construction?
|
||||
= t('.en_construction.submit_them')
|
||||
- else
|
||||
= t('.brouillon.explanation')
|
||||
- if !annotation?
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Dossiers::InvalidIneligibiliteRulesComponent < ApplicationComponent
|
||||
delegate :can_passer_en_construction?, to: :@dossier
|
||||
delegate :can_passer_en_construction?, to: :dossier
|
||||
|
||||
def initialize(dossier:)
|
||||
def initialize(dossier:, wrapped: true)
|
||||
@dossier = dossier
|
||||
@revision = dossier.revision
|
||||
|
||||
@opened = !dossier.can_passer_en_construction?
|
||||
@wrapped = wrapped
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :dossier
|
||||
|
||||
def render?
|
||||
!can_passer_en_construction?
|
||||
dossier.revision.ineligibilite_enabled?
|
||||
end
|
||||
|
||||
def error_message
|
||||
@dossier.revision.ineligibilite_message
|
||||
dossier.revision.ineligibilite_message
|
||||
end
|
||||
|
||||
def opened? = @opened
|
||||
def wrapped? = @wrapped
|
||||
end
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
%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" } }
|
||||
- modal_content = capture do
|
||||
%button.fr-sr-only{ aria: { controls: 'modal-eligibilite-rules-dialog' }, data: { 'fr-opened': opened?.to_s } }
|
||||
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' } }
|
||||
%dialog.fr-modal{ "aria-labelledby" => "fr-modal-title-modal-1", role: "dialog", id: 'modal-eligibilite-rules-dialog', data: { 'turbo-permanent' => true } }
|
||||
.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
|
||||
|
@ -14,3 +14,9 @@
|
|||
%span.fr-icon-arrow-right-line.fr-icon--lg>
|
||||
= t('.modal.title')
|
||||
%p= error_message
|
||||
|
||||
- if wrapped?
|
||||
#ineligibilite_rules_modal
|
||||
= modal_content
|
||||
- else
|
||||
= modal_content
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
= render Dsfr::AlertComponent.new(state: :warning) do |c|
|
||||
= render Dsfr::AlertComponent.new(state: :warning, extra_class_names: "fr-mb-3w") do |c|
|
||||
- c.with_body do
|
||||
= t('.pending_republish_html', href: admin_procedure_path(@procedure.id))
|
||||
|
|
|
@ -11,7 +11,8 @@ module Administrateurs
|
|||
draft_revision.assign_attributes(procedure_revision_params)
|
||||
|
||||
if draft_revision.validate(:ineligibilite_rules_editor) && draft_revision.save
|
||||
redirect_to edit_admin_procedure_ineligibilite_rules_path(@procedure)
|
||||
flash[:notice] = "Les conditions d‘inéligibilité ont été modifiées."
|
||||
redirect_to [:admin, @procedure]
|
||||
else
|
||||
flash[:alert] = draft_revision.errors.full_messages
|
||||
render :edit
|
||||
|
|
|
@ -280,9 +280,7 @@ module Users
|
|||
def update
|
||||
@dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier
|
||||
@dossier = dossier_with_champs(pj_template: false)
|
||||
@can_passer_en_construction_was, @can_passer_en_construction_is = dossier.track_can_passer_en_construction do
|
||||
update_dossier_and_compute_errors
|
||||
end
|
||||
update_dossier_and_compute_errors
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
|
|
|
@ -80,11 +80,13 @@ export class AutosaveController extends ApplicationController {
|
|||
// Wait next tick so champs having JS can interact
|
||||
// with form elements before extracting form data.
|
||||
setTimeout(() => {
|
||||
this.enqueueAutosaveRequest();
|
||||
this.enqueueAutosaveWithValidationRequest();
|
||||
this.showConditionnalSpinner(target);
|
||||
}, 0);
|
||||
},
|
||||
inputable: (target) => this.enqueueOnInput(target, true),
|
||||
inputable: (target) => {
|
||||
this.enqueueOnInput(target, true);
|
||||
},
|
||||
hidden: (target) => {
|
||||
// In comboboxes we dispatch a "change" event on hidden inputs to trigger autosave.
|
||||
// We want to debounce them.
|
||||
|
@ -152,7 +154,7 @@ export class AutosaveController extends ApplicationController {
|
|||
|
||||
private didRequestRetry() {
|
||||
if (this.#needsRetry) {
|
||||
this.enqueueAutosaveRequest();
|
||||
this.enqueueAutosaveWithValidationRequest();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -38,15 +38,15 @@ module DossierCloneConcern
|
|||
end
|
||||
|
||||
def forked_with_changes?
|
||||
if forked_diff.present?
|
||||
forked_diff.values.any?(&:present?) || forked_groupe_instructeur_changed?
|
||||
end
|
||||
return false if forked_diff.blank?
|
||||
|
||||
forked_diff.values.any?(&:present?) || forked_groupe_instructeur_changed?
|
||||
end
|
||||
|
||||
def champ_forked_with_changes?(champ)
|
||||
if forked_diff.present?
|
||||
forked_diff.values.any? { |champs| champs.any? { _1.public_id == champ.public_id } }
|
||||
end
|
||||
return false if forked_diff.blank?
|
||||
|
||||
forked_diff.values.any? { |champs| champs.any? { _1.public_id == champ.public_id } }
|
||||
end
|
||||
|
||||
def make_diff(editing_fork)
|
||||
|
|
|
@ -1025,18 +1025,6 @@ class Dossier < ApplicationRecord
|
|||
procedure.accuse_lecture? && termine?
|
||||
end
|
||||
|
||||
def track_can_passer_en_construction
|
||||
if !revision.ineligibilite_enabled
|
||||
yield
|
||||
[true, true] # without eligibilite rules, we never reach dossier.champs.visible?, don't cache anything
|
||||
else
|
||||
from = can_passer_en_construction? # with eligibilite rules, self.champ[x].visible is cached by passing thru conditions checks
|
||||
yield
|
||||
champs.map(&:reset_visible) # we must reset self.champs[x].visible?, because an update occurred and we should re-evaluate champs[x] visibility
|
||||
[from, can_passer_en_construction?]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_default_champs
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
= 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 @can_passer_en_construction_was && !@can_passer_en_construction_is
|
||||
= turbo_stream.append('contenu', render(Dossiers::InvalidIneligibiliteRulesComponent.new(dossier: @dossier)))
|
||||
- else @ineligibilite_rules_is_computable
|
||||
= turbo_stream.remove(dom_id(@dossier, :ineligibilite_rules_broken))
|
||||
- if params[:validate].present? && @dossier.revision.ineligibilite_enabled?
|
||||
= turbo_stream.update :ineligibilite_rules_modal, render(Dossiers::InvalidIneligibiliteRulesComponent.new(dossier: @dossier, wrapped: false))
|
||||
|
||||
- if @update_contact_information
|
||||
= turbo_stream.update "contact_information", partial: 'shared/dossiers/update_contact_information', locals: { dossier: @dossier, procedure: @dossier.procedure }
|
||||
|
|
44
spec/components/dossiers/autosave_footer_component_spec.rb
Normal file
44
spec/components/dossiers/autosave_footer_component_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Dossiers::AutosaveFooterComponent, type: :component do
|
||||
subject(:component) { render_inline(described_class.new(dossier:, annotation:)) }
|
||||
|
||||
let(:dossier) { create(:dossier) }
|
||||
let(:annotation) { false }
|
||||
|
||||
context 'when showing brouillon state (default state)' do
|
||||
it 'displays brouillon explanation' do
|
||||
expect(component).to have_text("Votre brouillon")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when editing fork and can pass en construction' do
|
||||
let(:dossier) { create(:dossier, :en_construction).find_or_create_editing_fork(create(:user)) }
|
||||
|
||||
it 'displays en construction explanation' do
|
||||
expect(component).to have_text("Vos modifications")
|
||||
expect(component).to have_text("Déposez-les")
|
||||
end
|
||||
|
||||
context 'when dossier is not eligible' do
|
||||
before do
|
||||
allow(dossier).to receive(:can_passer_en_construction?).and_return(false)
|
||||
end
|
||||
|
||||
it 'displays en construction explanation' do
|
||||
expect(component).to have_text("Vos modifications")
|
||||
expect(component).not_to have_text("Déposez-les")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when showing annotations' do
|
||||
let(:annotation) { true }
|
||||
|
||||
it 'displays annotations explanation' do
|
||||
expect(component).to have_text("Vos annotations")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -243,7 +243,8 @@ describe Administrateurs::IneligibiliteRulesController, type: :controller do
|
|||
draft_revision = procedure.reload.draft_revision
|
||||
expect(draft_revision.ineligibilite_message).to eq('panpan')
|
||||
expect(draft_revision.ineligibilite_enabled).to eq(true)
|
||||
expect(response).to redirect_to(edit_admin_procedure_ineligibilite_rules_path(procedure))
|
||||
expect(response).to redirect_to(admin_procedure_path(procedure))
|
||||
expect(flash.notice).not_to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -775,9 +775,11 @@ describe Users::DossiersController, type: :controller do
|
|||
let(:types_de_champ_public) { [{ type: :text }, { type: :integer_number }] }
|
||||
let(:text_champ) { dossier.project_champs_public.first }
|
||||
let(:number_champ) { dossier.project_champs_public.last }
|
||||
let(:validate) { "true" }
|
||||
let(:submit_payload) do
|
||||
{
|
||||
id: dossier.id,
|
||||
validate:,
|
||||
dossier: {
|
||||
groupe_instructeur_id: dossier.groupe_instructeur_id,
|
||||
champs_public_attributes: {
|
||||
|
@ -805,28 +807,36 @@ describe Users::DossiersController, type: :controller do
|
|||
end
|
||||
render_views
|
||||
|
||||
context 'when it switches from true to false' do
|
||||
context 'when it becomes invalid' 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(:can_passer_en_construction_was)).to eq(true)
|
||||
expect(assigns(:can_passer_en_construction_is)).to eq(false)
|
||||
expect(response.body).to match(ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken))
|
||||
expect(response.body).to match(/aria-controls='modal-eligibilite-rules-dialog'[^>]*data-fr-opened='true'/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it stays true' do
|
||||
context 'when it says valid' 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(:can_passer_en_construction_was)).to eq(true)
|
||||
expect(assigns(:can_passer_en_construction_is)).to eq(true)
|
||||
expect(response.body).not_to have_selector("##{ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken)}")
|
||||
expect(response.body).to match(/aria-controls='modal-eligibilite-rules-dialog'[^>]*data-fr-opened='false'/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not validating' do
|
||||
let(:validate) { nil }
|
||||
let(:value) { must_be_greater_than + 1 }
|
||||
|
||||
it 'does not render invalid ineligible modal' do
|
||||
subject
|
||||
dossier.reload
|
||||
expect(dossier.can_passer_en_construction?).to be_falsey
|
||||
expect(response.body).not_to include("aria-controls='modal-eligibilite-rules-dialog'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,21 +28,21 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
visit brouillon_dossier_path(dossier)
|
||||
# no error while dossier is empty
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# does raise error when dossier is filled with condition that does not match
|
||||
# does nothing when dossier is filled with condition that does not match
|
||||
within "#champ-1" do
|
||||
find("label", text: "Non").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# raise error when dossier is filled with condition that matches
|
||||
# open modal when dossier is filled with condition that matches
|
||||
within "#champ-1" do
|
||||
find("label", text: "Oui").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: true)
|
||||
expect(page).to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
|
||||
# reload page and see error
|
||||
visit brouillon_dossier_path(dossier)
|
||||
|
@ -51,6 +51,7 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
|
||||
# modal is closable, and we can change our dossier response to be eligible
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: true)
|
||||
expect(page).to have_text("Vous ne pouvez pas déposer votre dossier")
|
||||
within("#modal-eligibilite-rules-dialog") { click_on "Fermer" }
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
|
@ -78,7 +79,7 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
visit brouillon_dossier_path(dossier)
|
||||
# no error while dossier is empty
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# first condition matches (so ineligible), cannot submit dossier and error message is clear
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
|
@ -148,14 +149,14 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
visit brouillon_dossier_path(dossier)
|
||||
# no error while dossier is empty
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# only one condition is matches, can submit dossier
|
||||
within "#champ-#{first_tdc.stable_id}" do
|
||||
find("label", text: "Oui").click
|
||||
end
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
# Now test dossier modification
|
||||
click_on "Déposer le dossier"
|
||||
|
@ -196,7 +197,7 @@ describe 'Dossier Inéligibilité', js: true do
|
|||
scenario 'ineligibilite rules without validation on champ ensure to re-process cached champs.visible' do
|
||||
visit brouillon_dossier_path(dossier)
|
||||
expect(page).to have_selector(:button, text: "Déposer le dossier", disabled: false)
|
||||
expect(page).not_to have_content("Vous ne pouvez pas déposer votre dossier")
|
||||
expect(page).to have_selector("#modal-eligibilite-rules-dialog", visible: false)
|
||||
|
||||
within "#champ-1" do
|
||||
find("label", text: "Non").click
|
||||
|
|
|
@ -160,10 +160,11 @@ describe 'shared/dossiers/edit', type: :view do
|
|||
|
||||
before do
|
||||
allow(dossier).to receive(:can_passer_en_construction?).and_return(false)
|
||||
allow(dossier.revision).to receive(:ineligibilite_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
it 'renders broken transitions rules dialog' do
|
||||
expect(subject).to have_selector("##{ActionView::RecordIdentifier.dom_id(dossier, :ineligibilite_rules_broken)}")
|
||||
expect(subject).to have_selector("#ineligibilite_rules_modal [data-fr-opened='true']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue