Merge pull request #9668 from adullact/feature-ouidou/admin_creation_delegation_commentaire_email_notification

Délégation de compte admin: notification par email à l'ajout d'un commentaire
This commit is contained in:
Colin Darie 2024-02-02 14:09:22 +00:00 committed by GitHub
commit 460240713f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 160 additions and 35 deletions

View file

@ -25,7 +25,7 @@ class GroupeGestionnaire::GroupeGestionnaireListCommentaires::CommentaireCompone
end
def highlight?
commentaire_seen_at = current_gestionnaire.commentaire_seen_at(@groupe_gestionnaire, @commentaire.sender)
commentaire_seen_at = current_gestionnaire.commentaire_seen_at(@groupe_gestionnaire, @commentaire.sender_id, @commentaire.sender_type)
commentaire_seen_at.nil? || commentaire_seen_at < @commentaire.created_at
end
end

View file

@ -3,6 +3,7 @@ module Administrateurs
before_action :retrieve_groupe_gestionnaire, only: [:show, :administrateurs, :gestionnaires, :commentaires, :create_commentaire]
def show
@unread_commentaires = current_administrateur.unread_commentaires?
end
def administrateurs
@ -12,13 +13,20 @@ module Administrateurs
end
def commentaires
@commentaire_seen_at = current_administrateur.commentaire_seen_at
@commentaire = CommentaireGroupeGestionnaire.new
current_administrateur.mark_commentaire_as_seen
end
def create_commentaire
@commentaire = @groupe_gestionnaire.commentaire_groupe_gestionnaires.create(commentaire_params.merge(sender: current_administrateur))
if @commentaire.errors.empty?
commentaire_url = gestionnaire_groupe_gestionnaire_commentaire_url(@groupe_gestionnaire, @commentaire)
@groupe_gestionnaire.gestionnaires.each do |gestionnaire|
GroupeGestionnaireMailer.notify_new_commentaire_groupe_gestionnaire(@groupe_gestionnaire, @commentaire, @commentaire.sender_email, gestionnaire.email, commentaire_url).deliver_later
end
current_administrateur.mark_commentaire_as_seen
flash.notice = "Message envoyé"
redirect_to admin_groupe_gestionnaire_commentaires_path
else

View file

@ -7,16 +7,18 @@ module Gestionnaires
end
def show
@commentaire_seen_at = current_gestionnaire.commentaire_seen_at(@groupe_gestionnaire, @last_commentaire.sender)
@commentaire_seen_at = current_gestionnaire.commentaire_seen_at(@groupe_gestionnaire, @last_commentaire.sender_id, @last_commentaire.sender_type)
@commentaire = CommentaireGroupeGestionnaire.new
current_gestionnaire.mark_commentaire_as_seen(@groupe_gestionnaire, @last_commentaire.sender)
current_gestionnaire.mark_commentaire_as_seen(@groupe_gestionnaire, @last_commentaire.sender_id, @last_commentaire.sender_type)
end
def create
@commentaire = @groupe_gestionnaire.commentaire_groupe_gestionnaires.create(commentaire_params.merge(sender: @last_commentaire.sender, gestionnaire: current_gestionnaire))
@commentaire = @groupe_gestionnaire.commentaire_groupe_gestionnaires.create(commentaire_params.merge(sender_id: @last_commentaire.sender_id, sender_type: @last_commentaire.sender_type, gestionnaire: current_gestionnaire))
if @commentaire.errors.empty?
GroupeGestionnaireMailer.notify_new_commentaire_groupe_gestionnaire(@groupe_gestionnaire, @commentaire, current_gestionnaire.email, @commentaire.sender_email, admin_groupe_gestionnaire_commentaires_path).deliver_later
flash.notice = "Message envoyé"
current_gestionnaire.mark_commentaire_as_seen(@groupe_gestionnaire, @commentaire.sender_id, @commentaire.sender_type)
redirect_to gestionnaire_groupe_gestionnaire_commentaire_path(@groupe_gestionnaire, @commentaire)
else
flash.alert = @commentaire.errors.full_messages
@ -27,6 +29,7 @@ module Gestionnaires
def destroy
if @last_commentaire.soft_deletable?(current_gestionnaire)
@last_commentaire.soft_delete!
@commentaire_seen_at = current_gestionnaire.commentaire_seen_at(@groupe_gestionnaire, @last_commentaire.sender_id, @last_commentaire.sender_type)
flash.notice = t('.notice')
else

View file

@ -1,4 +1,5 @@
class GroupeGestionnaireMailer < ApplicationMailer
helper MailerHelper
layout 'mailers/layout'
def notify_removed_gestionnaire(groupe_gestionnaire, removed_gestionnaire_email, current_super_admin_email)
@ -37,6 +38,16 @@ class GroupeGestionnaireMailer < ApplicationMailer
mail(bcc: added_administrateur_emails, subject: subject)
end
def notify_new_commentaire_groupe_gestionnaire(groupe_gestionnaire, commentaire, sender_email, recipient_email, commentaire_url)
@groupe_gestionnaire = groupe_gestionnaire
@commentaire = commentaire
@sender_email = sender_email
@commentaire_url = commentaire_url
@subject = "Vous avez un nouveau message dans le groupe gestionnaire \"#{groupe_gestionnaire.name}\""
mail(to: recipient_email, subject: @subject)
end
def self.critical_email?(action_name)
false
end

View file

@ -145,4 +145,12 @@ class Administrateur < ApplicationRecord
# required to display feature flags field in manager
def features
end
def unread_commentaires?
commentaire_seen_at.nil? || commentaire_seen_at < commentaire_groupe_gestionnaires.last.created_at
end
def mark_commentaire_as_seen
update(commentaire_seen_at: Time.zone.now)
end
end

View file

@ -44,17 +44,17 @@ class Gestionnaire < ApplicationRecord
.exists?
end
def commentaire_seen_at(groupe_gestionnaire, sender)
def commentaire_seen_at(groupe_gestionnaire, sender_id, sender_type)
FollowCommentaireGroupeGestionnaire
.where(gestionnaire: self, groupe_gestionnaire:, sender:)
.where(gestionnaire: self, groupe_gestionnaire:, sender_id:, sender_type:)
.order(id: :desc)
.last
&.commentaire_seen_at
end
def mark_commentaire_as_seen(groupe_gestionnaire, sender)
def mark_commentaire_as_seen(groupe_gestionnaire, sender_id, sender_type)
FollowCommentaireGroupeGestionnaire
.where(gestionnaire: self, groupe_gestionnaire: groupe_gestionnaire, sender: sender, unfollowed_at: nil)
.where(gestionnaire: self, groupe_gestionnaire:, sender_id:, sender_type:, unfollowed_at: nil)
.first_or_initialize.update(commentaire_seen_at: Time.zone.now)
end
end

View file

@ -9,5 +9,5 @@
%ul.messages-list{ data: { controller: 'scroll-to' } }
- current_administrateur.commentaire_groupe_gestionnaires.where(groupe_gestionnaire: @groupe_gestionnaire).each do |commentaire|
%li.message{ class: commentaire_is_from_me_class(commentaire, current_administrateur), id: dom_id(commentaire) }
= render(GroupeGestionnaire::GroupeGestionnaireCommentaires::CommentaireComponent.new(commentaire: commentaire, connected_user: current_administrateur, is_gestionnaire: false))
= render(GroupeGestionnaire::GroupeGestionnaireCommentaires::CommentaireComponent.new(commentaire: commentaire, connected_user: current_administrateur, is_gestionnaire: false, commentaire_seen_at: @commentaire_seen_at))
= render partial: "shared/groupe_gestionnaires/commentaires/form", locals: { commentaire: @commentaire, form_url: admin_groupe_gestionnaire_create_commentaire_path }

View file

@ -4,7 +4,7 @@
.fr-container
%h2= "Gestion du groupe gestionnaire \"#{@groupe_gestionnaire.name}\""
.fr-grid-row.fr-grid-row--gutters.fr-mb-5w
.fr-grid-row.fr-grid-row--gutters.fr-mb-5w.fr-groupe_gestionnaire_cards
= render GroupeGestionnaire::Card::GestionnairesComponent.new(groupe_gestionnaire: @groupe_gestionnaire, path: admin_groupe_gestionnaire_gestionnaires_path, is_gestionnaire: false)
= render GroupeGestionnaire::Card::AdministrateursComponent.new(groupe_gestionnaire: @groupe_gestionnaire, path: admin_groupe_gestionnaire_administrateurs_path, is_gestionnaire: false)
= render GroupeGestionnaire::Card::CommentairesComponent.new(groupe_gestionnaire: @groupe_gestionnaire, administrateur: current_administrateur, path: admin_groupe_gestionnaire_commentaires_path)
= render GroupeGestionnaire::Card::CommentairesComponent.new(groupe_gestionnaire: @groupe_gestionnaire, administrateur: current_administrateur, path: admin_groupe_gestionnaire_commentaires_path, unread_commentaires: @unread_commentaires)

View file

@ -1,8 +0,0 @@
.fr-container
%nav#header-navigation.fr-nav{ role: 'navigation', 'aria-label': 'Menu principal administrateur' }
%ul.fr-nav__list
%li.fr-nav__item= link_to 'Mes démarches', admin_procedures_path, class:'fr-nav__link', 'aria-current': current_page?(controller: 'procedures', action: :index) ? 'true' : nil
- if Rails.application.config.ds_zonage_enabled
%li.fr-nav__item= link_to 'Toutes les démarches', all_admin_procedures_path(zone_ids: current_administrateur.zones), class:'fr-nav__link', 'aria-current': current_page?(all_admin_procedures_path) ? 'page' : nil
- if current_administrateur.groupe_gestionnaire_id
%li.fr-nav__item= link_to 'Mon groupe gestionnaire', admin_groupe_gestionnaire_path, class:'fr-nav__link', 'aria-current': current_page?(admin_groupe_gestionnaire_path) ? 'page' : nil

View file

@ -1,3 +1,3 @@
- if @last_commentaire.discarded?
= turbo_stream.update @last_commentaire do
= render(GroupeGestionnaire::GroupeGestionnaireCommentaires::CommentaireComponent.new(commentaire: @last_commentaire, connected_user: current_gestionnaire))
= render(GroupeGestionnaire::GroupeGestionnaireCommentaires::CommentaireComponent.new(commentaire: @last_commentaire, connected_user: current_gestionnaire, commentaire_seen_at: @commentaire_seen_at))

View file

@ -0,0 +1,12 @@
- content_for(:title, "#{@subject}")
%p= t(:hello, scope: [:views, :shared, :greetings])
%p
= t('.body', groupe_gestionnaire_name: @groupe_gestionnaire.name, sender_email: @sender_email)
%p= round_button("consulter le message", @commentaire_url, :primary)
= render partial: "layouts/mailers/signature"
- content_for :footer do
= render 'layouts/mailers/commentaire_groupe_gestionnaire_footer'

View file

@ -0,0 +1,2 @@
%strong
= t('.do_not_reply_html', application_name: APPLICATION_NAME, sender_email: @sender_email)

View file

@ -0,0 +1,4 @@
fr:
groupe_gestionnaire_mailer:
notify_new_commentaire_groupe_gestionnaire:
body: Vous avez un nouveau message envoyé de la part de %{sender_email} dans la messagerie du groupe gestionnaire %{groupe_gestionnaire_name}

View file

@ -25,3 +25,5 @@ fr:
accepte: a été accepté le %{processed_at}.
refuse: a été refusé le %{processed_at}.
sans_suite: a été classé sans suite le %{processed_at}.
commentaire_groupe_gestionnaire_footer:
do_not_reply_html: Merci de ne pas répondre à cet email. Consultez votre message sur %{application_name} ou contactez votre expéditeur par <a href="mailto:%{sender_email}">mail</a>

View file

@ -0,0 +1,5 @@
class AddCommentaireSeenAtToAdministrateurs < ActiveRecord::Migration[7.0]
def change
add_column :administrateurs, :commentaire_seen_at, :datetime
end
end

View file

@ -61,6 +61,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_26_071130) do
end
create_table "administrateurs", id: :serial, force: :cascade do |t|
t.datetime "commentaire_seen_at"
t.datetime "created_at", precision: nil
t.bigint "groupe_gestionnaire_id"
t.datetime "updated_at", precision: nil
@ -97,6 +98,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_26_071130) do
t.datetime "created_at", null: false
t.string "encrypted_token", null: false
t.date "expiration_notices_sent_at", default: [], array: true
t.date "expires_at"
t.datetime "last_v1_authenticated_at"
t.datetime "last_v2_authenticated_at"
t.string "name", null: false
@ -104,7 +106,6 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_26_071130) do
t.datetime "updated_at", null: false
t.integer "version", default: 3, null: false
t.boolean "write_access", default: true, null: false
t.date "expires_at"
t.index ["administrateur_id"], name: "index_api_tokens_on_administrateur_id"
end
@ -266,15 +267,15 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_26_071130) do
create_table "commentaire_groupe_gestionnaires", force: :cascade do |t|
t.string "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "discarded_at", precision: 6
t.datetime "created_at", null: false
t.datetime "discarded_at", precision: nil
t.string "gestionnaire_email"
t.bigint "gestionnaire_id"
t.bigint "groupe_gestionnaire_id"
t.string "sender_email"
t.bigint "sender_id", null: false
t.string "sender_type", null: false
t.datetime "updated_at", precision: 6, null: false
t.datetime "updated_at", null: false
t.index ["gestionnaire_id"], name: "index_commentaire_groupe_gestionnaires_on_gestionnaire_id"
t.index ["groupe_gestionnaire_id"], name: "index_commentaire_groupe_gestionnaires_on_groupe_gestionnaire"
end
@ -414,8 +415,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_26_071130) do
create_table "dossier_transfers", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "email", null: false
t.datetime "updated_at", null: false
t.boolean "from_support", default: false, null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_dossier_transfers_on_email"
end
@ -610,14 +611,14 @@ ActiveRecord::Schema[7.0].define(version: 2024_01_26_071130) do
end
create_table "follow_commentaire_groupe_gestionnaires", force: :cascade do |t|
t.datetime "commentaire_seen_at", precision: 6
t.datetime "created_at", precision: 6, null: false
t.datetime "commentaire_seen_at", precision: nil
t.datetime "created_at", null: false
t.bigint "gestionnaire_id", null: false
t.bigint "groupe_gestionnaire_id"
t.bigint "sender_id"
t.string "sender_type"
t.datetime "unfollowed_at", precision: 6, precision: nil
t.datetime "updated_at", precision: 6, null: false
t.datetime "unfollowed_at", precision: nil
t.datetime "updated_at", null: false
t.index ["gestionnaire_id", "groupe_gestionnaire_id", "sender_id", "sender_type", "unfollowed_at"], name: "index_follow_commentaire_on_groupe_gestionnaire_unfollow", unique: true
t.index ["gestionnaire_id"], name: "index_follow_commentaire_on_gestionnaire"
t.index ["groupe_gestionnaire_id"], name: "index_follow_commentaire_on_groupe_gestionnaire"

View file

@ -54,4 +54,21 @@ RSpec.describe GroupeGestionnaireMailer, type: :mailer do
it { expect(subject.body).to include('Vous venez dêtre nommé administrateur du groupe gestionnaire') }
it { expect(subject.bcc).to match_array(['int3@g', 'int4@g']) }
end
describe '#notify_new_commentaire_groupe_gestionnaire' do
let(:groupe_gestionnaire) { create(:groupe_gestionnaire) }
let(:gestionnaire) { create(:gestionnaire, email: 'int3@g') }
let(:admin) { create(:administrateur, email: 'toto@email.com') }
let(:commentaire) { create(:commentaire_groupe_gestionnaire, sender: admin) }
let(:commentaire_url) { gestionnaire_groupe_gestionnaire_commentaire_url(groupe_gestionnaire, commentaire) }
subject { described_class.notify_new_commentaire_groupe_gestionnaire(groupe_gestionnaire, commentaire, admin.email, gestionnaire.email, commentaire_url) }
it { expect(subject.body).to include('Vous avez un nouveau message dans le groupe gestionnaire') }
it { expect(subject.to).to match_array(['int3@g']) }
end
end

View file

@ -27,6 +27,15 @@ class GroupeGestionnaireMailerPreview < ActionMailer::Preview
GroupeGestionnaireMailer.notify_added_administrateurs(groupe_gestionnaire, administrateurs, current_super_admin_email)
end
def notify_new_commentaire_groupe_gestionnaire
groupe_gestionnaire = GroupeGestionnaire.new(id: 1, name: 'un groupe gestionnaire')
commentaire = CommentaireGroupeGestionnaire.new(id: 1)
admin_email = 'admin@dgfip.com'
gestionnaire = Gestionnaire.new(user: user)
commentaire_url = Rails.application.routes.url_helpers.gestionnaire_groupe_gestionnaire_commentaire_url(groupe_gestionnaire, commentaire)
GroupeGestionnaireMailer.notify_new_commentaire_groupe_gestionnaire(groupe_gestionnaire, commentaire, admin_email, gestionnaire.email, commentaire_url)
end
private
def user

View file

@ -285,4 +285,55 @@ describe Administrateur, type: :model do
expect(admin.zones).to eq [zone1, zone2]
end
end
describe "#unread_commentaires?" do
context "commentaire_seen_at is nil" do
let(:gestionnaire) { create(:gestionnaire) }
let(:administrateur) { create(:administrateur) }
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
let!(:commentaire_groupe_gestionnaire) { create(:commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, sender: administrateur, gestionnaire: gestionnaire, created_at: 12.hours.ago) }
it do
expect(administrateur.unread_commentaires?).to eq true
end
end
context "commentaire_seen_at before last commentaire" do
let(:gestionnaire) { create(:gestionnaire) }
let(:administrateur) { create(:administrateur, commentaire_seen_at: 1.day.ago) }
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
let!(:commentaire_groupe_gestionnaire) { create(:commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, sender: administrateur, gestionnaire: gestionnaire, created_at: 12.hours.ago) }
it do
expect(administrateur.unread_commentaires?).to eq true
end
end
context "commentaire_seen_at after last commentaire" do
let(:gestionnaire) { create(:gestionnaire) }
let(:administrateur) { create(:administrateur, commentaire_seen_at: 1.hour.ago) }
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
let!(:commentaire_groupe_gestionnaire) { create(:commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, sender: administrateur, gestionnaire: gestionnaire, created_at: 12.hours.ago) }
it do
expect(administrateur.unread_commentaires?).to eq false
end
end
end
describe "#mark_commentaire_as_seen" do
let(:now) { Time.zone.now.beginning_of_minute }
let(:gestionnaire) { create(:gestionnaire) }
let(:administrateur) { create(:administrateur) }
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
let!(:commentaire_groupe_gestionnaire) { create(:commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, sender: administrateur, created_at: 12.hours.ago) }
before do
Timecop.freeze(now) do
administrateur.mark_commentaire_as_seen
end
end
it { expect(administrateur.commentaire_seen_at).to eq now }
end
end

View file

@ -133,7 +133,7 @@ describe Gestionnaire, type: :model do
let!(:commentaire_groupe_gestionnaire) { create(:commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, sender: administrateur, created_at: 12.hours.ago) }
let!(:follow_commentaire_groupe_gestionnaire) { create(:follow_commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, gestionnaire: gestionnaire, sender: administrateur, commentaire_seen_at: Time.zone.now) }
it { expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur).to_date).to eq Date.current }
it { expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur.id, "Administrateur").to_date).to eq Date.current }
end
context "when never seen commentaire" do
@ -142,7 +142,7 @@ describe Gestionnaire, type: :model do
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
let!(:commentaire_groupe_gestionnaire) { create(:commentaire_groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire, sender: administrateur, created_at: 12.hours.ago) }
it { expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur)).to eq nil }
it { expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur.id, "Administrateur")).to eq nil }
end
end
@ -157,13 +157,13 @@ describe Gestionnaire, type: :model do
subject do
travel_to(now) do
gestionnaire.mark_commentaire_as_seen(groupe_gestionnaire, administrateur)
gestionnaire.mark_commentaire_as_seen(groupe_gestionnaire, administrateur.id, "Administrateur")
end
end
it do
subject
expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur)).to eq now
expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur.id, "Administrateur")).to eq now
end
end
@ -176,13 +176,13 @@ describe Gestionnaire, type: :model do
subject do
travel_to(now) do
gestionnaire.mark_commentaire_as_seen(groupe_gestionnaire, administrateur)
gestionnaire.mark_commentaire_as_seen(groupe_gestionnaire, administrateur.id, "Administrateur")
end
end
it do
subject
expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur)).to eq now
expect(gestionnaire.commentaire_seen_at(groupe_gestionnaire, administrateur.id, "Administrateur")).to eq now
end
end
end