From 2c79ca94f5bfbaae686b8296091398be142c7591 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 28 Feb 2023 15:06:39 +0100 Subject: [PATCH 01/24] chore(schema): create dossier_corrections --- app/models/commentaire.rb | 1 + app/models/concerns/dossier_resolvable_concern.rb | 7 +++++++ app/models/dossier.rb | 1 + app/models/dossier_resolution.rb | 15 +++++++++++++++ .../20230228134859_create_dossier_resolutions.rb | 15 +++++++++++++++ db/schema.rb | 13 +++++++++++++ spec/factories/dossier_resolutions.rb | 11 +++++++++++ spec/models/dossier_resolution_spec.rb | 5 +++++ 8 files changed, 68 insertions(+) create mode 100644 app/models/concerns/dossier_resolvable_concern.rb create mode 100644 app/models/dossier_resolution.rb create mode 100644 db/migrate/20230228134859_create_dossier_resolutions.rb create mode 100644 spec/factories/dossier_resolutions.rb create mode 100644 spec/models/dossier_resolution_spec.rb diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index f5e3561e7..1a90af788 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -19,6 +19,7 @@ class Commentaire < ApplicationRecord belongs_to :instructeur, inverse_of: :commentaires, optional: true belongs_to :expert, inverse_of: :commentaires, optional: true + has_one :dossier_resolution, inverse_of: :commentaire, dependent: :nullify validate :messagerie_available?, on: :create, unless: -> { dossier.brouillon? } diff --git a/app/models/concerns/dossier_resolvable_concern.rb b/app/models/concerns/dossier_resolvable_concern.rb new file mode 100644 index 000000000..cb78a2ba0 --- /dev/null +++ b/app/models/concerns/dossier_resolvable_concern.rb @@ -0,0 +1,7 @@ +module DossierResolvableConcern + extend ActiveSupport::Concern + + included do + has_many :resolutions, class_name: 'DossierResolution', dependent: :destroy + end +end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index ac717eba4..2e48629d5 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -50,6 +50,7 @@ class Dossier < ApplicationRecord include DossierFilteringConcern include DossierPrefillableConcern include DossierRebaseConcern + include DossierResolvableConcern include DossierSearchableConcern include DossierSectionsConcern include DossierCloneConcern diff --git a/app/models/dossier_resolution.rb b/app/models/dossier_resolution.rb new file mode 100644 index 000000000..5f5fc97e1 --- /dev/null +++ b/app/models/dossier_resolution.rb @@ -0,0 +1,15 @@ +# == Schema Information +# +# Table name: dossier_resolutions +# +# id :bigint not null, primary key +# resolved_at :datetime +# created_at :datetime not null +# updated_at :datetime not null +# commentaire_id :bigint +# dossier_id :bigint not null +# +class DossierResolution < ApplicationRecord + belongs_to :dossier + belongs_to :commentaire +end diff --git a/db/migrate/20230228134859_create_dossier_resolutions.rb b/db/migrate/20230228134859_create_dossier_resolutions.rb new file mode 100644 index 000000000..38c597600 --- /dev/null +++ b/db/migrate/20230228134859_create_dossier_resolutions.rb @@ -0,0 +1,15 @@ +class CreateDossierCorrections < ActiveRecord::Migration[6.1] + disable_ddl_transaction! + + def change + create_table :dossier_corrections do |t| + t.references :dossier, null: false, foreign_key: true + t.references :commentaire, foreign_key: true + t.datetime :resolved_at, precision: 6 + + t.timestamps + end + + add_index :dossier_corrections, :resolved_at, where: "(resolved_at IS NULL OR resolved_at IS NOT NULL)", algorithm: :concurrently + end +end diff --git a/db/schema.rb b/db/schema.rb index 0c7119b4a..0a6b89a84 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -318,6 +318,17 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_08_160551) do t.index ["dossier_id"], name: "index_dossier_batch_operations_on_dossier_id" end + create_table "dossier_corrections", force: :cascade do |t| + t.bigint "commentaire_id" + t.datetime "created_at", precision: 6, null: false + t.bigint "dossier_id", null: false + t.datetime "resolved_at", precision: 6 + t.datetime "updated_at", precision: 6, null: false + t.index ["commentaire_id"], name: "index_dossier_corrections_on_commentaire_id" + t.index ["dossier_id"], name: "index_dossier_corrections_on_dossier_id" + t.index ["resolved_at"], name: "index_dossier_corrections_on_resolved_at", where: "((resolved_at IS NULL) OR (resolved_at IS NOT NULL))" + end + create_table "dossier_operation_logs", force: :cascade do |t| t.boolean "automatic_operation", default: false, null: false t.bigint "bill_signature_id" @@ -1010,6 +1021,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_05_08_160551) do add_foreign_key "commentaires", "instructeurs" add_foreign_key "dossier_batch_operations", "batch_operations" add_foreign_key "dossier_batch_operations", "dossiers" + add_foreign_key "dossier_corrections", "commentaires" + add_foreign_key "dossier_corrections", "dossiers" add_foreign_key "dossier_operation_logs", "bill_signatures" add_foreign_key "dossier_transfer_logs", "dossiers" add_foreign_key "dossiers", "batch_operations" diff --git a/spec/factories/dossier_resolutions.rb b/spec/factories/dossier_resolutions.rb new file mode 100644 index 000000000..0bc95ec54 --- /dev/null +++ b/spec/factories/dossier_resolutions.rb @@ -0,0 +1,11 @@ +FactoryBot.define do + factory :dossier_resolution do + dossier + commentaire + resolved_at { nil } + + trait :resolved do + resolved_at { Time.zone.now } + end + end +end diff --git a/spec/models/dossier_resolution_spec.rb b/spec/models/dossier_resolution_spec.rb new file mode 100644 index 000000000..8acd68e8e --- /dev/null +++ b/spec/models/dossier_resolution_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe DossierResolution, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end From d73b96ea40c838e06b4090d6675190114e4ffb09 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 28 Feb 2023 15:11:48 +0100 Subject: [PATCH 02/24] feat(dossier): pending resolution state --- .../concerns/dossier_resolvable_concern.rb | 7 +++++ app/models/dossier_resolution.rb | 2 ++ .../dossier_resolvable_concern_spec.rb | 28 +++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 spec/models/concern/dossier_resolvable_concern_spec.rb diff --git a/app/models/concerns/dossier_resolvable_concern.rb b/app/models/concerns/dossier_resolvable_concern.rb index cb78a2ba0..cb71fe885 100644 --- a/app/models/concerns/dossier_resolvable_concern.rb +++ b/app/models/concerns/dossier_resolvable_concern.rb @@ -3,5 +3,12 @@ module DossierResolvableConcern included do has_many :resolutions, class_name: 'DossierResolution', dependent: :destroy + + def pending_resolution? + # We don't want to show any alert if user is not allowed to modify the dossier + return false unless en_construction? + + resolutions.pending.exists? + end end end diff --git a/app/models/dossier_resolution.rb b/app/models/dossier_resolution.rb index 5f5fc97e1..706d65367 100644 --- a/app/models/dossier_resolution.rb +++ b/app/models/dossier_resolution.rb @@ -12,4 +12,6 @@ class DossierResolution < ApplicationRecord belongs_to :dossier belongs_to :commentaire + + scope :pending, -> { where(resolved_at: nil) } end diff --git a/spec/models/concern/dossier_resolvable_concern_spec.rb b/spec/models/concern/dossier_resolvable_concern_spec.rb new file mode 100644 index 000000000..4127f6c1d --- /dev/null +++ b/spec/models/concern/dossier_resolvable_concern_spec.rb @@ -0,0 +1,28 @@ +describe DossierResolvableConcern do + describe "#pending_resolution?" do + let(:dossier) { create(:dossier, :en_construction) } + + context "when dossier has no resolution" do + it { expect(dossier.pending_resolution?).to be_falsey } + end + + context "when dossier has a pending resolution" do + before { create(:dossier_resolution, dossier:) } + + it { expect(dossier.pending_resolution?).to be_truthy } + end + + context "when dossier has a resolved resolution" do + before { create(:dossier_resolution, :resolved, dossier:) } + + it { expect(dossier.pending_resolution?).to be_falsey } + end + + context "when dossier is not en_construction" do + let(:dossier) { create(:dossier, :en_instruction) } + before { create(:dossier_resolution, dossier:) } + + it { expect(dossier.pending_resolution?).to be_falsey } + end + end +end From ab1271efd9e73012c63edac5ef22d0c5a72dace2 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 28 Feb 2023 16:32:37 +0100 Subject: [PATCH 03/24] fix(yaml): duplicate shared.procedures keys --- config/locales/fr.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index e66005122..ef73f6053 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -824,11 +824,6 @@ fr: explication_html: "

API Particulier facilite l’accès des administrations aux données familiales (CAF), aux données fiscales (DGFiP), au statut pôle-emploi et au statut étudiant d’un citoyen pour simplifier les démarches administratives mises en œuvre par les collectivités et les administrations.
Cela permet aux administrations d’accéder à des informations certifiées à la source et ainsi :

  • de s’affranchir des pièces justificatives lors des démarches en ligne,
  • de réduire le nombre d’erreurs de saisie,
  • d’écarter le risque de fraude documentaire.

Important : les disposition de l’article L144-8 n’autorisent que l’échange des informations strictement nécessaires pour traiter une démarche.

En conséquence, ne sélectionnez ici que les données auxquelles vous aurez accès d’un point de vue légal.

" update: sources_ok: 'Mise à jour effectuée' - procedures: - show: - ready: "Validé" - needs_configuration: "À configurer" - configure_api_particulier_token: "Configurer le jeton API particulier" zones: ministeres: Ministères france_connect: From 5d61c6fa35fd59fb958337674bfe55581c211af2 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 13 Mar 2023 11:42:11 +0100 Subject: [PATCH 04/24] refactor(messagerie): form more dsfr compliant --- .../shared/dossiers/messages/_form.html.haml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/shared/dossiers/messages/_form.html.haml b/app/views/shared/dossiers/messages/_form.html.haml index 69bca30cc..5e616301c 100644 --- a/app/views/shared/dossiers/messages/_form.html.haml +++ b/app/views/shared/dossiers/messages/_form.html.haml @@ -1,5 +1,5 @@ = render NestedForms::FormOwnerComponent.new -= form_for(commentaire, url: form_url, html: { class: 'form', multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: @dossier.present? ? dom_id(@dossier) : dom_id(@procedure, :bulk_message) } }) do |f| += form_for(commentaire, url: form_url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: @dossier.present? ? dom_id(@dossier) : dom_id(@procedure, :bulk_message) } }) do |f| - dossier = commentaire.dossier - placeholder = t('views.shared.dossiers.messages.form.write_message_to_administration_placeholder') - if instructeur_signed_in? || administrateur_signed_in? || expert_signed_in? @@ -10,11 +10,11 @@ = t('message', scope: [:utils]) %span.mandatory * = f.text_area :body, rows: 5, placeholder: placeholder, title: placeholder, required: true, class: 'fr-input message-textarea' - .flex.justify-between.wrap - - disable_piece_jointe = defined?(disable_piece_jointe) ? disable_piece_jointe : false - %div - - if !disable_piece_jointe - = render Attachment::EditComponent.new(attached_file: commentaire.piece_jointe) - .send-wrapper.fr-my-3w - = f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn send', data: { disable: true } + - disable_piece_jointe = defined?(disable_piece_jointe) ? disable_piece_jointe : false + - if !disable_piece_jointe + .fr-mt-3w + = render Attachment::EditComponent.new(attached_file: commentaire.piece_jointe) + + .fr-mt-3w + = f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true } From ca3b1279420849c37af91787d7c8a963467a7ea8 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 14 Mar 2023 17:23:17 +0100 Subject: [PATCH 05/24] feat(instructeur): can flag a dossier as "pending corrections" --- .../message_component.en.yml | 1 + .../message_component.fr.yml | 1 + .../message_component.html.haml | 3 + .../instructeurs/dossiers_controller.rb | 28 +++++++ app/models/commentaire.rb | 4 + .../concerns/dossier_resolvable_concern.rb | 16 ++++ .../dossiers/_instruction_button.html.haml | 73 ++++++++++------ .../_instruction_button_motivation.html.haml | 8 +- config/routes.rb | 1 + .../instructeurs/dossiers_controller_spec.rb | 84 +++++++++++++++++++ .../dossier_resolvable_concern_spec.rb | 52 ++++++++++++ .../_instruction_button.html.haml_spec.rb | 12 ++- .../dossiers/show.html.haml_spec.rb | 3 +- 13 files changed, 254 insertions(+), 32 deletions(-) diff --git a/app/components/dossiers/message_component/message_component.en.yml b/app/components/dossiers/message_component/message_component.en.yml index a24af5c49..22ac79059 100644 --- a/app/components/dossiers/message_component/message_component.en.yml +++ b/app/components/dossiers/message_component/message_component.en.yml @@ -7,3 +7,4 @@ en: automatic_email: Automatic email you: You deleted_body: Message deleted + flagged_pending_corrections: Modification requested diff --git a/app/components/dossiers/message_component/message_component.fr.yml b/app/components/dossiers/message_component/message_component.fr.yml index 4386b2ea2..af808590c 100644 --- a/app/components/dossiers/message_component/message_component.fr.yml +++ b/app/components/dossiers/message_component/message_component.fr.yml @@ -7,3 +7,4 @@ fr: automatic_email: Email automatique you: Vous deleted_body: Message supprimé + flagged_pending_corrections: Modification demandée diff --git a/app/components/dossiers/message_component/message_component.html.haml b/app/components/dossiers/message_component/message_component.html.haml index 2785815a2..992c60b45 100644 --- a/app/components/dossiers/message_component/message_component.html.haml +++ b/app/components/dossiers/message_component/message_component.html.haml @@ -6,6 +6,9 @@ = commentaire_issuer - if commentaire_from_guest? %span.fr-text--xs.fr-text-mention--grey.font-weight-normal= t('.guest') + - if commentaire.flagged_pending_corrections? + %span.fr-badge.fr-badge--sm.fr-badge--info + = t('.flagged_pending_corrections') %span.date{ class: ["fr-text--xs", "fr-text-mention--grey", "font-weight-normal", highlight_if_unseen_class], data: scroll_to_target } = commentaire_date .rich-text diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 220fd83a5..53513e77b 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -223,6 +223,34 @@ module Instructeurs render :change_state end + def pending_corrections + message, piece_jointe = params.require(:dossier).permit(:motivation, :justificatif_motivation).values + + if message.empty? + flash.alert = "Vous devez préciser quelle modification est attendue." + elsif !dossier.may_flag_as_pending_correction? + flash.alert = dossier.termine? ? "Impossible de demander de corriger un dossier terminé." : "Le dossier est déjà en attente de correction." + else + commentaire = CommentaireService.create(current_instructeur, dossier, { body: message, piece_jointe: }) + dossier.flag_as_pending_correction!(commentaire) + dossier.update!(last_commentaire_updated_at: Time.zone.now) + current_instructeur.follow(dossier) + + flash.notice = "Dossier marqué comme en attente de correction." + end + + respond_to do |format| + format.turbo_stream do + @dossier = dossier + render :change_state + end + + format.html do + redirect_back(fallback_location: instructeur_procedure_path(procedure)) + end + end + end + def create_commentaire @commentaire = CommentaireService.create(current_instructeur, dossier, commentaire_params) diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index 1a90af788..a3eecafed 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -95,6 +95,10 @@ class Commentaire < ApplicationRecord update! body: '' end + def flagged_pending_corrections? + DossierResolution.exists?(commentaire: self) + end + private def notify diff --git a/app/models/concerns/dossier_resolvable_concern.rb b/app/models/concerns/dossier_resolvable_concern.rb index cb71fe885..f4d827b56 100644 --- a/app/models/concerns/dossier_resolvable_concern.rb +++ b/app/models/concerns/dossier_resolvable_concern.rb @@ -4,6 +4,22 @@ module DossierResolvableConcern included do has_many :resolutions, class_name: 'DossierResolution', dependent: :destroy + def flag_as_pending_correction!(commentaire) + return unless may_flag_as_pending_correction? + + resolutions.create(commentaire:) + + return if en_construction? + + repasser_en_construction!(instructeur: commentaire.instructeur) + end + + def may_flag_as_pending_correction? + return false if resolutions.pending.exists? + + en_construction? || may_repasser_en_construction? + end + def pending_resolution? # We don't want to show any alert if user is not allowed to modify the dossier return false unless en_construction? diff --git a/app/views/instructeurs/dossiers/_instruction_button.html.haml b/app/views/instructeurs/dossiers/_instruction_button.html.haml index fbf1b1b3b..c8ed93ca5 100644 --- a/app/views/instructeurs/dossiers/_instruction_button.html.haml +++ b/app/views/instructeurs/dossiers/_instruction_button.html.haml @@ -1,35 +1,56 @@ -- if dossier.en_instruction? +- if dossier.en_instruction? || (dossier.en_construction? && dossier.may_flag_as_pending_correction?) = render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: { turbo_force: :server } }, button_options: { class: [button_or_label_class(dossier)]}, role: dossier.en_instruction? ? :region : :menu) do |menu| - menu.with_button_inner_html do - Instruire le dossier + = dossier.en_instruction? ? "Instruire le dossier" : "Demander une modification" - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do - %span.icon.accept - .dropdown-description - %h4 Accepter - L’usager sera informé que son dossier a été accepté + - if dossier.en_instruction? + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do + %span.icon.accept + .dropdown-description + %h4 Accepter + L’usager sera informé que son dossier a été accepté - - menu.with_item(class: "hidden inactive form-inside") do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } + - menu.with_item(class: "hidden inactive form-inside") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do - %span.icon.without-continuation - .dropdown-description - %h4 Classer sans suite - L’usager sera informé que son dossier a été classé sans suite + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do + %span.icon.without-continuation + .dropdown-description + %h4 Classer sans suite + L’usager sera informé que son dossier a été classé sans suite - - menu.with_item(class: "hidden inactive form-inside") do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } + - menu.with_item(class: "hidden inactive form-inside") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do - %span.icon.refuse - .dropdown-description - %h4 Refuser - L’usager sera informé que son dossier a été refusé + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do + %span.icon.refuse + .dropdown-description + %h4 Refuser + L’usager sera informé que son dossier a été refusé - - menu.with_item(class: "hidden inactive form-inside") do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } + - menu.with_item(class: "hidden inactive form-inside") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } + + - if dossier.may_flag_as_pending_correction? + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'pending_corrections');", role: 'menuitem') do + %span.fr-icon.fr-icon-error-warning-line.fr-text-default--info{ "aria-hidden": "true" } + .dropdown-description + %h4 Demander une modification + L’usager sera informé que des modifications sont attendues + + - menu.with_item(class: class_names("inactive form-inside": true, hidden: dossier.en_instruction?)) do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, + visible: !dossier.en_instruction?, + form_path: pending_corrections_instructeur_dossier_path(dossier.procedure, dossier), + placeholder: 'Expliquez au demandeur quelles modifications sont attendues', + popup_class: 'pending_corrections', + button_justificatif_label: "Ajouter une pièce jointe (facultatif)", + process_button: dossier.en_construction? ? 'Valider' : 'Valider et repasser en construction', + process_action: nil, + title: 'Marquer en attente de corrections', + confirm: 'Envoyer la demande de corrections ?'} diff --git a/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml b/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml index 4d089c6e4..31bc2c56f 100644 --- a/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml +++ b/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml @@ -1,5 +1,5 @@ -.motivation.hidden{ class: popup_class } - = form_tag(terminer_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo: true, turbo_confirm: confirm }, method: :post, multipart: true) do +.motivation{ class: class_names(popup_class => true, hidden: !defined?(visible) || !visible) } + = form_tag(defined?(form_path) ? form_path : terminer_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo: true, turbo_confirm: confirm }, method: :post, multipart: true) do - if title == 'Accepter' = text_area :dossier, :motivation, class: 'fr-input', placeholder: placeholder, required: false - if dossier.attestation_template&.activated? @@ -28,11 +28,11 @@ - else = text_area :dossier, :motivation, class: 'fr-input', placeholder: placeholder, required: true .optional-justificatif{ id: "justificatif_motivation_suggest_#{popup_class}", onclick: "DS.showImportJustificatif('#{popup_class}');" } - %button.fr-btn.fr-btn--tertiary-no-outline.fr-btn--icon-left.fr-icon-attachment-line.fr-ml-0{ type: 'button', onclick: "DS.showImportJustificatif('accept');" } Ajouter un justificatif (optionnel) + %button.fr-btn.fr-btn--tertiary-no-outline.fr-btn--icon-left.fr-icon-attachment-line.fr-ml-0{ type: 'button', onclick: "DS.showImportJustificatif('accept');" }= defined?(button_justificatif_label) ? button_justificatif_label : "Ajouter un justificatif (optionnel)" .hidden{ id: "justificatif_motivation_import_#{popup_class}" } = file_field :dossier, :justificatif_motivation, direct_upload: true, id: "dossier_justificatif_motivation_#{popup_class}",onchange: "DS.showDeleteJustificatif('#{popup_class}');" .hidden.js_delete_motivation{ id: "delete_motivation_import_#{popup_class}" } %button.fr-btn.fr-btn--tertiary-no-outline.fr-btn--icon-left.fr-icon-delete-line.fr-ml-0.fr-mt-1w{ type: 'button', onclick: "DS.deleteJustificatif('#{popup_class}');" } Supprimer le justificatif .fr-mt-2w = button_tag "Annuler", type: :reset, class: 'fr-btn fr-btn--secondary', onclick: 'DS.motivationCancel();' - = button_tag 'Valider la décision', name: :process_action, value: process_action, class: 'fr-btn fr-mr-0', title: title + = button_tag defined?(process_button) ? process_button : 'Valider la décision', name: :process_action, value: process_action, class: 'fr-btn fr-mr-0', title: title diff --git a/config/routes.rb b/config/routes.rb index 5c15cebf5..91d193c75 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -445,6 +445,7 @@ Rails.application.routes.draw do post 'repasser-en-construction' => 'dossiers#repasser_en_construction' post 'repasser-en-instruction' => 'dossiers#repasser_en_instruction' post 'terminer' + post 'pending_corrections' post 'send-to-instructeurs' => 'dossiers#send_to_instructeurs' post 'avis' => 'dossiers#create_avis' get 'print' => 'dossiers#print' diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 4b499faf4..5961564ec 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -496,6 +496,90 @@ describe Instructeurs::DossiersController, type: :controller do end end + describe '#pending_corrections' do + let(:message) { 'do that' } + let(:justificatif) { nil } + + subject do + post :pending_corrections, params: { + procedure_id: procedure.id, dossier_id: dossier.id, + dossier: { motivation: message, justificatif_motivation: justificatif } + }, format: :turbo_stream + end + + before { sign_in(instructeur.user) } + + context "dossier en instruction" do + let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) } + + before { subject } + + it 'pass en_construction and create a pending resolution' do + expect(response).to have_http_status(:ok) + expect(response.body).to include('en attente de modifications') + + expect(dossier.reload).to be_en_construction + expect(dossier).to be_pending_resolution + end + + it 'create a comment with text body' do + expect(dossier.commentaires.last.body).to eq("do that") + expect(dossier.commentaires.last).to be_flagged_pending_corrections + end + + context 'with an attachment' do + let(:justificatif) { fake_justificatif } + + it 'attach file to comment' do + expect(dossier.commentaires.last.piece_jointe).to be_attached + end + end + + context 'with an empty message' do + let(:message) { '' } + + it 'requires a message' do + expect(dossier.reload).not_to be_pending_resolution + expect(dossier.commentaires.count).to eq(0) + expect(response.body).to include('Vous devez préciser') + end + end + + context 'dossier already having pending corrections' do + before do + create(:dossier_resolution, dossier:) + end + + it 'does not create an new pending resolution' do + expect { subject }.not_to change { DossierResolution.count } + end + + it 'shows a flash alert' do + subject + + expect(response.body).to include('') + end + end + end + + context 'dossier en_construction' do + it 'can create a pending resolution' do + subject + expect(dossier.reload).to be_pending_resolution + expect(dossier.commentaires.last).to be_flagged_pending_corrections + end + end + + context 'dossier is termine' do + let(:dossier) { create(:dossier, :accepte, :with_individual, procedure: procedure) } + + it 'does not create a pending resolution' do + expect { subject }.not_to change { DossierResolution.count } + expect(response.body).to include('Impossible') + end + end + end + describe '#messagerie' do before { expect(controller.current_instructeur).to receive(:mark_tab_as_seen).with(dossier, :messagerie) } subject { get :messagerie, params: { procedure_id: procedure.id, dossier_id: dossier.id } } diff --git a/spec/models/concern/dossier_resolvable_concern_spec.rb b/spec/models/concern/dossier_resolvable_concern_spec.rb index 4127f6c1d..d02b0b539 100644 --- a/spec/models/concern/dossier_resolvable_concern_spec.rb +++ b/spec/models/concern/dossier_resolvable_concern_spec.rb @@ -25,4 +25,56 @@ describe DossierResolvableConcern do it { expect(dossier.pending_resolution?).to be_falsey } end end + + describe '#flag_as_pending_correction!' do + let(:dossier) { create(:dossier, :en_construction) } + let(:instructeur) { create(:instructeur) } + let(:commentaire) { create(:commentaire, dossier:, instructeur:) } + + context 'when dossier is en_construction' do + it 'creates a resolution' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.resolutions.pending.count }.by(1) + end + + it 'does not change dossier state' do + expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.state } + end + end + + context 'when dossier is not en_instruction' do + let(:dossier) { create(:dossier, :en_instruction) } + + it 'creates a resolution' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.resolutions.pending.count }.by(1) + end + + it 'repasse dossier en_construction' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.state }.to('en_construction') + end + end + + context 'when dossier has already a pending resolution' do + before { create(:dossier_resolution, dossier:) } + + it 'does not create a resolution' do + expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.resolutions.pending.count } + end + end + + context 'when dossier has already a resolved resolution' do + before { create(:dossier_resolution, :resolved, dossier:) } + + it 'creates a resolution' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.resolutions.pending.count }.by(1) + end + end + + context 'when dossier is not en_construction and may not be repassed en_construction' do + let(:dossier) { create(:dossier, :accepte) } + + it 'does not create a resolution' do + expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.resolutions.pending.count } + end + end + end end diff --git a/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb b/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb index be44ec1ac..a1cf9193e 100644 --- a/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb @@ -29,15 +29,25 @@ describe 'instructeurs/dossiers/instruction_button', type: :view do end end + context 'en_construction' do + let(:dossier) { create(:dossier, :en_construction) } + + it 'renders a dropdown' do + expect(rendered).to have_dropdown_title('Demander une modification') + expect(rendered).to have_dropdown_items(count: 2) # form is already expanded so we have 2 visible items + end + end + context 'en_instruction' do let(:dossier) { create(:dossier, :en_instruction) } it 'renders a dropdown' do expect(rendered).to have_dropdown_title('Instruire le dossier') - expect(rendered).to have_dropdown_items(count: 3) + expect(rendered).to have_dropdown_items(count: 4) expect(rendered).to have_dropdown_item('Accepter') expect(rendered).to have_dropdown_item('Classer sans suite') expect(rendered).to have_dropdown_item('Refuser') + expect(rendered).to have_dropdown_item('Demander une modification') end end end diff --git a/spec/views/instructeur/dossiers/show.html.haml_spec.rb b/spec/views/instructeur/dossiers/show.html.haml_spec.rb index 945eb8f87..34a3a44f0 100644 --- a/spec/views/instructeur/dossiers/show.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/show.html.haml_spec.rb @@ -61,7 +61,8 @@ describe 'instructeurs/dossiers/show', type: :view do within("form[action=\"#{follow_instructeur_dossier_path(dossier.procedure, dossier)}\"]") do expect(subject).to have_button('Suivre le dossier') end - expect(subject).to have_selector('.header-actions ul:first-child .fr-btn', count: 2) + expect(subject).to have_button('Demander une modification') + expect(subject).to have_selector('.header-actions ul:first-child > li.instruction-button', count: 1) end end From 95652671700e0c5771f73d1841151bf1d3d41887 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 14 Mar 2023 18:48:19 +0100 Subject: [PATCH 06/24] feat(dossier): notify user by email about pending corrections --- app/controllers/instructeurs/dossiers_controller.rb | 2 +- app/mailers/dossier_mailer.rb | 13 +++++++++++++ app/mailers/notification_mailer.rb | 4 ++++ app/models/commentaire.rb | 12 ++++++++---- .../notify_pending_correction.html.haml | 13 +++++++++++++ .../dossier_mailer/notify_pending_correction/en.yml | 12 ++++++++++++ .../dossier_mailer/notify_pending_correction/fr.yml | 13 +++++++++++++ .../instructeurs/dossiers_controller_spec.rb | 12 +++++++++++- spec/mailers/previews/dossier_mailer_preview.rb | 4 ++++ 9 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 app/views/dossier_mailer/notify_pending_correction.html.haml create mode 100644 config/locales/views/dossier_mailer/notify_pending_correction/en.yml create mode 100644 config/locales/views/dossier_mailer/notify_pending_correction/fr.yml diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 53513e77b..c82e3c0c3 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -231,7 +231,7 @@ module Instructeurs elsif !dossier.may_flag_as_pending_correction? flash.alert = dossier.termine? ? "Impossible de demander de corriger un dossier terminé." : "Le dossier est déjà en attente de correction." else - commentaire = CommentaireService.create(current_instructeur, dossier, { body: message, piece_jointe: }) + commentaire = CommentaireService.build(current_instructeur, dossier, { body: message, piece_jointe: }) dossier.flag_as_pending_correction!(commentaire) dossier.update!(last_commentaire_updated_at: Time.zone.now) current_instructeur.follow(dossier) diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb index 81d42cf4f..24ebeab43 100644 --- a/app/mailers/dossier_mailer.rb +++ b/app/mailers/dossier_mailer.rb @@ -46,6 +46,19 @@ class DossierMailer < ApplicationMailer end end + def notify_pending_correction(dossier) + I18n.with_locale(dossier.user_locale) do + @dossier = dossier + @service = dossier.procedure.service + @logo_url = attach_logo(dossier.procedure) + @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) + + mail(to: dossier.user_email_for(:notification), subject: @subject) do |format| + format.html { render layout: 'mailers/notifications_layout' } + end + end + end + def notify_new_avis_to_instructeur(avis, instructeur_email) I18n.with_locale(avis.dossier.user_locale) do @avis = avis diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index d7293e290..c4bfdf593 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -50,6 +50,10 @@ class NotificationMailer < ApplicationMailer with(dossier: dossier, state: Dossier.states.fetch(:sans_suite)).send_notification end + def self.send_pending_correction(dossier) + with(dossier: dossier).send_notification + end + private def set_services_publics_plus diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index a3eecafed..a34c477c2 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -19,7 +19,7 @@ class Commentaire < ApplicationRecord belongs_to :instructeur, inverse_of: :commentaires, optional: true belongs_to :expert, inverse_of: :commentaires, optional: true - has_one :dossier_resolution, inverse_of: :commentaire, dependent: :nullify + has_one :dossier_correction, inverse_of: :commentaire, dependent: :nullify validate :messagerie_available?, on: :create, unless: -> { dossier.brouillon? } @@ -95,8 +95,8 @@ class Commentaire < ApplicationRecord update! body: '' end - def flagged_pending_corrections? - DossierResolution.exists?(commentaire: self) + def flagged_pending_correction? + DossierCorrection.exists?(commentaire: self) end private @@ -113,7 +113,11 @@ class Commentaire < ApplicationRecord end def notify_user(job_options = {}) - DossierMailer.with(commentaire: self).notify_new_answer.deliver_later(job_options) + if flagged_pending_correction? + DossierMailer.notify_pending_correction(dossier).deliver_later(job_options) + else + DossierMailer.with(commentaire: self).notify_new_answer.deliver_later(job_options) + end end def messagerie_available? diff --git a/app/views/dossier_mailer/notify_pending_correction.html.haml b/app/views/dossier_mailer/notify_pending_correction.html.haml new file mode 100644 index 000000000..6a576b6dd --- /dev/null +++ b/app/views/dossier_mailer/notify_pending_correction.html.haml @@ -0,0 +1,13 @@ +- content_for :procedure_logo do + = render 'layouts/mailers/logo', url: @logo_url + +%p= t(:hello, scope: [:views, :shared, :greetings]) + +%p= t('.explanation_html', dossier_id: @dossier.id, libelle_demarche: @dossier.procedure.libelle) +%p= t('.link') += round_button(t('.access_message'), messagerie_dossier_url(@dossier), :primary) + += render 'layouts/mailers/signature', service: @service + +- content_for :footer do + = render 'layouts/mailers/service_footer', service: @service, dossier: @dossier diff --git a/config/locales/views/dossier_mailer/notify_pending_correction/en.yml b/config/locales/views/dossier_mailer/notify_pending_correction/en.yml new file mode 100644 index 000000000..bd0d3938f --- /dev/null +++ b/config/locales/views/dossier_mailer/notify_pending_correction/en.yml @@ -0,0 +1,12 @@ +--- +en: + dossier_mailer: + notify_pending_correction: + subject: You need to modify your file no. %{dossier_id} « %{libelle_demarche} » + explanation_html: + In order to continue its instruction, an instructor asks you to edit information to your file no. %{dossier_id} of the « %{libelle_demarche} » procedure. + link: + Check your file's mailbox to see what changes need to be made, then edit the file directly on the website. + access_message: Open the mailbox + + diff --git a/config/locales/views/dossier_mailer/notify_pending_correction/fr.yml b/config/locales/views/dossier_mailer/notify_pending_correction/fr.yml new file mode 100644 index 000000000..b44025ce5 --- /dev/null +++ b/config/locales/views/dossier_mailer/notify_pending_correction/fr.yml @@ -0,0 +1,13 @@ +--- +fr: + dossier_mailer: + notify_pending_correction: + subject: Vous devez corriger votre dossier nº %{dossier_id} « %{libelle_demarche} » + explanation_html: + Afin de poursuivre son instruction, un instructeur vous demande d’apporter des corrections à votre dossier nº %{dossier_id} de la démarche « %{libelle_demarche} ». + link: + Consultez la messagerie de votre dossier pour prendre connaissance des modifications à effectuer, + puis modifiez le dossier directement sur le site. + access_message: Ouvrir la messagerie + + diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 5961564ec..fd25ce681 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -507,13 +507,23 @@ describe Instructeurs::DossiersController, type: :controller do }, format: :turbo_stream end - before { sign_in(instructeur.user) } + before do + sign_in(instructeur.user) + + allow(DossierMailer).to receive(:notify_pending_correction) + .and_return(double(deliver_later: nil)) + end context "dossier en instruction" do let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) } before { subject } + it 'sends an email to user' do + expect(DossierMailer).to have_received(:notify_pending_correction).once + expect(DossierMailer).to have_received(:notify_pending_correction).with(dossier) + end + it 'pass en_construction and create a pending resolution' do expect(response).to have_http_status(:ok) expect(response.body).to include('en attente de modifications') diff --git a/spec/mailers/previews/dossier_mailer_preview.rb b/spec/mailers/previews/dossier_mailer_preview.rb index 2ef877538..2066e4bf3 100644 --- a/spec/mailers/previews/dossier_mailer_preview.rb +++ b/spec/mailers/previews/dossier_mailer_preview.rb @@ -8,6 +8,10 @@ class DossierMailerPreview < ActionMailer::Preview DossierMailer.with(commentaire: commentaire(on: draft)).notify_new_answer end + def notify_pending_correction + DossierMailer.with(dossier: dossier_en_construction).notify_pending_correction + end + def notify_revert_to_instruction DossierMailer.notify_revert_to_instruction(dossier) end From 7a9917fb32daa87764daba70f55f6597ba7e386d Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Thu, 23 Mar 2023 12:03:33 +0100 Subject: [PATCH 07/24] style: user dossier state badges must be sized sm --- app/helpers/dossier_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/dossier_helper.rb b/app/helpers/dossier_helper.rb index b94ac46e5..5fb04f9c3 100644 --- a/app/helpers/dossier_helper.rb +++ b/app/helpers/dossier_helper.rb @@ -79,7 +79,7 @@ module DossierHelper def status_badge(state, alignment_class = '') status_text = dossier_display_state(state, lower: true) - tag.span(status_text, class: "fr-badge #{class_badge_state(state)} fr-badge--no-icon #{alignment_class}", role: 'status') + tag.span(status_text, class: "fr-badge fr-badge--sm #{class_badge_state(state)} fr-badge--no-icon #{alignment_class}", role: 'status') end def deletion_reason_badge(reason) From 538e24fa7eb3327fbe3c57cb90a48d09ed542d48 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 27 Mar 2023 16:23:11 +0200 Subject: [PATCH 08/24] feat(dossier/projection): supports pending_correction? --- app/services/dossier_projection_service.rb | 24 +++++++++++++++++-- .../dossier_projection_service_spec.rb | 23 ++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/app/services/dossier_projection_service.rb b/app/services/dossier_projection_service.rb index 4a32aae39..c7f97a3da 100644 --- a/app/services/dossier_projection_service.rb +++ b/app/services/dossier_projection_service.rb @@ -1,5 +1,11 @@ class DossierProjectionService - class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :columns) + class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :resolutions, :columns) do + def pending_correction? + return false if resolutions.blank? + + resolutions.any? { _1[:resolved_at].nil? } + end + end end TABLE = 'table' @@ -23,7 +29,8 @@ class DossierProjectionService batch_operation_field = { TABLE => 'self', COLUMN => 'batch_operation_id' } hidden_by_user_at_field = { TABLE => 'self', COLUMN => 'hidden_by_user_at' } hidden_by_administration_at_field = { TABLE => 'self', COLUMN => 'hidden_by_administration_at' } - ([state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field] + fields) # the view needs state and archived dossier attributes + dossier_resolutions = { TABLE => 'dossier_resolutions', COLUMN => 'resolved_at' } + ([state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, dossier_resolutions] + fields) # the view needs state and archived dossier attributes .each { |f| f[:id_value_h] = {} } .group_by { |f| f[TABLE] } # one query per table .each do |table, fields| @@ -76,6 +83,18 @@ class DossierProjectionService .where(id: dossiers_ids) .pluck('dossiers.id, groupe_instructeurs.label') .to_h + when 'dossier_resolutions' + columns = fields.map { _1[COLUMN].to_sym } + + id_value_h = DossierResolution.where(dossier_id: dossiers_ids) + .pluck(:dossier_id, *columns) + .group_by(&:first) # group resolutions by dossier_id + .transform_values do |values| # build each resolution has an hash column => value + values.map { Hash[columns.zip(_1[1..-1])] } + end + + fields[0][:id_value_h] = id_value_h + when 'procedure' Dossier .joins(:procedure) @@ -111,6 +130,7 @@ class DossierProjectionService hidden_by_user_at_field[:id_value_h][dossier_id], hidden_by_administration_at_field[:id_value_h][dossier_id], batch_operation_field[:id_value_h][dossier_id], + dossier_resolutions[:id_value_h][dossier_id], fields.map { |f| f[:id_value_h][dossier_id] } ) end diff --git a/spec/services/dossier_projection_service_spec.rb b/spec/services/dossier_projection_service_spec.rb index ea72f3ba3..cf11a1363 100644 --- a/spec/services/dossier_projection_service_spec.rb +++ b/spec/services/dossier_projection_service_spec.rb @@ -248,6 +248,29 @@ describe DossierProjectionService do it { is_expected.to eq("") } end end + + context 'for dossier reolutions table' do + let(:table) { 'dossier_resolutions' } + let(:column) { 'resolved_at' } + let(:dossier) { create(:dossier, :en_construction) } + subject { described_class.project(dossiers_ids, fields)[0] } + + context "when dossier has pending correction" do + before { create(:dossier_resolution, dossier:) } + + it { expect(subject.pending_correction?).to be(true) } + end + + context "when dossier has a resolved correction" do + before { create(:dossier_resolution, :resolved, dossier:) } + + it { expect(subject.pending_correction?).to eq(false) } + end + + context "when dossier has no correction at all" do + it { expect(subject.pending_correction?).to eq(false) } + end + end end end end From 36efb31728bd708335c7df8a8abdbd91c5b1b96e Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Thu, 23 Mar 2023 12:47:45 +0100 Subject: [PATCH 09/24] =?UTF-8?q?chore:=20badges=20"en=20attente"=20/=20"?= =?UTF-8?q?=C3=A0=20corriger"=20in=20dossiers=20list=20&=20headers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/stylesheets/dossiers_table.scss | 3 ++- app/helpers/dossier_helper.rb | 4 ++++ app/views/instructeurs/dossiers/_header_top.html.haml | 2 ++ app/views/instructeurs/procedures/show.html.haml | 7 +++---- app/views/users/dossiers/_dossiers_list.html.haml | 6 +++++- app/views/users/dossiers/show/_header.html.haml | 1 + config/locales/models/dossier/en.yml | 3 +++ config/locales/models/dossier/fr.yml | 3 +++ 8 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/dossiers_table.scss b/app/assets/stylesheets/dossiers_table.scss index 602e9c616..4f903da50 100644 --- a/app/assets/stylesheets/dossiers_table.scss +++ b/app/assets/stylesheets/dossiers_table.scss @@ -45,7 +45,8 @@ } } - .number-col { + .number-col, + .fr-badge { white-space: nowrap; } diff --git a/app/helpers/dossier_helper.rb b/app/helpers/dossier_helper.rb index 5fb04f9c3..c898cefc4 100644 --- a/app/helpers/dossier_helper.rb +++ b/app/helpers/dossier_helper.rb @@ -94,6 +94,10 @@ module DossierHelper tag.span(status_text, class: "label #{status_class} ") end + def pending_correction_badge(for_profile, html_class: nil) + tag.span(Dossier.human_attribute_name("pending_correction.#{for_profile}"), class: ['fr-badge fr-badge--sm fr-badge--warning super', html_class], role: 'status') + end + def demandeur_dossier(dossier) if dossier.procedure.for_individual? "#{dossier&.individual&.nom} #{dossier&.individual&.prenom}" diff --git a/app/views/instructeurs/dossiers/_header_top.html.haml b/app/views/instructeurs/dossiers/_header_top.html.haml index 27b8ac424..6889e0ed4 100644 --- a/app/views/instructeurs/dossiers/_header_top.html.haml +++ b/app/views/instructeurs/dossiers/_header_top.html.haml @@ -5,6 +5,8 @@ = "Dossier nº #{dossier.id}" = status_badge(dossier.state, 'super') + = pending_correction_badge(:for_instructeur) if dossier.pending_resolution? + = link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link" = procedure_badge(dossier.procedure) diff --git a/app/views/instructeurs/procedures/show.html.haml b/app/views/instructeurs/procedures/show.html.haml index 4678472e3..d5c62c75f 100644 --- a/app/views/instructeurs/procedures/show.html.haml +++ b/app/views/instructeurs/procedures/show.html.haml @@ -170,10 +170,9 @@ = "- #{t('views.instructeurs.dossiers.deleted_by_user')}" if p.hidden_by_user_at.present? %td.status-col - - if p.hidden_by_administration_at.present? - %span.cell-link= status_badge(p.state) - - else - %a.cell-link{ href: path }= status_badge(p.state) + - status = [status_badge(p.state)] + - status << pending_correction_badge(:for_instructeur, html_class: "fr-mt-1v") if p.pending_correction? + = link_to_if(p.hidden_by_administration_at.blank?, safe_join(status), path, class: class_names("cell-link": true, "fr-py-0": status.many?)) %td.action-col.follow-col %ul.inline.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline.fr-btns-group--icon-right diff --git a/app/views/users/dossiers/_dossiers_list.html.haml b/app/views/users/dossiers/_dossiers_list.html.haml index 2a5c2951b..4c5a7361a 100644 --- a/app/views/users/dossiers/_dossiers_list.html.haml +++ b/app/views/users/dossiers/_dossiers_list.html.haml @@ -37,7 +37,11 @@ %td %span.cell-link= demandeur_dossier(dossier) %td.status-col - = status_badge(dossier.state) + - if dossier.pending_correction? + = pending_correction_badge(:for_user) + - else + = status_badge(dossier.state) + %td.updated-at-col.cell-link = try_format_date(dossier.updated_at) %td.action-col.follow-col diff --git a/app/views/users/dossiers/show/_header.html.haml b/app/views/users/dossiers/show/_header.html.haml index 62836d088..485d93ed8 100644 --- a/app/views/users/dossiers/show/_header.html.haml +++ b/app/views/users/dossiers/show/_header.html.haml @@ -3,6 +3,7 @@ %h1 = dossier.procedure.libelle = status_badge(dossier.state, 'super') + = pending_correction_badge(:for_user) if dossier.pending_resolution? %h2 = t('views.users.dossiers.show.header.dossier_number', dossier_id: dossier.id) - if dossier.depose_at.present? diff --git a/config/locales/models/dossier/en.yml b/config/locales/models/dossier/en.yml index 77edfb763..6ed42b17d 100644 --- a/config/locales/models/dossier/en.yml +++ b/config/locales/models/dossier/en.yml @@ -15,6 +15,9 @@ en: accepte: "Accepted" refuse: "Refused" sans_suite: "No further action" + pending_correction: + for_instructeur: "pending" + for_user: "to be corrected" traitement: state: "State" traitement/state: diff --git a/config/locales/models/dossier/fr.yml b/config/locales/models/dossier/fr.yml index ba3638d05..d0db5795a 100644 --- a/config/locales/models/dossier/fr.yml +++ b/config/locales/models/dossier/fr.yml @@ -19,6 +19,9 @@ fr: accepte: "Accepté" refuse: "Refusé" sans_suite: "Classé sans suite" + pending_correction: + for_instructeur: "en attente" + for_user: "à corriger" traitement: state: "État" traitement/state: From b14a70abf7c0364ec68794d8e7d5f7e08408fb8a Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 27 Mar 2023 19:11:22 +0200 Subject: [PATCH 10/24] feat(instructeur): filter dossiers "en attente" --- app/models/procedure_presentation.rb | 8 +++++++- config/locales/views/instructeurs/fr.yml | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb index 194ef1519..b86606dbf 100644 --- a/app/models/procedure_presentation.rb +++ b/app/models/procedure_presentation.rb @@ -183,6 +183,8 @@ class ProcedurePresentation < ApplicationRecord .filter_map { |v| Time.zone.parse(v).beginning_of_day rescue nil } dossiers.filter_by_datetimes(column, dates) + elsif field['column'] == "state" && values.include?("pending_correction") + dossiers.joins(:resolutions).where(resolutions: DossierResolution.pending) else dossiers.where("dossiers.#{column} IN (?)", values) end @@ -245,7 +247,11 @@ class ProcedurePresentation < ApplicationRecord if [TYPE_DE_CHAMP, TYPE_DE_CHAMP_PRIVATE].include?(filter[TABLE]) find_type_de_champ(filter[COLUMN]).dynamic_type.filter_to_human(filter['value']) elsif filter['column'] == 'state' - Dossier.human_attribute_name("state.#{filter['value']}") + if filter['value'] == 'pending_correction' + Dossier.human_attribute_name("pending_correction.for_instructeur") + else + Dossier.human_attribute_name("state.#{filter['value']}") + end elsif filter['table'] == 'groupe_instructeur' && filter['column'] == 'id' instructeur.groupe_instructeurs .find { _1.id == filter['value'].to_i }&.label || filter['value'] diff --git a/config/locales/views/instructeurs/fr.yml b/config/locales/views/instructeurs/fr.yml index 372c9107b..9afb2b1ca 100644 --- a/config/locales/views/instructeurs/fr.yml +++ b/config/locales/views/instructeurs/fr.yml @@ -19,6 +19,7 @@ fr: classe_sans_suite: Le %{processed_at}, %{email} a classé ce dossier sans suite filterable_state: en_construction: "En construction" + pending_correction: "En attente" en_instruction: "En instruction" accepte: "Accepté" refuse: "Refusé" From 5ab44fc7a9b3678e6f80877d4b14f07d74d558f1 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Mon, 3 Apr 2023 17:05:54 +0200 Subject: [PATCH 11/24] chore: rename dossier_resolution => dossier_correction --- ...cern.rb => dossier_correctable_concern.rb} | 12 ++--- app/models/dossier.rb | 4 +- ...er_resolution.rb => dossier_correction.rb} | 4 +- app/models/procedure_presentation.rb | 2 +- app/services/dossier_projection_service.rb | 20 +++---- .../dossiers/_header_top.html.haml | 2 +- .../users/dossiers/show/_header.html.haml | 2 +- ...30228134859_create_dossier_corrections.rb} | 0 .../instructeurs/dossiers_controller_spec.rb | 20 +++---- ..._resolutions.rb => dossier_corrections.rb} | 2 +- ...rb => dossier_correctable_concern_spec.rb} | 52 +++++++++---------- ...ion_spec.rb => dossier_correction_spec.rb} | 2 +- .../dossier_projection_service_spec.rb | 6 +-- 13 files changed, 64 insertions(+), 64 deletions(-) rename app/models/concerns/{dossier_resolvable_concern.rb => dossier_correctable_concern.rb} (68%) rename app/models/{dossier_resolution.rb => dossier_correction.rb} (82%) rename db/migrate/{20230228134859_create_dossier_resolutions.rb => 20230228134859_create_dossier_corrections.rb} (100%) rename spec/factories/{dossier_resolutions.rb => dossier_corrections.rb} (82%) rename spec/models/concern/{dossier_resolvable_concern_spec.rb => dossier_correctable_concern_spec.rb} (52%) rename spec/models/{dossier_resolution_spec.rb => dossier_correction_spec.rb} (62%) diff --git a/app/models/concerns/dossier_resolvable_concern.rb b/app/models/concerns/dossier_correctable_concern.rb similarity index 68% rename from app/models/concerns/dossier_resolvable_concern.rb rename to app/models/concerns/dossier_correctable_concern.rb index f4d827b56..cc9c9bce3 100644 --- a/app/models/concerns/dossier_resolvable_concern.rb +++ b/app/models/concerns/dossier_correctable_concern.rb @@ -1,13 +1,13 @@ -module DossierResolvableConcern +module DossierCorrectableConcern extend ActiveSupport::Concern included do - has_many :resolutions, class_name: 'DossierResolution', dependent: :destroy + has_many :corrections, class_name: 'DossierCorrection', dependent: :destroy def flag_as_pending_correction!(commentaire) return unless may_flag_as_pending_correction? - resolutions.create(commentaire:) + corrections.create(commentaire:) return if en_construction? @@ -15,16 +15,16 @@ module DossierResolvableConcern end def may_flag_as_pending_correction? - return false if resolutions.pending.exists? + return false if corrections.pending.exists? en_construction? || may_repasser_en_construction? end - def pending_resolution? + def pending_correction? # We don't want to show any alert if user is not allowed to modify the dossier return false unless en_construction? - resolutions.pending.exists? + corrections.pending.exists? end end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 2e48629d5..8b2054d3b 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -47,13 +47,13 @@ # user_id :integer # class Dossier < ApplicationRecord + include DossierCloneConcern + include DossierCorrectableConcern include DossierFilteringConcern include DossierPrefillableConcern include DossierRebaseConcern - include DossierResolvableConcern include DossierSearchableConcern include DossierSectionsConcern - include DossierCloneConcern enum state: { brouillon: 'brouillon', diff --git a/app/models/dossier_resolution.rb b/app/models/dossier_correction.rb similarity index 82% rename from app/models/dossier_resolution.rb rename to app/models/dossier_correction.rb index 706d65367..95d16bce6 100644 --- a/app/models/dossier_resolution.rb +++ b/app/models/dossier_correction.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: dossier_resolutions +# Table name: dossier_corrections # # id :bigint not null, primary key # resolved_at :datetime @@ -9,7 +9,7 @@ # commentaire_id :bigint # dossier_id :bigint not null # -class DossierResolution < ApplicationRecord +class DossierCorrection < ApplicationRecord belongs_to :dossier belongs_to :commentaire diff --git a/app/models/procedure_presentation.rb b/app/models/procedure_presentation.rb index b86606dbf..42e47398d 100644 --- a/app/models/procedure_presentation.rb +++ b/app/models/procedure_presentation.rb @@ -184,7 +184,7 @@ class ProcedurePresentation < ApplicationRecord dossiers.filter_by_datetimes(column, dates) elsif field['column'] == "state" && values.include?("pending_correction") - dossiers.joins(:resolutions).where(resolutions: DossierResolution.pending) + dossiers.joins(:corrections).where(corrections: DossierCorrection.pending) else dossiers.where("dossiers.#{column} IN (?)", values) end diff --git a/app/services/dossier_projection_service.rb b/app/services/dossier_projection_service.rb index c7f97a3da..a5e348ad6 100644 --- a/app/services/dossier_projection_service.rb +++ b/app/services/dossier_projection_service.rb @@ -1,9 +1,9 @@ class DossierProjectionService - class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :resolutions, :columns) do + class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :corrections, :columns) do def pending_correction? - return false if resolutions.blank? + return false if corrections.blank? - resolutions.any? { _1[:resolved_at].nil? } + corrections.any? { _1[:resolved_at].nil? } end end end @@ -29,8 +29,8 @@ class DossierProjectionService batch_operation_field = { TABLE => 'self', COLUMN => 'batch_operation_id' } hidden_by_user_at_field = { TABLE => 'self', COLUMN => 'hidden_by_user_at' } hidden_by_administration_at_field = { TABLE => 'self', COLUMN => 'hidden_by_administration_at' } - dossier_resolutions = { TABLE => 'dossier_resolutions', COLUMN => 'resolved_at' } - ([state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, dossier_resolutions] + fields) # the view needs state and archived dossier attributes + dossier_corrections = { TABLE => 'dossier_corrections', COLUMN => 'resolved_at' } + ([state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, dossier_corrections] + fields) # the view needs state and archived dossier attributes .each { |f| f[:id_value_h] = {} } .group_by { |f| f[TABLE] } # one query per table .each do |table, fields| @@ -83,13 +83,13 @@ class DossierProjectionService .where(id: dossiers_ids) .pluck('dossiers.id, groupe_instructeurs.label') .to_h - when 'dossier_resolutions' + when 'dossier_corrections' columns = fields.map { _1[COLUMN].to_sym } - id_value_h = DossierResolution.where(dossier_id: dossiers_ids) + id_value_h = DossierCorrection.where(dossier_id: dossiers_ids) .pluck(:dossier_id, *columns) - .group_by(&:first) # group resolutions by dossier_id - .transform_values do |values| # build each resolution has an hash column => value + .group_by(&:first) # group corrections by dossier_id + .transform_values do |values| # build each correction has an hash column => value values.map { Hash[columns.zip(_1[1..-1])] } end @@ -130,7 +130,7 @@ class DossierProjectionService hidden_by_user_at_field[:id_value_h][dossier_id], hidden_by_administration_at_field[:id_value_h][dossier_id], batch_operation_field[:id_value_h][dossier_id], - dossier_resolutions[:id_value_h][dossier_id], + dossier_corrections[:id_value_h][dossier_id], fields.map { |f| f[:id_value_h][dossier_id] } ) end diff --git a/app/views/instructeurs/dossiers/_header_top.html.haml b/app/views/instructeurs/dossiers/_header_top.html.haml index 6889e0ed4..4537515f2 100644 --- a/app/views/instructeurs/dossiers/_header_top.html.haml +++ b/app/views/instructeurs/dossiers/_header_top.html.haml @@ -5,7 +5,7 @@ = "Dossier nº #{dossier.id}" = status_badge(dossier.state, 'super') - = pending_correction_badge(:for_instructeur) if dossier.pending_resolution? + = pending_correction_badge(:for_instructeur) if dossier.pending_correction? = link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link" = procedure_badge(dossier.procedure) diff --git a/app/views/users/dossiers/show/_header.html.haml b/app/views/users/dossiers/show/_header.html.haml index 485d93ed8..e5544c01a 100644 --- a/app/views/users/dossiers/show/_header.html.haml +++ b/app/views/users/dossiers/show/_header.html.haml @@ -3,7 +3,7 @@ %h1 = dossier.procedure.libelle = status_badge(dossier.state, 'super') - = pending_correction_badge(:for_user) if dossier.pending_resolution? + = pending_correction_badge(:for_user) if dossier.pending_correction? %h2 = t('views.users.dossiers.show.header.dossier_number', dossier_id: dossier.id) - if dossier.depose_at.present? diff --git a/db/migrate/20230228134859_create_dossier_resolutions.rb b/db/migrate/20230228134859_create_dossier_corrections.rb similarity index 100% rename from db/migrate/20230228134859_create_dossier_resolutions.rb rename to db/migrate/20230228134859_create_dossier_corrections.rb diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index fd25ce681..74eb24daa 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -524,12 +524,12 @@ describe Instructeurs::DossiersController, type: :controller do expect(DossierMailer).to have_received(:notify_pending_correction).with(dossier) end - it 'pass en_construction and create a pending resolution' do + it 'pass en_construction and create a pending correction' do expect(response).to have_http_status(:ok) expect(response.body).to include('en attente de modifications') expect(dossier.reload).to be_en_construction - expect(dossier).to be_pending_resolution + expect(dossier).to be_pending_correction end it 'create a comment with text body' do @@ -549,7 +549,7 @@ describe Instructeurs::DossiersController, type: :controller do let(:message) { '' } it 'requires a message' do - expect(dossier.reload).not_to be_pending_resolution + expect(dossier.reload).not_to be_pending_correction expect(dossier.commentaires.count).to eq(0) expect(response.body).to include('Vous devez préciser') end @@ -557,11 +557,11 @@ describe Instructeurs::DossiersController, type: :controller do context 'dossier already having pending corrections' do before do - create(:dossier_resolution, dossier:) + create(:dossier_correction, dossier:) end - it 'does not create an new pending resolution' do - expect { subject }.not_to change { DossierResolution.count } + it 'does not create an new pending correction' do + expect { subject }.not_to change { DossierCorrection.count } end it 'shows a flash alert' do @@ -573,9 +573,9 @@ describe Instructeurs::DossiersController, type: :controller do end context 'dossier en_construction' do - it 'can create a pending resolution' do + it 'can create a pending correction' do subject - expect(dossier.reload).to be_pending_resolution + expect(dossier.reload).to be_pending_correction expect(dossier.commentaires.last).to be_flagged_pending_corrections end end @@ -583,8 +583,8 @@ describe Instructeurs::DossiersController, type: :controller do context 'dossier is termine' do let(:dossier) { create(:dossier, :accepte, :with_individual, procedure: procedure) } - it 'does not create a pending resolution' do - expect { subject }.not_to change { DossierResolution.count } + it 'does not create a pending correction' do + expect { subject }.not_to change { DossierCorrection.count } expect(response.body).to include('Impossible') end end diff --git a/spec/factories/dossier_resolutions.rb b/spec/factories/dossier_corrections.rb similarity index 82% rename from spec/factories/dossier_resolutions.rb rename to spec/factories/dossier_corrections.rb index 0bc95ec54..a3f88a9f5 100644 --- a/spec/factories/dossier_resolutions.rb +++ b/spec/factories/dossier_corrections.rb @@ -1,5 +1,5 @@ FactoryBot.define do - factory :dossier_resolution do + factory :dossier_correction do dossier commentaire resolved_at { nil } diff --git a/spec/models/concern/dossier_resolvable_concern_spec.rb b/spec/models/concern/dossier_correctable_concern_spec.rb similarity index 52% rename from spec/models/concern/dossier_resolvable_concern_spec.rb rename to spec/models/concern/dossier_correctable_concern_spec.rb index d02b0b539..15e6df6ab 100644 --- a/spec/models/concern/dossier_resolvable_concern_spec.rb +++ b/spec/models/concern/dossier_correctable_concern_spec.rb @@ -1,28 +1,28 @@ -describe DossierResolvableConcern do - describe "#pending_resolution?" do +describe DossierCorrectableConcern do + describe "#pending_correction?" do let(:dossier) { create(:dossier, :en_construction) } - context "when dossier has no resolution" do - it { expect(dossier.pending_resolution?).to be_falsey } + context "when dossier has no correction" do + it { expect(dossier.pending_correction?).to be_falsey } end - context "when dossier has a pending resolution" do - before { create(:dossier_resolution, dossier:) } + context "when dossier has a pending correction" do + before { create(:dossier_correction, dossier:) } - it { expect(dossier.pending_resolution?).to be_truthy } + it { expect(dossier.pending_correction?).to be_truthy } end - context "when dossier has a resolved resolution" do - before { create(:dossier_resolution, :resolved, dossier:) } + context "when dossier has a resolved correction" do + before { create(:dossier_correction, :resolved, dossier:) } - it { expect(dossier.pending_resolution?).to be_falsey } + it { expect(dossier.pending_correction?).to be_falsey } end context "when dossier is not en_construction" do let(:dossier) { create(:dossier, :en_instruction) } - before { create(:dossier_resolution, dossier:) } + before { create(:dossier_correction, dossier:) } - it { expect(dossier.pending_resolution?).to be_falsey } + it { expect(dossier.pending_correction?).to be_falsey } end end @@ -32,8 +32,8 @@ describe DossierResolvableConcern do let(:commentaire) { create(:commentaire, dossier:, instructeur:) } context 'when dossier is en_construction' do - it 'creates a resolution' do - expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.resolutions.pending.count }.by(1) + it 'creates a correction' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.corrections.pending.count }.by(1) end it 'does not change dossier state' do @@ -44,8 +44,8 @@ describe DossierResolvableConcern do context 'when dossier is not en_instruction' do let(:dossier) { create(:dossier, :en_instruction) } - it 'creates a resolution' do - expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.resolutions.pending.count }.by(1) + it 'creates a correction' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.corrections.pending.count }.by(1) end it 'repasse dossier en_construction' do @@ -53,27 +53,27 @@ describe DossierResolvableConcern do end end - context 'when dossier has already a pending resolution' do - before { create(:dossier_resolution, dossier:) } + context 'when dossier has already a pending correction' do + before { create(:dossier_correction, dossier:) } - it 'does not create a resolution' do - expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.resolutions.pending.count } + it 'does not create a correction' do + expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.corrections.pending.count } end end - context 'when dossier has already a resolved resolution' do - before { create(:dossier_resolution, :resolved, dossier:) } + context 'when dossier has already a resolved correction' do + before { create(:dossier_correction, :resolved, dossier:) } - it 'creates a resolution' do - expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.resolutions.pending.count }.by(1) + it 'creates a correction' do + expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.corrections.pending.count }.by(1) end end context 'when dossier is not en_construction and may not be repassed en_construction' do let(:dossier) { create(:dossier, :accepte) } - it 'does not create a resolution' do - expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.resolutions.pending.count } + it 'does not create a correction' do + expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.corrections.pending.count } end end end diff --git a/spec/models/dossier_resolution_spec.rb b/spec/models/dossier_correction_spec.rb similarity index 62% rename from spec/models/dossier_resolution_spec.rb rename to spec/models/dossier_correction_spec.rb index 8acd68e8e..42dac10e8 100644 --- a/spec/models/dossier_resolution_spec.rb +++ b/spec/models/dossier_correction_spec.rb @@ -1,5 +1,5 @@ require 'rails_helper' -RSpec.describe DossierResolution, type: :model do +RSpec.describe DossierCorrection, type: :model do pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/services/dossier_projection_service_spec.rb b/spec/services/dossier_projection_service_spec.rb index cf11a1363..c61c12f73 100644 --- a/spec/services/dossier_projection_service_spec.rb +++ b/spec/services/dossier_projection_service_spec.rb @@ -250,19 +250,19 @@ describe DossierProjectionService do end context 'for dossier reolutions table' do - let(:table) { 'dossier_resolutions' } + let(:table) { 'dossier_corrections' } let(:column) { 'resolved_at' } let(:dossier) { create(:dossier, :en_construction) } subject { described_class.project(dossiers_ids, fields)[0] } context "when dossier has pending correction" do - before { create(:dossier_resolution, dossier:) } + before { create(:dossier_correction, dossier:) } it { expect(subject.pending_correction?).to be(true) } end context "when dossier has a resolved correction" do - before { create(:dossier_resolution, :resolved, dossier:) } + before { create(:dossier_correction, :resolved, dossier:) } it { expect(subject.pending_correction?).to eq(false) } end From 62cc9d30d899e87bf9d75aeea43f5162214528b4 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 4 Apr 2023 10:29:27 +0200 Subject: [PATCH 12/24] chore(correction): passing en_instruction resolve pending corrections --- .../concerns/dossier_correctable_concern.rb | 4 ++++ app/models/dossier.rb | 2 ++ .../dossier_correctable_concern_spec.rb | 23 +++++++++++++++++++ spec/models/dossier_spec.rb | 6 +++++ 4 files changed, 35 insertions(+) diff --git a/app/models/concerns/dossier_correctable_concern.rb b/app/models/concerns/dossier_correctable_concern.rb index cc9c9bce3..6b5b38d45 100644 --- a/app/models/concerns/dossier_correctable_concern.rb +++ b/app/models/concerns/dossier_correctable_concern.rb @@ -26,5 +26,9 @@ module DossierCorrectableConcern corrections.pending.exists? end + + def resolve_pending_correction! + corrections.pending.update(resolved_at: Time.current) + end end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 8b2054d3b..5edc3c65b 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -891,6 +891,8 @@ class Dossier < ApplicationRecord .processed_at save! + resolve_pending_correction! + if !disable_notification NotificationMailer.send_en_instruction_notification(self).deliver_later end diff --git a/spec/models/concern/dossier_correctable_concern_spec.rb b/spec/models/concern/dossier_correctable_concern_spec.rb index 15e6df6ab..430696505 100644 --- a/spec/models/concern/dossier_correctable_concern_spec.rb +++ b/spec/models/concern/dossier_correctable_concern_spec.rb @@ -77,4 +77,27 @@ describe DossierCorrectableConcern do end end end + + describe "#resolve_pending_correction!" do + let(:dossier) { create(:dossier, :en_construction) } + + subject(:resolve) { dossier.resolve_pending_correction! } + context "when dossier has no correction" do + it { expect { resolve }.not_to change { dossier.corrections.pending.count } } + end + + context "when dossier has a pending correction" do + let!(:correction) { create(:dossier_correction, dossier:) } + + it { + expect { resolve }.to change { correction.reload.resolved_at }.from(nil) + } + end + + context "when dossier has a already resolved correction" do + before { create(:dossier_correction, :resolved, dossier:) } + + it { expect { resolve }.not_to change { dossier.corrections.pending.count } } + end + end end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index c17ae2d7b..6673a1a35 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -1044,6 +1044,7 @@ describe Dossier do let(:last_operation) { dossier.dossier_operation_logs.last } let(:operation_serialized) { last_operation.data } let(:instructeur) { create(:instructeur) } + let!(:correction) { create(:dossier_correction, dossier:) } before { dossier.passer_en_instruction!(instructeur: instructeur) } @@ -1055,6 +1056,11 @@ describe Dossier do it { expect(operation_serialized['operation']).to eq('passer_en_instruction') } it { expect(operation_serialized['dossier_id']).to eq(dossier.id) } it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) } + + it "resolve pending correction" do + expect(dossier.pending_correction?).to be_falsey + expect(correction.reload.resolved_at).to be_present + end end describe '#passer_automatiquement_en_instruction!' do From 8839ac7f764cf8f26a5473c8883e782ede24140f Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 4 Apr 2023 10:32:36 +0200 Subject: [PATCH 13/24] fix(instructeur): pending_correction flag does not make messagerie unseen --- app/controllers/instructeurs/dossiers_controller.rb | 2 +- spec/controllers/instructeurs/dossiers_controller_spec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index c82e3c0c3..219b350aa 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -13,7 +13,7 @@ module Instructeurs before_action :redirect_on_dossier_in_batch_operation, only: [:archive, :unarchive, :follow, :unfollow, :passer_en_instruction, :repasser_en_construction, :repasser_en_instruction, :terminer, :restore, :destroy, :extend_conservation] after_action :mark_demande_as_read, only: :show - after_action :mark_messagerie_as_read, only: [:messagerie, :create_commentaire] + after_action :mark_messagerie_as_read, only: [:messagerie, :create_commentaire, :pending_corrections] after_action :mark_avis_as_read, only: [:avis, :create_avis] after_action :mark_annotations_privees_as_read, only: [:annotations_privees, :update_annotations] diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 74eb24daa..13f3be510 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -512,6 +512,8 @@ describe Instructeurs::DossiersController, type: :controller do allow(DossierMailer).to receive(:notify_pending_correction) .and_return(double(deliver_later: nil)) + + expect(controller.current_instructeur).to receive(:mark_tab_as_seen).with(dossier, :messagerie) end context "dossier en instruction" do From a32014d4fd6b92c81a36fafc04162223826aa117 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 11 Apr 2023 18:31:16 +0200 Subject: [PATCH 14/24] feat(correction): user can mark dossier en_construction as resolved --- app/assets/stylesheets/forms.scss | 2 +- app/components/dossiers/edit_footer_component.rb | 3 ++- app/controllers/users/dossiers_controller.rb | 4 ++++ app/views/shared/dossiers/_edit.html.haml | 8 +++++++- config/locales/en.yml | 2 ++ config/locales/fr.yml | 2 ++ spec/controllers/users/dossiers_controller_spec.rb | 10 ++++++++++ 7 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index 2974a2fae..d6e7d944f 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -30,7 +30,7 @@ color: $dark-red; } - label, + label:not(.fr-label), legend.form-label { font-size: 18px; margin-bottom: $default-padding; diff --git a/app/components/dossiers/edit_footer_component.rb b/app/components/dossiers/edit_footer_component.rb index 0e7fc0128..fca7fab45 100644 --- a/app/components/dossiers/edit_footer_component.rb +++ b/app/components/dossiers/edit_footer_component.rb @@ -27,7 +27,8 @@ class Dossiers::EditFooterComponent < ApplicationComponent { class: 'fr-btn fr-btn--sm', method: :post, - data: { 'disable-with': t('.submitting'), controller: 'autosave-submit' } + data: { 'disable-with': t('.submitting'), controller: 'autosave-submit' }, + form: { id: "form-submit-en-construction" } } end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 36bb97fc1..3f979c4c2 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -227,6 +227,10 @@ module Users editing_fork_origin.merge_fork(@dossier) RoutingEngine.compute(editing_fork_origin) + if cast_bool(params.dig(:dossier, :pending_correction_confirm)) + editing_fork_origin.resolve_pending_correction! + end + redirect_to dossier_path(editing_fork_origin) else flash.now.alert = errors diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml index 083c9b78e..1f1dbefcd 100644 --- a/app/views/shared/dossiers/_edit.html.haml +++ b/app/views/shared/dossiers/_edit.html.haml @@ -39,6 +39,12 @@ dossier.procedure.groupe_instructeurs.active.map { |gi| [gi.label, gi.id] }, { include_blank: dossier.brouillon? } - = render EditableChamp::SectionComponent.new(champs: dossier_for_editing.champs_public) + + - if dossier.pending_correction? + .fr-checkbox-group.fr-my-3w + = check_box_tag field_name(:dossier, :pending_correction_confirm), "1", false, form: "form-submit-en-construction" + %label.fr-label{ for: :dossier_pending_correction_confirm }= t('views.shared.dossiers.edit.pending_correction.confirm_label') + + = render Dossiers::EditFooterComponent.new(dossier: dossier_for_editing, annotation: false) diff --git a/config/locales/en.yml b/config/locales/en.yml index a60b14563..36cf3ee11 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -332,6 +332,8 @@ en: autosave: Your file is automatically saved after each modification. You can close the window at any time and pick up where you left off later. notice: "Download the notice of the procedure" notice_title: "To help you complete your file, you can consult the notice to this procedure." + pending_correction: + confirm_label: I certify that I have made all corrections requested by the administration. messages: form: send_message: "Send message" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index ef73f6053..210631de9 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -332,6 +332,8 @@ fr: autosave: Votre dossier est enregistré automatiquement après chaque modification. Vous pouvez à tout moment fermer la fenêtre et reprendre plus tard là où vous en étiez. notice: Télécharger le guide de la démarche notice_title: "Pour vous aider à remplir votre dossier, vous pouvez consulter le guide de cette démarche." + pending_correction: + confirm_label: Je certifie avoir effectué toutes les corrections demandées par l’administration. messages: form: send_message: "Envoyer le message" diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 7e04f3931..eddcc7732 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -513,6 +513,16 @@ describe Users::DossiersController, type: :controller do expect(flash.alert).to eq("Les modifications ont déjà été déposées") end end + + context "when there are pending correction" do + let!(:correction) { create(:dossier_correction, dossier: dossier) } + + subject { post :submit_en_construction, params: { id: dossier.id, dossier: { pending_correction_confirm: "1" } } } + + it "resolve correction" do + expect { subject }.to change { correction.reload.resolved_at }.to be_truthy + end + end end describe '#update brouillon' do From 734d88d80d24f150e51b90fbd037b27ecc668ee7 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Thu, 13 Apr 2023 19:10:09 +0200 Subject: [PATCH 15/24] fix(pending-correction): wording alignement --- .../message_component/message_component.en.yml | 1 - .../message_component/message_component.fr.yml | 1 - .../message_component/message_component.html.haml | 5 ++--- app/controllers/instructeurs/dossiers_controller.rb | 6 +++--- .../dossiers/_instruction_button.html.haml | 12 ++++++------ .../dossier_mailer/notify_pending_correction/en.yml | 2 +- config/routes.rb | 2 +- .../instructeurs/dossiers_controller_spec.rb | 10 +++++----- spec/models/dossier_correction_spec.rb | 5 ----- spec/services/dossier_projection_service_spec.rb | 2 +- .../dossiers/_instruction_button.html.haml_spec.rb | 4 ++-- .../instructeur/dossiers/show.html.haml_spec.rb | 2 +- 12 files changed, 22 insertions(+), 30 deletions(-) delete mode 100644 spec/models/dossier_correction_spec.rb diff --git a/app/components/dossiers/message_component/message_component.en.yml b/app/components/dossiers/message_component/message_component.en.yml index 22ac79059..a24af5c49 100644 --- a/app/components/dossiers/message_component/message_component.en.yml +++ b/app/components/dossiers/message_component/message_component.en.yml @@ -7,4 +7,3 @@ en: automatic_email: Automatic email you: You deleted_body: Message deleted - flagged_pending_corrections: Modification requested diff --git a/app/components/dossiers/message_component/message_component.fr.yml b/app/components/dossiers/message_component/message_component.fr.yml index af808590c..4386b2ea2 100644 --- a/app/components/dossiers/message_component/message_component.fr.yml +++ b/app/components/dossiers/message_component/message_component.fr.yml @@ -7,4 +7,3 @@ fr: automatic_email: Email automatique you: Vous deleted_body: Message supprimé - flagged_pending_corrections: Modification demandée diff --git a/app/components/dossiers/message_component/message_component.html.haml b/app/components/dossiers/message_component/message_component.html.haml index 992c60b45..1937b2492 100644 --- a/app/components/dossiers/message_component/message_component.html.haml +++ b/app/components/dossiers/message_component/message_component.html.haml @@ -6,9 +6,8 @@ = commentaire_issuer - if commentaire_from_guest? %span.fr-text--xs.fr-text-mention--grey.font-weight-normal= t('.guest') - - if commentaire.flagged_pending_corrections? - %span.fr-badge.fr-badge--sm.fr-badge--info - = t('.flagged_pending_corrections') + - if commentaire.flagged_pending_correction? + = helpers.pending_correction_badge(connected_user.is_a?(Instructeur) ? :for_instructeur : :for_user) %span.date{ class: ["fr-text--xs", "fr-text-mention--grey", "font-weight-normal", highlight_if_unseen_class], data: scroll_to_target } = commentaire_date .rich-text diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 219b350aa..9970a45d2 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -13,7 +13,7 @@ module Instructeurs before_action :redirect_on_dossier_in_batch_operation, only: [:archive, :unarchive, :follow, :unfollow, :passer_en_instruction, :repasser_en_construction, :repasser_en_instruction, :terminer, :restore, :destroy, :extend_conservation] after_action :mark_demande_as_read, only: :show - after_action :mark_messagerie_as_read, only: [:messagerie, :create_commentaire, :pending_corrections] + after_action :mark_messagerie_as_read, only: [:messagerie, :create_commentaire, :pending_correction] after_action :mark_avis_as_read, only: [:avis, :create_avis] after_action :mark_annotations_privees_as_read, only: [:annotations_privees, :update_annotations] @@ -223,11 +223,11 @@ module Instructeurs render :change_state end - def pending_corrections + def pending_correction message, piece_jointe = params.require(:dossier).permit(:motivation, :justificatif_motivation).values if message.empty? - flash.alert = "Vous devez préciser quelle modification est attendue." + flash.alert = "Vous devez préciser quelle correction est attendue." elsif !dossier.may_flag_as_pending_correction? flash.alert = dossier.termine? ? "Impossible de demander de corriger un dossier terminé." : "Le dossier est déjà en attente de correction." else diff --git a/app/views/instructeurs/dossiers/_instruction_button.html.haml b/app/views/instructeurs/dossiers/_instruction_button.html.haml index c8ed93ca5..0296ec8cc 100644 --- a/app/views/instructeurs/dossiers/_instruction_button.html.haml +++ b/app/views/instructeurs/dossiers/_instruction_button.html.haml @@ -1,7 +1,7 @@ - if dossier.en_instruction? || (dossier.en_construction? && dossier.may_flag_as_pending_correction?) = render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: { turbo_force: :server } }, button_options: { class: [button_or_label_class(dossier)]}, role: dossier.en_instruction? ? :region : :menu) do |menu| - menu.with_button_inner_html do - = dossier.en_instruction? ? "Instruire le dossier" : "Demander une modification" + = dossier.en_instruction? ? "Instruire le dossier" : "Demander une correction" - if dossier.en_instruction? - menu.with_item do @@ -37,18 +37,18 @@ - if dossier.may_flag_as_pending_correction? - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'pending_corrections');", role: 'menuitem') do + = link_to('#', onclick: "DS.showMotivation(event, 'pending_correction');", role: 'menuitem') do %span.fr-icon.fr-icon-error-warning-line.fr-text-default--info{ "aria-hidden": "true" } .dropdown-description - %h4 Demander une modification + %h4 Demander une correction L’usager sera informé que des modifications sont attendues - menu.with_item(class: class_names("inactive form-inside": true, hidden: dossier.en_instruction?)) do = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, visible: !dossier.en_instruction?, - form_path: pending_corrections_instructeur_dossier_path(dossier.procedure, dossier), - placeholder: 'Expliquez au demandeur quelles modifications sont attendues', - popup_class: 'pending_corrections', + form_path: pending_correction_instructeur_dossier_path(dossier.procedure, dossier), + placeholder: 'Expliquez au demandeur quelle(s) correction(s) sont attendues', + popup_class: 'pending_correction', button_justificatif_label: "Ajouter une pièce jointe (facultatif)", process_button: dossier.en_construction? ? 'Valider' : 'Valider et repasser en construction', process_action: nil, diff --git a/config/locales/views/dossier_mailer/notify_pending_correction/en.yml b/config/locales/views/dossier_mailer/notify_pending_correction/en.yml index bd0d3938f..c2bca39ce 100644 --- a/config/locales/views/dossier_mailer/notify_pending_correction/en.yml +++ b/config/locales/views/dossier_mailer/notify_pending_correction/en.yml @@ -4,7 +4,7 @@ en: notify_pending_correction: subject: You need to modify your file no. %{dossier_id} « %{libelle_demarche} » explanation_html: - In order to continue its instruction, an instructor asks you to edit information to your file no. %{dossier_id} of the « %{libelle_demarche} » procedure. + In order to continue its instruction, an instructor requested you to edit information to your file no. %{dossier_id} of the « %{libelle_demarche} » procedure. link: Check your file's mailbox to see what changes need to be made, then edit the file directly on the website. access_message: Open the mailbox diff --git a/config/routes.rb b/config/routes.rb index 91d193c75..4f5efd627 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -445,7 +445,7 @@ Rails.application.routes.draw do post 'repasser-en-construction' => 'dossiers#repasser_en_construction' post 'repasser-en-instruction' => 'dossiers#repasser_en_instruction' post 'terminer' - post 'pending_corrections' + post 'pending_correction' post 'send-to-instructeurs' => 'dossiers#send_to_instructeurs' post 'avis' => 'dossiers#create_avis' get 'print' => 'dossiers#print' diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 13f3be510..968c859d2 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -496,12 +496,12 @@ describe Instructeurs::DossiersController, type: :controller do end end - describe '#pending_corrections' do + describe '#pending_correction' do let(:message) { 'do that' } let(:justificatif) { nil } subject do - post :pending_corrections, params: { + post :pending_correction, params: { procedure_id: procedure.id, dossier_id: dossier.id, dossier: { motivation: message, justificatif_motivation: justificatif } }, format: :turbo_stream @@ -528,7 +528,7 @@ describe Instructeurs::DossiersController, type: :controller do it 'pass en_construction and create a pending correction' do expect(response).to have_http_status(:ok) - expect(response.body).to include('en attente de modifications') + expect(response.body).to include('en attente de correction') expect(dossier.reload).to be_en_construction expect(dossier).to be_pending_correction @@ -536,7 +536,7 @@ describe Instructeurs::DossiersController, type: :controller do it 'create a comment with text body' do expect(dossier.commentaires.last.body).to eq("do that") - expect(dossier.commentaires.last).to be_flagged_pending_corrections + expect(dossier.commentaires.last).to be_flagged_pending_correction end context 'with an attachment' do @@ -578,7 +578,7 @@ describe Instructeurs::DossiersController, type: :controller do it 'can create a pending correction' do subject expect(dossier.reload).to be_pending_correction - expect(dossier.commentaires.last).to be_flagged_pending_corrections + expect(dossier.commentaires.last).to be_flagged_pending_correction end end diff --git a/spec/models/dossier_correction_spec.rb b/spec/models/dossier_correction_spec.rb deleted file mode 100644 index 42dac10e8..000000000 --- a/spec/models/dossier_correction_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe DossierCorrection, type: :model do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/services/dossier_projection_service_spec.rb b/spec/services/dossier_projection_service_spec.rb index c61c12f73..8b0234e2e 100644 --- a/spec/services/dossier_projection_service_spec.rb +++ b/spec/services/dossier_projection_service_spec.rb @@ -249,7 +249,7 @@ describe DossierProjectionService do end end - context 'for dossier reolutions table' do + context 'for dossier corrections table' do let(:table) { 'dossier_corrections' } let(:column) { 'resolved_at' } let(:dossier) { create(:dossier, :en_construction) } diff --git a/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb b/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb index a1cf9193e..e8e787609 100644 --- a/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb @@ -33,7 +33,7 @@ describe 'instructeurs/dossiers/instruction_button', type: :view do let(:dossier) { create(:dossier, :en_construction) } it 'renders a dropdown' do - expect(rendered).to have_dropdown_title('Demander une modification') + expect(rendered).to have_dropdown_title('Demander une correction') expect(rendered).to have_dropdown_items(count: 2) # form is already expanded so we have 2 visible items end end @@ -47,7 +47,7 @@ describe 'instructeurs/dossiers/instruction_button', type: :view do expect(rendered).to have_dropdown_item('Accepter') expect(rendered).to have_dropdown_item('Classer sans suite') expect(rendered).to have_dropdown_item('Refuser') - expect(rendered).to have_dropdown_item('Demander une modification') + expect(rendered).to have_dropdown_item('Demander une correction') end end end diff --git a/spec/views/instructeur/dossiers/show.html.haml_spec.rb b/spec/views/instructeur/dossiers/show.html.haml_spec.rb index 34a3a44f0..8b38e703b 100644 --- a/spec/views/instructeur/dossiers/show.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/show.html.haml_spec.rb @@ -61,7 +61,7 @@ describe 'instructeurs/dossiers/show', type: :view do within("form[action=\"#{follow_instructeur_dossier_path(dossier.procedure, dossier)}\"]") do expect(subject).to have_button('Suivre le dossier') end - expect(subject).to have_button('Demander une modification') + expect(subject).to have_button('Demander une correction') expect(subject).to have_selector('.header-actions ul:first-child > li.instruction-button', count: 1) end end From ec37611807e020ebd93aede0fa02ad03c491210e Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Thu, 13 Apr 2023 19:12:03 +0200 Subject: [PATCH 16/24] fix(schema): drop drop_down_lists migration not reversible --- db/migrate/20230331075755_drop_table_drop_down_lists.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20230331075755_drop_table_drop_down_lists.rb b/db/migrate/20230331075755_drop_table_drop_down_lists.rb index a098b380e..f19257889 100644 --- a/db/migrate/20230331075755_drop_table_drop_down_lists.rb +++ b/db/migrate/20230331075755_drop_table_drop_down_lists.rb @@ -1,5 +1,5 @@ class DropTableDropDownLists < ActiveRecord::Migration[6.1] def up - drop_table :drop_down_lists + drop_table :drop_down_lists if table_exists?(:drop_down_lists) end end From a85e10582d29df7b2b644cd1bff8eb3566aed43c Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 18 Apr 2023 12:10:06 +0200 Subject: [PATCH 17/24] fix(a11y): instruction_button role=region also when only pending correction item --- app/views/instructeurs/dossiers/_instruction_button.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/instructeurs/dossiers/_instruction_button.html.haml b/app/views/instructeurs/dossiers/_instruction_button.html.haml index 0296ec8cc..85a5b10ca 100644 --- a/app/views/instructeurs/dossiers/_instruction_button.html.haml +++ b/app/views/instructeurs/dossiers/_instruction_button.html.haml @@ -1,5 +1,5 @@ - if dossier.en_instruction? || (dossier.en_construction? && dossier.may_flag_as_pending_correction?) - = render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: { turbo_force: :server } }, button_options: { class: [button_or_label_class(dossier)]}, role: dossier.en_instruction? ? :region : :menu) do |menu| + = render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: { turbo_force: :server } }, button_options: { class: [button_or_label_class(dossier)]}, role: :region) do |menu| - menu.with_button_inner_html do = dossier.en_instruction? ? "Instruire le dossier" : "Demander une correction" From 3c4ea6f8cf7ab0c2e759de78ba986c8f55654a0d Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 18 Apr 2023 15:09:02 +0200 Subject: [PATCH 18/24] refactor(instructeur): instruction menu as component --- .../instruction_menu_component.rb | 24 ++++++++ .../instruction_menu_component.en.yml | 3 + .../instruction_menu_component.fr.yml | 3 + .../instruction_menu_component.html.haml | 54 ++++++++++++++++++ .../dossiers/_header_actions.html.haml | 2 +- .../instruction_menu_component_spec.rb | 55 +++++++++++++++++++ .../_instruction_button.html.haml_spec.rb | 53 ------------------ 7 files changed, 140 insertions(+), 54 deletions(-) create mode 100644 app/components/instructeurs/instruction_menu_component.rb create mode 100644 app/components/instructeurs/instruction_menu_component/instruction_menu_component.en.yml create mode 100644 app/components/instructeurs/instruction_menu_component/instruction_menu_component.fr.yml create mode 100644 app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml create mode 100644 spec/components/instructeurs/instruction_menu_component_spec.rb delete mode 100644 spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb diff --git a/app/components/instructeurs/instruction_menu_component.rb b/app/components/instructeurs/instruction_menu_component.rb new file mode 100644 index 000000000..e704fd7fc --- /dev/null +++ b/app/components/instructeurs/instruction_menu_component.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class Instructeurs::InstructionMenuComponent < ApplicationComponent + attr_reader :dossier + + def initialize(dossier:) + @dossier = dossier + end + + def render? + return true if dossier.en_instruction? + return true if dossier.en_construction? && dossier.may_flag_as_pending_correction? + + false + end + + def menu_label + if dossier.en_instruction? + t(".instruct") + else + "Demander une correction" + end + end +end diff --git a/app/components/instructeurs/instruction_menu_component/instruction_menu_component.en.yml b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.en.yml new file mode 100644 index 000000000..165ddbeaf --- /dev/null +++ b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.en.yml @@ -0,0 +1,3 @@ +--- +en: + instruct: Instruct the file diff --git a/app/components/instructeurs/instruction_menu_component/instruction_menu_component.fr.yml b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.fr.yml new file mode 100644 index 000000000..e82e45e9e --- /dev/null +++ b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.fr.yml @@ -0,0 +1,3 @@ +--- +fr: + instruct: Instruire le dossier diff --git a/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml new file mode 100644 index 000000000..7f0bb8b9d --- /dev/null +++ b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml @@ -0,0 +1,54 @@ += render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: { turbo_force: :server } }, role: :region) do |menu| + - menu.with_button_inner_html do + = menu_label + + - if dossier.en_instruction? + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do + %span.icon.accept + .dropdown-description + %h4 Accepter + L’usager sera informé que son dossier a été accepté + + - menu.with_item(class: "hidden inactive form-inside") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } + + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do + %span.icon.without-continuation + .dropdown-description + %h4 Classer sans suite + L’usager sera informé que son dossier a été classé sans suite + + - menu.with_item(class: "hidden inactive form-inside") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } + + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do + %span.icon.refuse + .dropdown-description + %h4 Refuser + L’usager sera informé que son dossier a été refusé + + - menu.with_item(class: "hidden inactive form-inside") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } + + - if dossier.may_flag_as_pending_correction? + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'pending_correction');", role: 'menuitem') do + %span.fr-icon.fr-icon-error-warning-line.fr-text-default--info{ "aria-hidden": "true" } + .dropdown-description + %h4 Demander une correction + L’usager sera informé que des modifications sont attendues + + - menu.with_item(class: class_names("inactive form-inside": true, hidden: dossier.en_instruction?)) do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, + visible: !dossier.en_instruction?, + form_path: pending_correction_instructeur_dossier_path(dossier.procedure, dossier), + placeholder: 'Expliquez au demandeur quelle(s) correction(s) sont attendues', + popup_class: 'pending_correction', + button_justificatif_label: "Ajouter une pièce jointe (facultatif)", + process_button: dossier.en_construction? ? 'Valider' : 'Valider et repasser en construction', + process_action: nil, + title: 'Marquer en attente de corrections', + confirm: 'Envoyer la demande de corrections ?'} diff --git a/app/views/instructeurs/dossiers/_header_actions.html.haml b/app/views/instructeurs/dossiers/_header_actions.html.haml index 38b8856e9..af4275504 100644 --- a/app/views/instructeurs/dossiers/_header_actions.html.haml +++ b/app/views/instructeurs/dossiers/_header_actions.html.haml @@ -10,4 +10,4 @@ turbo: true } %li.instruction-button - = render partial: "instruction_button", locals: { dossier: dossier } + = render Instructeurs::InstructionMenuComponent.new(dossier:) diff --git a/spec/components/instructeurs/instruction_menu_component_spec.rb b/spec/components/instructeurs/instruction_menu_component_spec.rb new file mode 100644 index 000000000..96c6a64a2 --- /dev/null +++ b/spec/components/instructeurs/instruction_menu_component_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +RSpec.describe Instructeurs::InstructionMenuComponent, type: :component do + include DossierHelper + + subject do + render_inline(described_class.new(dossier:)) + end + + matcher :have_dropdown_title do |expected_title| + match do |subject| + expect(subject).to have_selector('.dropdown .dropdown-button', text: expected_title) + end + end + + matcher :have_dropdown_items do |options| + match do |subject| + expected_count = options[:count] || 1 + expect(subject).to have_selector('ul.dropdown-items li:not(.hidden)', count: expected_count) + end + end + + matcher :have_dropdown_item do |expected_title, options = {}| + match do |subject| + expected_href = options[:href] + if (expected_href.present?) + expect(subject).to have_selector("ul.dropdown-items li a[href='#{expected_href}']", text: expected_title) + else + expect(subject).to have_selector('ul.dropdown-items li', text: expected_title) + end + end + end + + context 'en_construction' do + let(:dossier) { create(:dossier, :en_construction) } + + it 'renders a dropdown' do + expect(subject).to have_dropdown_title('Demander une correction') + expect(subject).to have_dropdown_items(count: 2) # form is already expanded so we have 2 visible items + end + end + + context 'en_instruction' do + let(:dossier) { create(:dossier, :en_instruction) } + + it 'renders a dropdown' do + expect(subject).to have_dropdown_title('Instruire le dossier') + expect(subject).to have_dropdown_items(count: 4) + expect(subject).to have_dropdown_item('Accepter') + expect(subject).to have_dropdown_item('Classer sans suite') + expect(subject).to have_dropdown_item('Refuser') + expect(subject).to have_dropdown_item('Demander une correction') + end + end +end diff --git a/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb b/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb deleted file mode 100644 index e8e787609..000000000 --- a/spec/views/instructeur/dossiers/_instruction_button.html.haml_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -describe 'instructeurs/dossiers/instruction_button', type: :view do - include DossierHelper - - subject! do - render('instructeurs/dossiers/instruction_button', dossier: dossier) - end - - matcher :have_dropdown_title do |expected_title| - match do |rendered| - expect(rendered).to have_selector('.dropdown .dropdown-button', text: expected_title) - end - end - - matcher :have_dropdown_items do |options| - match do |rendered| - expected_count = options[:count] || 1 - expect(rendered).to have_selector('ul.dropdown-items li:not(.hidden)', count: expected_count) - end - end - - matcher :have_dropdown_item do |expected_title, options = {}| - match do |rendered| - expected_href = options[:href] - if (expected_href.present?) - expect(rendered).to have_selector("ul.dropdown-items li a[href='#{expected_href}']", text: expected_title) - else - expect(rendered).to have_selector('ul.dropdown-items li', text: expected_title) - end - end - end - - context 'en_construction' do - let(:dossier) { create(:dossier, :en_construction) } - - it 'renders a dropdown' do - expect(rendered).to have_dropdown_title('Demander une correction') - expect(rendered).to have_dropdown_items(count: 2) # form is already expanded so we have 2 visible items - end - end - - context 'en_instruction' do - let(:dossier) { create(:dossier, :en_instruction) } - - it 'renders a dropdown' do - expect(rendered).to have_dropdown_title('Instruire le dossier') - expect(rendered).to have_dropdown_items(count: 4) - expect(rendered).to have_dropdown_item('Accepter') - expect(rendered).to have_dropdown_item('Classer sans suite') - expect(rendered).to have_dropdown_item('Refuser') - expect(rendered).to have_dropdown_item('Demander une correction') - end - end -end From fd4a9a6a2f06ccf9abd414d44a99ef392bf9240a Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 18 Apr 2023 18:06:24 +0200 Subject: [PATCH 19/24] refactor(instruction): menu repasser en construction + demander une correction --- app/assets/stylesheets/motivation.scss | 1 - .../en_construction_menu_component.rb | 24 +++++++ .../en_construction_menu_component.en.yml | 4 ++ .../en_construction_menu_component.fr.yml | 4 ++ .../en_construction_menu_component.html.haml | 31 +++++++++ .../instruction_menu_component.rb | 11 +-- .../instruction_menu_component.html.haml | 68 +++++++------------ .../dossiers/_header_actions.html.haml | 4 +- .../_instruction_button_motivation.html.haml | 2 +- .../procedures/_dossier_actions.html.haml | 6 +- .../instructeurs/procedures/show.html.haml | 3 +- app/views/recherche/index.html.haml | 3 +- .../en_contruction_menu_component_spec.rb | 53 +++++++++++++++ .../instruction_menu_component_spec.rb | 8 +-- .../dossiers/show.html.haml_spec.rb | 12 ++-- 15 files changed, 164 insertions(+), 70 deletions(-) create mode 100644 app/components/instructeurs/en_construction_menu_component.rb create mode 100644 app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.en.yml create mode 100644 app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.fr.yml create mode 100644 app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml create mode 100644 spec/components/instructeurs/en_contruction_menu_component_spec.rb diff --git a/app/assets/stylesheets/motivation.scss b/app/assets/stylesheets/motivation.scss index 32e076dff..fff6c80f0 100644 --- a/app/assets/stylesheets/motivation.scss +++ b/app/assets/stylesheets/motivation.scss @@ -2,7 +2,6 @@ @import "constants"; .motivation { - padding: $default-padding; color: $black; width: 450px; diff --git a/app/components/instructeurs/en_construction_menu_component.rb b/app/components/instructeurs/en_construction_menu_component.rb new file mode 100644 index 000000000..a2c930f80 --- /dev/null +++ b/app/components/instructeurs/en_construction_menu_component.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class Instructeurs::EnConstructionMenuComponent < ApplicationComponent + attr_reader :dossier + + def initialize(dossier:) + @dossier = dossier + end + + def render? + return true if dossier.may_repasser_en_construction? + return true if dossier.may_flag_as_pending_correction? + + false + end + + def menu_label + if dossier.en_construction? + t('.request_correction') + else + t(".revert_en_construction") + end + end +end diff --git a/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.en.yml b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.en.yml new file mode 100644 index 000000000..d9d6426bb --- /dev/null +++ b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.en.yml @@ -0,0 +1,4 @@ +--- +en: + revert_en_construction: Revert to in progress + request_correction: Request a correction diff --git a/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.fr.yml b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.fr.yml new file mode 100644 index 000000000..4430f7654 --- /dev/null +++ b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.fr.yml @@ -0,0 +1,4 @@ +--- +fr: + revert_en_construction: Repasser en construction + request_correction: Demander une correction diff --git a/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml new file mode 100644 index 000000000..67c90adcb --- /dev/null +++ b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml @@ -0,0 +1,31 @@ += render Dropdown::MenuComponent.new(wrapper: :div, button_options: { class: "fr-btn--secondary" }, wrapper_options: { data: {'turbo-force': true} }, role: :region) do |menu| + - menu.with_button_inner_html do + = menu_label + + - if dossier.may_repasser_en_construction? + = menu.with_item do + = link_to(repasser_en_construction_instructeur_dossier_path(dossier.procedure.id, dossier.id), method: :post, role: 'menuitem') do + %span.fr-icon.fr-icon-draft-line.fr-text-default--info.fr-mt-1v{ "aria-hidden": "true" } + .dropdown-description + %h4= t('.revert_en_construction') + L’usager sera notifié qu’il peut modifier son dossier + + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'pending_correction');", role: 'menuitem') do + %span.fr-icon.fr-icon-error-warning-line.fr-text-default--info.fr-mt-1v{ "aria-hidden": "true" } + + .dropdown-description + %h4= t('.request_correction') + L’usager sera notifié que des modifications sont attendues + + - menu.with_item(class: "inactive form-inside fr-pt-1v") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:, + visible: true, + form_path: pending_correction_instructeur_dossier_path(dossier.procedure, dossier), + placeholder: 'Expliquez au demandeur quelle(s) correction(s) sont attendues', + popup_class: 'pending_correction', + button_justificatif_label: "Ajouter une pièce jointe (facultatif)", + process_button: dossier.en_construction? ? 'Valider' : 'Valider et repasser en construction', + process_action: nil, + title: 'Marquer en attente de corrections', + confirm: 'Envoyer la demande de corrections ?'} diff --git a/app/components/instructeurs/instruction_menu_component.rb b/app/components/instructeurs/instruction_menu_component.rb index e704fd7fc..01dc27f85 100644 --- a/app/components/instructeurs/instruction_menu_component.rb +++ b/app/components/instructeurs/instruction_menu_component.rb @@ -8,17 +8,10 @@ class Instructeurs::InstructionMenuComponent < ApplicationComponent end def render? - return true if dossier.en_instruction? - return true if dossier.en_construction? && dossier.may_flag_as_pending_correction? - - false + dossier.en_instruction? end def menu_label - if dossier.en_instruction? - t(".instruct") - else - "Demander une correction" - end + t(".instruct") end end diff --git a/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml index 7f0bb8b9d..51a08855a 100644 --- a/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml +++ b/app/components/instructeurs/instruction_menu_component/instruction_menu_component.html.haml @@ -2,53 +2,33 @@ - menu.with_button_inner_html do = menu_label - - if dossier.en_instruction? - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do - %span.icon.accept - .dropdown-description - %h4 Accepter - L’usager sera informé que son dossier a été accepté + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do + %span.icon.accept + .dropdown-description + %h4 Accepter + L’usager sera informé que son dossier a été accepté - - menu.with_item(class: "hidden inactive form-inside") do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } + - menu.with_item(class: "hidden inactive form-inside fr-pt-1v") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" } - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do - %span.icon.without-continuation - .dropdown-description - %h4 Classer sans suite - L’usager sera informé que son dossier a été classé sans suite + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do + %span.icon.without-continuation + .dropdown-description + %h4 Classer sans suite + L’usager sera informé que son dossier a été classé sans suite - - menu.with_item(class: "hidden inactive form-inside") do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } + - menu.with_item(class: "hidden inactive form-inside fr-pt-1v") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' } - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do - %span.icon.refuse - .dropdown-description - %h4 Refuser - L’usager sera informé que son dossier a été refusé + - menu.with_item do + = link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do + %span.icon.refuse + .dropdown-description + %h4 Refuser + L’usager sera informé que son dossier a été refusé - - menu.with_item(class: "hidden inactive form-inside") do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } + - menu.with_item(class: "hidden inactive form-inside fr-pt-1v") do + = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' } - - if dossier.may_flag_as_pending_correction? - - menu.with_item do - = link_to('#', onclick: "DS.showMotivation(event, 'pending_correction');", role: 'menuitem') do - %span.fr-icon.fr-icon-error-warning-line.fr-text-default--info{ "aria-hidden": "true" } - .dropdown-description - %h4 Demander une correction - L’usager sera informé que des modifications sont attendues - - - menu.with_item(class: class_names("inactive form-inside": true, hidden: dossier.en_instruction?)) do - = render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier: dossier, - visible: !dossier.en_instruction?, - form_path: pending_correction_instructeur_dossier_path(dossier.procedure, dossier), - placeholder: 'Expliquez au demandeur quelle(s) correction(s) sont attendues', - popup_class: 'pending_correction', - button_justificatif_label: "Ajouter une pièce jointe (facultatif)", - process_button: dossier.en_construction? ? 'Valider' : 'Valider et repasser en construction', - process_action: nil, - title: 'Marquer en attente de corrections', - confirm: 'Envoyer la demande de corrections ?'} diff --git a/app/views/instructeurs/dossiers/_header_actions.html.haml b/app/views/instructeurs/dossiers/_header_actions.html.haml index af4275504..d5d28a0b1 100644 --- a/app/views/instructeurs/dossiers/_header_actions.html.haml +++ b/app/views/instructeurs/dossiers/_header_actions.html.haml @@ -2,12 +2,14 @@ = render partial: "instructeurs/procedures/dossier_actions", locals: { procedure_id: dossier.procedure.id, dossier_id: dossier.id, + dossier: dossier, state: dossier.state, archived: dossier.archived, dossier_is_followed: current_instructeur&.follow?(dossier), close_to_expiration: dossier.close_to_expiration?, hidden_by_administration: dossier.hidden_by_administration?, - turbo: true } + turbo: true, + with_menu: true } %li.instruction-button = render Instructeurs::InstructionMenuComponent.new(dossier:) diff --git a/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml b/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml index 31bc2c56f..76b4d576e 100644 --- a/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml +++ b/app/views/instructeurs/dossiers/_instruction_button_motivation.html.haml @@ -1,4 +1,4 @@ -.motivation{ class: class_names(popup_class => true, hidden: !defined?(visible) || !visible) } +.motivation{ class: class_names(popup_class => true, hidden: !defined?(visible) || !visible, "fr-pb-2w fr-px-2w": true) } = form_tag(defined?(form_path) ? form_path : terminer_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo: true, turbo_confirm: confirm }, method: :post, multipart: true) do - if title == 'Accepter' = text_area :dossier, :motivation, class: 'fr-input', placeholder: placeholder, required: false diff --git a/app/views/instructeurs/procedures/_dossier_actions.html.haml b/app/views/instructeurs/procedures/_dossier_actions.html.haml index 91de51c96..d6e65f175 100644 --- a/app/views/instructeurs/procedures/_dossier_actions.html.haml +++ b/app/views/instructeurs/procedures/_dossier_actions.html.haml @@ -29,11 +29,15 @@ = "" - elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state) + - if with_menu + %li.en-construction-menu{ 'data-turbo': turbo ? 'true' : 'false' } + = render Instructeurs::EnConstructionMenuComponent.new(dossier:) + - if Dossier.states[:en_construction] == state %li{ 'data-turbo': turbo ? 'true' : 'false' } = button_to passer_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-btn--secondary fr-icon-edit-line' do Passer en instruction - - elsif Dossier.states[:en_instruction] == state + - elsif Dossier.states[:en_instruction] == state && !with_menu %li{ 'data-turbo': turbo ? 'true' : 'false' } = button_to repasser_en_construction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-btn--secondary fr-icon-draft-line' do Repasser en construction diff --git a/app/views/instructeurs/procedures/show.html.haml b/app/views/instructeurs/procedures/show.html.haml index d5c62c75f..a9b7fc008 100644 --- a/app/views/instructeurs/procedures/show.html.haml +++ b/app/views/instructeurs/procedures/show.html.haml @@ -183,7 +183,8 @@ dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id), close_to_expiration: @statut == 'expirant', hidden_by_administration: @statut == 'supprimes_recemment', - turbo: false } + turbo: false, + with_menu: false } %tfoot %tr %td.force-table-100{ colspan: @procedure_presentation.displayed_fields_for_headers.size + 2 } diff --git a/app/views/recherche/index.html.haml b/app/views/recherche/index.html.haml index d98aeef72..71814bd8c 100644 --- a/app/views/recherche/index.html.haml +++ b/app/views/recherche/index.html.haml @@ -101,7 +101,8 @@ dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id), close_to_expiration: nil, hidden_by_administration: nil, - turbo: false } + turbo: false, + with_menu: false } - else %td diff --git a/spec/components/instructeurs/en_contruction_menu_component_spec.rb b/spec/components/instructeurs/en_contruction_menu_component_spec.rb new file mode 100644 index 000000000..491d722f5 --- /dev/null +++ b/spec/components/instructeurs/en_contruction_menu_component_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +RSpec.describe Instructeurs::EnConstructionMenuComponent, type: :component do + include DossierHelper + + subject do + render_inline(described_class.new(dossier:)) + end + + matcher :have_dropdown_title do |expected_title| + match do |subject| + expect(subject).to have_selector('.dropdown .dropdown-button', text: expected_title) + end + end + + matcher :have_dropdown_items do |options| + match do |subject| + expected_count = options[:count] || 1 + expect(subject).to have_selector('ul.dropdown-items li:not(.hidden)', count: expected_count) + end + end + + matcher :have_dropdown_item do |expected_title, options = {}| + match do |subject| + expected_href = options[:href] + if (expected_href.present?) + expect(subject).to have_selector("ul.dropdown-items li a[href='#{expected_href}']", text: expected_title) + else + expect(subject).to have_selector('ul.dropdown-items li', text: expected_title) + end + end + end + + context 'en_construction' do + let(:dossier) { create(:dossier, :en_construction) } + + it 'renders a dropdown' do + expect(subject).to have_dropdown_title('Demander une correction') + expect(subject).to have_dropdown_items(count: 2) # form is already expanded so we have 2 visible items + end + end + + context 'en_instruction' do + let(:dossier) { create(:dossier, :en_instruction) } + + it 'renders a dropdown' do + expect(subject).to have_dropdown_title('Repasser en construction') + expect(subject).to have_dropdown_item('Demander une correction') + expect(subject).to have_dropdown_item('Repasser en construction') + expect(subject).to have_dropdown_items(count: 3) + end + end +end diff --git a/spec/components/instructeurs/instruction_menu_component_spec.rb b/spec/components/instructeurs/instruction_menu_component_spec.rb index 96c6a64a2..f655c371d 100644 --- a/spec/components/instructeurs/instruction_menu_component_spec.rb +++ b/spec/components/instructeurs/instruction_menu_component_spec.rb @@ -34,9 +34,8 @@ RSpec.describe Instructeurs::InstructionMenuComponent, type: :component do context 'en_construction' do let(:dossier) { create(:dossier, :en_construction) } - it 'renders a dropdown' do - expect(subject).to have_dropdown_title('Demander une correction') - expect(subject).to have_dropdown_items(count: 2) # form is already expanded so we have 2 visible items + it 'does not render' do + expect(subject.to_s).to be_empty end end @@ -45,11 +44,10 @@ RSpec.describe Instructeurs::InstructionMenuComponent, type: :component do it 'renders a dropdown' do expect(subject).to have_dropdown_title('Instruire le dossier') - expect(subject).to have_dropdown_items(count: 4) + expect(subject).to have_dropdown_items(count: 3) expect(subject).to have_dropdown_item('Accepter') expect(subject).to have_dropdown_item('Classer sans suite') expect(subject).to have_dropdown_item('Refuser') - expect(subject).to have_dropdown_item('Demander une correction') end end end diff --git a/spec/views/instructeur/dossiers/show.html.haml_spec.rb b/spec/views/instructeur/dossiers/show.html.haml_spec.rb index 8b38e703b..8745a8b2e 100644 --- a/spec/views/instructeur/dossiers/show.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/show.html.haml_spec.rb @@ -52,7 +52,7 @@ describe 'instructeurs/dossiers/show', type: :view do end end - context 'en_contruction' do + context 'en_construction' do let(:dossier) { create(:dossier, :en_construction) } it 'displays the correct actions' do within("form[action=\"#{passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier)}\"]") do @@ -75,15 +75,15 @@ describe 'instructeurs/dossiers/show', type: :view do end it 'displays the correct actions' do - within("form[action=\"#{repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier)}\"]") do - expect(subject).to have_button('Repasser en construction') - end within("form[action=\"#{unfollow_instructeur_dossier_path(dossier.procedure, dossier)}\"]") do expect(subject).to have_button('Ne plus suivre') end + + expect(subject).to have_button('Repasser en construction') + expect(subject).to have_selector('.en-construction-menu .fr-btn', count: 5) + expect(subject).to have_button('Instruire le dossier') - expect(subject).to have_selector('.header-actions ul:first-child > li .fr-btn', count: 15) - expect(subject).to have_selector('.header-actions ul:first-child > li.instruction-button', count: 1) + expect(subject).to have_selector('.instruction-button .fr-btn', count: 13) end end From 0fc09bb42ced12f17ae71d899f0fead1a992c8b0 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Tue, 18 Apr 2023 18:16:02 +0200 Subject: [PATCH 20/24] refactor(instruction): re-organize actions order & primary action --- .../procedures/_dossier_actions.html.haml | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/app/views/instructeurs/procedures/_dossier_actions.html.haml b/app/views/instructeurs/procedures/_dossier_actions.html.haml index d6e65f175..6dda325aa 100644 --- a/app/views/instructeurs/procedures/_dossier_actions.html.haml +++ b/app/views/instructeurs/procedures/_dossier_actions.html.haml @@ -29,25 +29,24 @@ = "" - elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state) - - if with_menu - %li.en-construction-menu{ 'data-turbo': turbo ? 'true' : 'false' } - = render Instructeurs::EnConstructionMenuComponent.new(dossier:) - - - if Dossier.states[:en_construction] == state - %li{ 'data-turbo': turbo ? 'true' : 'false' } - = button_to passer_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-btn--secondary fr-icon-edit-line' do - Passer en instruction - - elsif Dossier.states[:en_instruction] == state && !with_menu - %li{ 'data-turbo': turbo ? 'true' : 'false' } - = button_to repasser_en_construction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-btn--secondary fr-icon-draft-line' do - Repasser en construction - - - if dossier_is_followed %li = button_to unfollow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-btn--secondary fr-icon-star-fill' do = t('views.instructeurs.dossiers.stop_follow') - else %li - = button_to follow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-icon-star-line' do + = button_to follow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'fr-btn fr-btn--secondary fr-icon-star-line' do = t('views.instructeurs.dossiers.follow_file') + + - if with_menu + %li.en-construction-menu{ 'data-turbo': turbo ? 'true' : 'false' } + = render Instructeurs::EnConstructionMenuComponent.new(dossier:) + + - if Dossier.states[:en_construction] == state + %li{ 'data-turbo': turbo ? 'true' : 'false' } + = button_to passer_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-icon-edit-line' do + Passer en instruction + - elsif Dossier.states[:en_instruction] == state && !with_menu + %li{ 'data-turbo': turbo ? 'true' : 'false' } + = button_to repasser_en_construction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-btn--secondary fr-icon-draft-line' do + Repasser en construction From b495e0aff009e4ccd59fc3ed13e7f24747f9c3ba Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Wed, 19 Apr 2023 16:47:40 +0200 Subject: [PATCH 21/24] =?UTF-8?q?feat(user):=20dossier=20r=C3=A9sum=C3=A9?= =?UTF-8?q?=20with=20commentaire=20about=20corrections?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/concerns/dossier_correctable_concern.rb | 4 ++++ app/views/users/dossiers/show/_status_overview.html.haml | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/app/models/concerns/dossier_correctable_concern.rb b/app/models/concerns/dossier_correctable_concern.rb index 6b5b38d45..b82123078 100644 --- a/app/models/concerns/dossier_correctable_concern.rb +++ b/app/models/concerns/dossier_correctable_concern.rb @@ -27,6 +27,10 @@ module DossierCorrectableConcern corrections.pending.exists? end + def pending_correction + corrections.pending.first + end + def resolve_pending_correction! corrections.pending.update(resolved_at: Time.current) end diff --git a/app/views/users/dossiers/show/_status_overview.html.haml b/app/views/users/dossiers/show/_status_overview.html.haml index c258b3b6f..3fef287dc 100644 --- a/app/views/users/dossiers/show/_status_overview.html.haml +++ b/app/views/users/dossiers/show/_status_overview.html.haml @@ -8,6 +8,9 @@ = t('views.users.dossiers.show.status_overview.status_draft') %li.en-construction{ class: dossier.en_construction? ? 'active' : nil } = t('views.users.dossiers.show.status_overview.status_in_progress') + + - if dossier.pending_correction.present? + = "(#{Dossier.human_attribute_name("pending_correction.for_user")})" %li.en-instruction{ class: dossier.en_instruction? ? 'active' : nil } = t('views.users.dossiers.show.status_overview.status_review') %li.termine{ class: dossier.termine? ? 'active' : nil } @@ -23,6 +26,10 @@ -# brouillon does not occure - if dossier.en_construction? .en-construction + - if dossier.pending_correction.present? + .message.inverted-background + = render Dossiers::MessageComponent.new(commentaire: dossier.pending_correction.commentaire, connected_user: current_user) + %p{ role: 'status' } = t('views.users.dossiers.show.status_overview.en_construction_html') From 52c855357647c93675c7cef24b81f68231f94ffb Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Fri, 2 Jun 2023 12:43:08 +0200 Subject: [PATCH 22/24] fix(dossier_correction): don't allow to save with a missing (invalid) commentaire --- .../en_construction_menu_component.html.haml | 2 +- app/controllers/instructeurs/dossiers_controller.rb | 13 +++++++++---- app/models/concerns/dossier_correctable_concern.rb | 4 ++-- app/models/dossier_correction.rb | 2 ++ .../instructeurs/dossiers_controller_spec.rb | 10 ++++++++++ 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml index 67c90adcb..0c691ca45 100644 --- a/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml +++ b/app/components/instructeurs/en_construction_menu_component/en_construction_menu_component.html.haml @@ -1,4 +1,4 @@ -= render Dropdown::MenuComponent.new(wrapper: :div, button_options: { class: "fr-btn--secondary" }, wrapper_options: { data: {'turbo-force': true} }, role: :region) do |menu| += render Dropdown::MenuComponent.new(wrapper: :div, menu_options: { id: "menu-en-construction" }, button_options: { class: "fr-btn--secondary" }, role: :region) do |menu| - menu.with_button_inner_html do = menu_label diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 9970a45d2..fc4b82b19 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -232,11 +232,16 @@ module Instructeurs flash.alert = dossier.termine? ? "Impossible de demander de corriger un dossier terminé." : "Le dossier est déjà en attente de correction." else commentaire = CommentaireService.build(current_instructeur, dossier, { body: message, piece_jointe: }) - dossier.flag_as_pending_correction!(commentaire) - dossier.update!(last_commentaire_updated_at: Time.zone.now) - current_instructeur.follow(dossier) - flash.notice = "Dossier marqué comme en attente de correction." + if commentaire.valid? + dossier.flag_as_pending_correction!(commentaire) + dossier.update!(last_commentaire_updated_at: Time.zone.now) + current_instructeur.follow(dossier) + + flash.notice = "Dossier marqué comme en attente de correction." + else + flash.alert = commentaire.errors.full_messages.map { "Commentaire : #{_1}" } + end end respond_to do |format| diff --git a/app/models/concerns/dossier_correctable_concern.rb b/app/models/concerns/dossier_correctable_concern.rb index b82123078..ee9dc09aa 100644 --- a/app/models/concerns/dossier_correctable_concern.rb +++ b/app/models/concerns/dossier_correctable_concern.rb @@ -7,7 +7,7 @@ module DossierCorrectableConcern def flag_as_pending_correction!(commentaire) return unless may_flag_as_pending_correction? - corrections.create(commentaire:) + corrections.create!(commentaire:) return if en_construction? @@ -32,7 +32,7 @@ module DossierCorrectableConcern end def resolve_pending_correction! - corrections.pending.update(resolved_at: Time.current) + corrections.pending.update!(resolved_at: Time.current) end end end diff --git a/app/models/dossier_correction.rb b/app/models/dossier_correction.rb index 95d16bce6..201c19ceb 100644 --- a/app/models/dossier_correction.rb +++ b/app/models/dossier_correction.rb @@ -13,5 +13,7 @@ class DossierCorrection < ApplicationRecord belongs_to :dossier belongs_to :commentaire + validates_associated :commentaire + scope :pending, -> { where(resolved_at: nil) } end diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 968c859d2..22fa39364 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -547,6 +547,16 @@ describe Instructeurs::DossiersController, type: :controller do end end + context 'with an invalid comment / attachment' do + let(:justificatif) { Rack::Test::UploadedFile.new(Rails.root.join('Gemfile.lock'), 'text/lock') } + + it 'does not save anything' do + expect(dossier.reload).not_to be_pending_correction + expect(dossier.commentaires.count).to eq(0) + expect(response.body).to include('pas d’un type accepté') + end + end + context 'with an empty message' do let(:message) { '' } From 7f871728c703b8b2b7d065d0425ecd65cd3c176a Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Fri, 2 Jun 2023 15:46:23 +0200 Subject: [PATCH 23/24] =?UTF-8?q?refactor(message):=20badges=20=C3=A0=20co?= =?UTF-8?q?rriger/en=20attente/corrig=C3=A9=20when=20related=20to=20a=20co?= =?UTF-8?q?rrection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/dossiers/message_component.rb | 7 +++++ .../message_component.html.haml | 5 ++-- app/helpers/dossier_helper.rb | 4 +++ app/models/dossier_correction.rb | 4 +++ config/locales/models/dossier/en.yml | 1 + config/locales/models/dossier/fr.yml | 1 + .../dossiers/message_component_spec.rb | 28 +++++++++++++++++++ 7 files changed, 48 insertions(+), 2 deletions(-) diff --git a/app/components/dossiers/message_component.rb b/app/components/dossiers/message_component.rb index 16244c2a6..af19bc2f8 100644 --- a/app/components/dossiers/message_component.rb +++ b/app/components/dossiers/message_component.rb @@ -8,6 +8,13 @@ class Dossiers::MessageComponent < ApplicationComponent attr_reader :commentaire, :connected_user, :messagerie_seen_at + def correction_badge + return if commentaire.dossier_correction.nil? + return helpers.correction_resolved_badge if commentaire.dossier_correction.resolved? + + helpers.pending_correction_badge(connected_user.is_a?(Instructeur) ? :for_instructeur : :for_user) + end + private def show_reply_button? diff --git a/app/components/dossiers/message_component/message_component.html.haml b/app/components/dossiers/message_component/message_component.html.haml index 1937b2492..1839981a0 100644 --- a/app/components/dossiers/message_component/message_component.html.haml +++ b/app/components/dossiers/message_component/message_component.html.haml @@ -6,8 +6,9 @@ = commentaire_issuer - if commentaire_from_guest? %span.fr-text--xs.fr-text-mention--grey.font-weight-normal= t('.guest') - - if commentaire.flagged_pending_correction? - = helpers.pending_correction_badge(connected_user.is_a?(Instructeur) ? :for_instructeur : :for_user) + + = correction_badge + %span.date{ class: ["fr-text--xs", "fr-text-mention--grey", "font-weight-normal", highlight_if_unseen_class], data: scroll_to_target } = commentaire_date .rich-text diff --git a/app/helpers/dossier_helper.rb b/app/helpers/dossier_helper.rb index c898cefc4..81099a161 100644 --- a/app/helpers/dossier_helper.rb +++ b/app/helpers/dossier_helper.rb @@ -98,6 +98,10 @@ module DossierHelper tag.span(Dossier.human_attribute_name("pending_correction.#{for_profile}"), class: ['fr-badge fr-badge--sm fr-badge--warning super', html_class], role: 'status') end + def correction_resolved_badge + tag.span(Dossier.human_attribute_name("pending_correction.resolved"), class: ['fr-badge fr-badge--sm fr-badge--success super'], role: 'status') + end + def demandeur_dossier(dossier) if dossier.procedure.for_individual? "#{dossier&.individual&.nom} #{dossier&.individual&.prenom}" diff --git a/app/models/dossier_correction.rb b/app/models/dossier_correction.rb index 201c19ceb..eda8dfa40 100644 --- a/app/models/dossier_correction.rb +++ b/app/models/dossier_correction.rb @@ -16,4 +16,8 @@ class DossierCorrection < ApplicationRecord validates_associated :commentaire scope :pending, -> { where(resolved_at: nil) } + + def resolved? + resolved_at.present? + end end diff --git a/config/locales/models/dossier/en.yml b/config/locales/models/dossier/en.yml index 6ed42b17d..d33baed61 100644 --- a/config/locales/models/dossier/en.yml +++ b/config/locales/models/dossier/en.yml @@ -18,6 +18,7 @@ en: pending_correction: for_instructeur: "pending" for_user: "to be corrected" + resolved: corrected traitement: state: "State" traitement/state: diff --git a/config/locales/models/dossier/fr.yml b/config/locales/models/dossier/fr.yml index d0db5795a..cbfd9e4a8 100644 --- a/config/locales/models/dossier/fr.yml +++ b/config/locales/models/dossier/fr.yml @@ -22,6 +22,7 @@ fr: pending_correction: for_instructeur: "en attente" for_user: "à corriger" + resolved: corrigé traitement: state: "État" traitement/state: diff --git a/spec/components/dossiers/message_component_spec.rb b/spec/components/dossiers/message_component_spec.rb index e6b061548..413e7b9f4 100644 --- a/spec/components/dossiers/message_component_spec.rb +++ b/spec/components/dossiers/message_component_spec.rb @@ -144,4 +144,32 @@ RSpec.describe Dossiers::MessageComponent, type: :component do end end end + + describe '#correction_badge' do + let(:resolved_at) { nil } + + before do + create(:dossier_correction, commentaire:, dossier:, resolved_at:) + end + + it 'returns a badge à corriger' do + expect(subject).to have_text('à corriger') + end + + context 'connected as instructeur' do + let(:connected_user) { create(:instructeur) } + + it 'returns a badge en attente' do + expect(subject).to have_text('en attente') + end + end + + context 'when the correction is resolved' do + let(:resolved_at) { 1.minute.ago } + + it 'returns a badge corrigé' do + expect(subject).to have_text("corrigé") + end + end + end end From b1bcc784bb500b25f97f8df7b4fdfa2ed3c3e804 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Fri, 2 Jun 2023 15:46:58 +0200 Subject: [PATCH 24/24] refactor(messagerie): preload commentaires with corrections & attachments blobs --- app/models/dossier.rb | 2 ++ app/views/shared/dossiers/_messagerie.html.haml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 5edc3c65b..fb8ac746f 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -99,6 +99,8 @@ class Dossier < ApplicationRecord has_many :prefilled_champs_public, -> { root.public_only.prefilled }, class_name: 'Champ', inverse_of: false has_many :commentaires, inverse_of: :dossier, dependent: :destroy + has_many :preloaded_commentaires, -> { includes(:dossier_correction, piece_jointe_attachment: :blob) }, class_name: 'Commentaire', inverse_of: :dossier + has_many :invites, dependent: :destroy has_many :follows, -> { active }, inverse_of: :dossier has_many :previous_follows, -> { inactive }, class_name: 'Follow', inverse_of: :dossier diff --git a/app/views/shared/dossiers/_messagerie.html.haml b/app/views/shared/dossiers/_messagerie.html.haml index 56eba7d7e..854316665 100644 --- a/app/views/shared/dossiers/_messagerie.html.haml +++ b/app/views/shared/dossiers/_messagerie.html.haml @@ -1,6 +1,6 @@ .messagerie.container %ul.messages-list{ data: { controller: 'scroll-to' } } - - dossier.commentaires.with_attached_piece_jointe.each do |commentaire| + - dossier.preloaded_commentaires.each do |commentaire| %li.message{ class: commentaire_is_from_me_class(commentaire, connected_user), id: dom_id(commentaire) } = render Dossiers::MessageComponent.new(commentaire: commentaire, connected_user: connected_user, messagerie_seen_at: messagerie_seen_at, show_reply_button: show_reply_button(commentaire, connected_user))