feat(emails): show email errors on procedure page

This commit is contained in:
Paul Chavard 2022-11-02 10:03:10 +01:00
parent ddd5eab4b1
commit 33fc8a45ab
16 changed files with 129 additions and 63 deletions

View file

@ -11,6 +11,9 @@ class Procedure::Card::ChampsComponent < ApplicationComponent
end
def error_messages
@procedure.errors.messages_for(:draft_types_de_champ).to_sentence
[
@procedure.errors.messages_for(:draft_types_de_champ),
@procedure.errors.messages_for(:draft_revision)
].flatten.to_sentence
end
end

View file

@ -2,4 +2,16 @@ class Procedure::Card::EmailsComponent < ApplicationComponent
def initialize(procedure:)
@procedure = procedure
end
private
def error_messages
[
@procedure.errors.messages_for(:initiated_mail),
@procedure.errors.messages_for(:received_mail),
@procedure.errors.messages_for(:closed_mail),
@procedure.errors.messages_for(:refused_mail),
@procedure.errors.messages_for(:without_continuation_mail)
].flatten.to_sentence
end
end

View file

@ -2,8 +2,12 @@
= link_to admin_procedure_mail_templates_path(@procedure), class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.justify-between
%div
%span.icon.clock
%p.fr-tile-status-todo À configurer
- if error_messages.present?
%span.icon.refuse
%p.fr-tile-status-error À modifier
- else
%span.icon.clock
%p.fr-tile-status-todo À configurer
%div
%h3.fr-h6.fr-mt-10v= t('.title')
%p.fr-tile-subtitle Notifications automatiques

View file

@ -0,0 +1,35 @@
class Procedure::EmailTemplateCardComponent < ApplicationComponent
def initialize(email_template:)
@email_template = email_template
end
private
def title
@email_template.class.const_get(:DISPLAYED_NAME)
end
def desc
@email_template.subject if edited?
end
def error
@email_template.errors.full_messages.first if @email_template.errors.present?
end
def tag
if edited?
"modifié le #{@email_template.updated_at.strftime('%d-%m-%Y')}"
else
"Modèle standard"
end
end
def edited?
@email_template.updated_at.present?
end
def edit_path
edit_admin_procedure_mail_template_path(@email_template.procedure, @email_template.class.const_get(:SLUG))
end
end

View file

@ -0,0 +1,3 @@
= render Dsfr::CardVerticalComponent.new(title: title, desc: desc, error: error, tags: [tag]) do |c|
- c.with_footer_button do
= link_to 'Modifier', edit_path, class: 'fr-btn'

View file

@ -4,34 +4,43 @@ module Administrateurs
def index
@mail_templates = mail_templates
@mail_templates.each(&:validate)
end
def edit
@procedure = procedure
@mail_template = find_mail_template_by_slug(params[:id])
if !@mail_template.valid?
flash.now.alert = @mail_template.errors.full_messages
end
end
def show
redirect_to edit_admin_procedure_mail_template_path(procedure.id, params[:id])
end
def update
@procedure = procedure
mail_template = find_mail_template_by_slug(params[:id])
if mail_template.update(update_params)
flash.notice = "Email mis à jour"
redirect_to edit_admin_procedure_mail_template_path(mail_template.procedure_id, params[:id])
else
flash.alert = mail_template.errors.full_messages
end
flash.now.alert = mail_template.errors.full_messages
mail_template.rich_body = mail_template.body
redirect_to edit_admin_procedure_mail_template_path(mail_template.procedure_id, params[:id])
@mail_template = mail_template
render :edit
end
end
def preview
mail_template = find_mail_template_by_slug(params[:id])
dossier = Dossier.new(id: '1', procedure: procedure)
dossier = procedure.active_revision.dossier_for_preview(current_user)
@dossier = dossier
@logo_url = procedure.logo_url
@service = procedure.service
@rendered_template = sanitize(mail_template.rich_body.body.to_html)
@rendered_template = sanitize(mail_template.body_for_dossier(dossier))
@actions = mail_template.actions_for_dossier(dossier)
render(template: 'notification_mailer/send_notification', layout: 'mailers/notifications_layout')

View file

@ -181,7 +181,7 @@ module TagsSubstitutionConcern
{
libelle: 'groupe instructeur',
description: 'Le groupe instructeur en charge du dossier',
lambda: -> (d) { d.groupe_instructeur.label },
lambda: -> (d) { d.groupe_instructeur&.label },
available_for_states: Dossier::SOUMIS
}
]

View file

@ -272,11 +272,11 @@ class Procedure < ApplicationRecord
validates :draft_types_de_champ,
'types_de_champ/no_empty_block': true,
'types_de_champ/no_empty_drop_down': true,
if: :validate_for_publication?
on: :publication
validates :draft_types_de_champ_private,
'types_de_champ/no_empty_block': true,
'types_de_champ/no_empty_drop_down': true,
if: :validate_for_publication?
on: :publication
validate :check_juridique
validates :path, presence: true, format: { with: /\A[a-z0-9_\-]{3,200}\z/ }, uniqueness: { scope: [:path, :closed_at, :hidden_at, :unpublished_at], case_sensitive: false }
validates :duree_conservation_dossiers_dans_ds, allow_nil: false,
@ -295,6 +295,13 @@ class Procedure < ApplicationRecord
validates :lien_dpo, email_or_link: true, allow_nil: true
validates_with MonAvisEmbedValidator
validates_associated :draft_revision, on: :publication
validates_associated :initiated_mail, on: :publication
validates_associated :received_mail, on: :publication
validates_associated :closed_mail, on: :publication
validates_associated :refused_mail, on: :publication
validates_associated :without_continuation_mail, on: :publication
FILE_MAX_SIZE = 20.megabytes
validates :notice, content_type: [
"application/msword",
@ -798,8 +805,12 @@ class Procedure < ApplicationRecord
end
def publish_revision!
update!(draft_revision: create_new_revision, published_revision: draft_revision)
published_revision.touch(:published_at)
transaction do
self.published_revision = draft_revision
self.draft_revision = create_new_revision
save!(context: :publication)
published_revision.touch(:published_at)
end
dossiers
.state_not_termine
.find_each { |dossier| DossierRebaseJob.perform_later(dossier) }
@ -859,16 +870,15 @@ class Procedure < ApplicationRecord
new_draft.revision_types_de_champ.reload
end
def validate_for_publication?
validation_context == :publication || publiee?
end
def before_publish
assign_attributes(closed_at: nil, unpublished_at: nil)
end
def after_publish(canonical_procedure = nil)
update!(canonical_procedure: canonical_procedure, draft_revision: create_new_revision, published_revision: draft_revision)
self.canonical_procedure = canonical_procedure
self.published_revision = draft_revision
self.draft_revision = create_new_revision
save!(context: :publication)
touch(:published_at)
published_revision.touch(:published_at)
end

View file

@ -17,7 +17,7 @@
.tag Balise
.description Description
.items
- @mail_template.tags.each do |tag|
- tags.each do |tag|
.item
%code.tag
= "--#{tag[:libelle]}--"

View file

@ -5,7 +5,7 @@
= render partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_path],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
["Emails", admin_procedure_mail_templates_path(@procedure)],
["Configuration des emails", admin_procedure_mail_templates_path(@procedure)],
[@mail_template.class.const_get(:DISPLAYED_NAME)]] }
.procedure-form
@ -17,7 +17,7 @@
html: { class: 'form procedure-form__column--form' } do |f|
%h1.page-title= @mail_template.class.const_get(:DISPLAYED_NAME)
= render partial: 'form', locals: { f: f }
= render partial: 'form', locals: { f: f, tags: @mail_template.tags }
.procedure-form__actions.sticky--bottom
.actions-right
= f.submit 'Enregistrer', class: 'button primary send'
@ -30,4 +30,4 @@
.notice
Cet aperçu est mis à jour après chaque sauvegarde.
.procedure-preview
= render partial: 'apercu', locals: { procedure: @procedure }
= render partial: 'apercu', locals: { procedure: @mail_template.procedure }

View file

@ -4,15 +4,7 @@
["Configuration des emails"]] }
.container
- @mail_templates.each do |mail_template|
.card
.flex.justify-between
%div
.card-title= mail_template.class.const_get(:DISPLAYED_NAME)
- if mail_template.updated_at.blank?
%p.notice= mail_template.class.const_get(:DISPLAYED_NAME) === 'Accusé de réception' ? 'Personnalisé' : 'Modèle standard'
- else
%span.badge.baseline modifié le #{mail_template.updated_at.strftime('%d-%m-%Y')}
%div
= link_to 'Modifier', edit_admin_procedure_mail_template_path(@procedure, mail_template.class.const_get(:SLUG)), class: 'fr-btn'
.fr-grid-row.fr-grid-row--gutters.fr-py-5w
- @mail_templates.each do |mail_template|
.fr-col-md-6.fr-col-12
= render Procedure::EmailTemplateCardComponent.new(email_template: mail_template)

View file

@ -22,4 +22,4 @@
= render partial: 'publication_form_inputs', locals: { procedure: procedure, closed_procedures: @closed_procedures }
.flex.justify-end
= submit_tag procedure_publish_label(procedure, :submit), { disabled: publication_errors.present?, class: "button primary", id: 'publish' }
= submit_tag procedure_publish_label(procedure, :submit), { disabled: publication_errors.present?, class: "fr-btn fr-btn--primary", id: 'publish' }

View file

@ -42,7 +42,7 @@
.container
= render TypesDeChampEditor::ErrorsSummary.new(revision: @procedure.draft_revision)
- if @procedure.draft_changed? && @procedure.draft_revision.valid?
- if @procedure.draft_changed?
.container
.card.featured
.card-title
@ -50,7 +50,7 @@
= render partial: 'revision_changes', locals: { changes: @procedure.revision_changes }
.flex.mt-2.justify-end
= button_to "Réinitialiser les modifications", admin_procedure_reset_draft_path(@procedure), class: 'fr-btn fr-btn--secondary fr-mr-2w', method: :put
= link_to 'Publier les modifications', admin_procedure_publication_path(@procedure), class: 'fr-btn', id: 'publish-procedure-link', data: { disable_with: "Publication..." }
= button_to 'Publier les modifications', admin_procedure_publication_path(@procedure), class: 'fr-btn', id: 'publish-procedure-link', data: { disable_with: "Publication..." }, disabled: !@procedure.draft_revision.valid?, method: :get
- if !@procedure.procedure_expires_when_termine_enabled?
= render partial: 'administrateurs/procedures/suggest_expires_when_termine', locals: { procedure: @procedure }

View file

@ -468,7 +468,7 @@ Rails.application.routes.draw do
get 'transfert' => 'procedures#transfert', as: :transfert
get 'close' => 'procedures#close', as: :close
post 'transfer' => 'procedures#transfer', as: :transfer
resources :mail_templates, only: [:edit, :update]
resources :mail_templates, only: [:edit, :update, :show]
resources :groupe_instructeurs, only: [:index, :show, :create, :update, :destroy] do
member do

View file

@ -45,7 +45,7 @@ describe Administrateurs::MailTemplatesController, type: :controller do
describe 'PATCH update' do
let(:mail_subject) { 'Mise à jour de votre démarche' }
let(:mail_body) { '<div>Une mise à jour a été effectuée sur votre démarche n° --demarche-id--.</div>' }
let(:mail_body) { '<div>Une mise à jour a été effectuée sur votre démarche n° --numéro du dossier--.</div>' }
before :each do
patch :update,
@ -58,11 +58,23 @@ describe Administrateurs::MailTemplatesController, type: :controller do
it { expect(response).to redirect_to edit_admin_procedure_mail_template_path(procedure, initiated_mail.class.const_get(:SLUG)) }
context 'the mail template' do
context 'with valid email template' do
subject { procedure.reload; procedure.initiated_mail_template }
it { expect(subject.subject).to eq(mail_subject) }
it { expect(subject.body).to eq(mail_body) }
it do
expect(subject.subject).to eq(mail_subject)
expect(subject.body).to eq(mail_body)
end
end
context 'with invalid email template' do
subject { procedure.reload; procedure.initiated_mail_template }
let(:mail_body) { '<div>Une mise à jour a été effectuée sur votre démarche n° --numéro--.</div>' }
it do
expect(subject.body).not_to eq(mail_body)
expect(response.body).to match("Le contenu de lemail de notification de passage du dossier en instruction réfère au champ \"numéro\" qui nexiste pas")
end
end
end
end

View file

@ -327,11 +327,9 @@ describe Procedure do
end
end
context 'on a published procedure' do
before { procedure.publish }
context 'when validating for publication' do
it 'validates that no repetition type de champ is empty' do
procedure.validate
procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_repetition_error_message)
new_draft = procedure.draft_revision
@ -339,32 +337,20 @@ describe Procedure do
parent_coordinate = new_draft.revision_types_de_champ.find_by(type_de_champ: repetition)
new_draft.revision_types_de_champ.create(type_de_champ: create(:type_de_champ), position: 0, parent: parent_coordinate)
procedure.validate
procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).not_to include(invalid_repetition_error_message)
end
it 'validates that no drop-down type de champ is empty' do
procedure.validate
procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_drop_down_error_message)
drop_down.update!(drop_down_list_value: "--title--\r\nsome value")
procedure.reload.validate
procedure.reload.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).not_to include(invalid_drop_down_error_message)
end
end
context 'when validating for publication' do
it 'validates that no repetition type de champ is empty' do
procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_repetition_error_message)
end
it 'validates that no drop-down type de champ is empty' do
procedure.validate(:publication)
expect(procedure.errors.full_messages_for(:draft_types_de_champ)).to include(invalid_drop_down_error_message)
end
end
context 'when the champ is private' do
before do
repetition.update(private: true)