Merge pull request #9571 from adullact/feature-ouidou/admin_creation_delegation_gestionnaire_page_group_administrateur_management

Feature ouidou/admin creation delegation gestionnaire page group administrateur management
This commit is contained in:
Colin Darie 2023-12-12 10:41:28 +00:00 committed by GitHub
commit f9c4846fde
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 626 additions and 174 deletions

View file

@ -0,0 +1,5 @@
class GroupeGestionnaire::Card::AdministrateursComponent < ApplicationComponent
def initialize(groupe_gestionnaire:)
@groupe_gestionnaire = groupe_gestionnaire
end
end

View file

@ -0,0 +1,5 @@
---
fr:
title:
one: Administrateur
other: Administrateurs

View file

@ -0,0 +1,10 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to gestionnaire_groupe_gestionnaire_administrateurs_path(@groupe_gestionnaire), id: 'administrateurs', class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.column.align-center.justify-between
%p.fr-badge.fr-badge--success Validé
%div
.line-count.fr-my-1w
%p.fr-tag= @groupe_gestionnaire.administrateurs.size
%h3.fr-h6
= t('.title', count: @groupe_gestionnaire.administrateurs.size)
%p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit')

View file

@ -0,0 +1,52 @@
class GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent < ApplicationComponent
include ApplicationHelper
def initialize(groupe_gestionnaire:, administrateur:)
@groupe_gestionnaire = groupe_gestionnaire
@administrateur = administrateur
end
def email
if @administrateur == current_gestionnaire
"#{@administrateur.email} (Cest vous !)"
else
@administrateur.email
end
end
def created_at
try_format_datetime(@administrateur.created_at)
end
def registration_state
@administrateur.registration_state
end
def remove_button
if is_there_at_least_another_active_admin?
button_to 'Retirer',
remove_gestionnaire_groupe_gestionnaire_administrateur_path(@groupe_gestionnaire, @administrateur),
method: :delete,
class: 'fr-btn fr-btn--sm fr-btn--tertiary',
form: { data: { turbo: true, turbo_confirm: "Retirer « #{@administrateur.email} » des administrateurs de « #{@groupe_gestionnaire.name} » ?" } }
end
end
def destroy_button
if is_there_at_least_another_active_admin?
button_to 'Supprimer',
gestionnaire_groupe_gestionnaire_administrateur_path(@groupe_gestionnaire, @administrateur),
method: :delete,
class: 'fr-btn fr-btn--sm fr-btn--tertiary',
form: { data: { turbo: true, turbo_confirm: "Supprimer « #{@administrateur.email} » en tant qu'administrateurs ?" } }
end
end
def is_there_at_least_another_active_admin?
if @administrateur.active?
@groupe_gestionnaire.administrateurs.count(&:active?) > 1
else
@groupe_gestionnaire.administrateurs.count(&:active?) >= 1
end
end
end

View file

@ -0,0 +1,6 @@
%tr{ id: dom_id(@administrateur) }
%td= email
%td= created_at
%td= registration_state
%td= remove_button
%td= destroy_button

View file

@ -27,7 +27,7 @@ class GroupeGestionnaire::GroupeGestionnaireGestionnaires::GestionnaireComponent
button_to 'Retirer',
gestionnaire_groupe_gestionnaire_gestionnaire_path(@groupe_gestionnaire, @gestionnaire),
method: :delete,
class: 'button',
class: 'fr-btn fr-btn--sm fr-btn--tertiary',
form: { data: { turbo: true, turbo_confirm: "Retirer « #{@gestionnaire.email} » des gestionnaires de « #{@groupe_gestionnaire.name} » ?" } }
end
end

View file

@ -0,0 +1,99 @@
module Gestionnaires
class GroupeGestionnaireAdministrateursController < GestionnaireController
before_action :retrieve_groupe_gestionnaire
def index
end
def create
emails = [params.require(:administrateur)[:email]].to_json
emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) }
administrateurs_to_add, valid_emails, invalid_emails = Administrateur.find_all_by_identifier_with_emails(emails:)
not_found_emails = valid_emails - administrateurs_to_add.map(&:email)
# Send invitations to users without account
if not_found_emails.present?
administrateurs_to_add += not_found_emails.map do |email|
user = User.create_or_promote_to_administrateur(email, SecureRandom.hex)
user.invite_administrateur!(@groupe_gestionnaire)
user.administrateur
end
end
administrateurs_already_in_groupe_gestionnaire = []
# We dont't want to assign a user to an groupe_gestionnaire if they are already assigned to it
administrateurs_duplicate = administrateurs_to_add & @groupe_gestionnaire.administrateurs
administrateurs_to_add -= @groupe_gestionnaire.administrateurs
administrateurs_to_add.each do |administrateur|
# We don't change administrateur.groupe_gestionnaire_id is administrateur already in another groupe_gestionnaire for which current_gestionnaire is not a gestionnaire or if current_gestionnaire is not a superAdmin
if !current_gestionnaire.is_a?(SuperAdmin) &&
administrateur.groupe_gestionnaire_id &&
((administrateur.groupe_gestionnaire.ancestor_ids + [administrateur.groupe_gestionnaire_id]) & current_gestionnaire.groupe_gestionnaire_ids).empty?
administrateurs_already_in_groupe_gestionnaire << administrateur
next
end
@groupe_gestionnaire.add_administrateur(administrateur)
end
if administrateurs_already_in_groupe_gestionnaire.present?
flash[:alert] = I18n.t('activerecord.errors.administrateurs_already_in_groupe_gestionnaire',
count: administrateurs_already_in_groupe_gestionnaire.size,
emails: administrateurs_already_in_groupe_gestionnaire)
end
if invalid_emails.present?
flash[:alert] = I18n.t('activerecord.wrong_address',
count: invalid_emails.size,
emails: invalid_emails.join(', '))
end
if administrateurs_duplicate.present?
flash[:alert] = I18n.t('activerecord.errors.duplicate_email',
count: invalid_emails.size,
emails: administrateurs_duplicate.map(&:email).join(', '))
end
if administrateurs_to_add.present?
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_administrateur.create')
GroupeGestionnaireMailer
.notify_added_administrateurs(@groupe_gestionnaire, administrateurs_to_add, current_gestionnaire.email)
.deliver_later
end
@administrateur = administrateurs_to_add[0]
end
def destroy
@administrateur = Administrateur.find(params[:id])
if @groupe_gestionnaire.id != @administrateur.groupe_gestionnaire_id
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_administrateur.not_in_groupe_gestionnaire', email: @administrateur.email)
else
result = AdministrateurDeletionService.new(current_gestionnaire, @administrateur).call
case result
in Dry::Monads::Result::Success
logger.info("L'administrateur #{@administrateur.id} est supprimé par le gestionnaire #{current_gestionnaire.id} depuis le groupe gestionnaire #{@groupe_gestionnaire.id}")
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_administrateur.destroy', email: @administrateur.email)
GroupeGestionnaireMailer
.notify_removed_administrateur(@groupe_gestionnaire, @administrateur.email, current_gestionnaire.email)
.deliver_later
in Dry::Monads::Result::Failure(reason)
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_administrateur.cannot_be_deleted', email: @administrateur.email)
end
end
end
def remove
@administrateur = Administrateur.find(params[:id])
if @groupe_gestionnaire.id != @administrateur.groupe_gestionnaire_id
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_administrateur.not_in_groupe_gestionnaire', email: @administrateur.email)
else
@administrateur.update(groupe_gestionnaire_id: nil)
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_administrateur.remove', email: @administrateur.email)
GroupeGestionnaireMailer
.notify_removed_administrateur(@groupe_gestionnaire, @administrateur.email, current_gestionnaire.email)
.deliver_later
end
end
end
end

View file

@ -1,6 +1,6 @@
module Gestionnaires
class GroupeGestionnaireChildrenController < GestionnaireController
before_action :retrieve_groupe_gestionnaire, except: [:new]
before_action :retrieve_groupe_gestionnaire
def index
end

View file

@ -1,17 +1,71 @@
module Gestionnaires
class GroupeGestionnaireGestionnairesController < GestionnaireController
before_action :retrieve_groupe_gestionnaire, except: [:new]
before_action :retrieve_groupe_gestionnaire
def index
end
def create
gestionnaires, flash[:alert], flash[:notice] = @groupe_gestionnaire.add_gestionnaires(emails: [params.require(:gestionnaire)[:email]], current_user: current_gestionnaire)
@gestionnaire = gestionnaires[0]
emails = [params.require(:gestionnaire)[:email]].to_json
emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) }
gestionnaires_to_add, valid_emails, invalid_emails = Gestionnaire.find_all_by_identifier_with_emails(emails:)
not_found_emails = valid_emails - gestionnaires_to_add.map(&:email)
# Send invitations to users without account
if not_found_emails.present?
gestionnaires_to_add += not_found_emails.map do |email|
user = User.create_or_promote_to_gestionnaire(email, SecureRandom.hex)
user.invite_gestionnaire!(@groupe_gestionnaire)
user.gestionnaire
end
end
# We dont't want to assign a user to an groupe_gestionnaire if they are already assigned to it
gestionnaires_duplicate = gestionnaires_to_add & @groupe_gestionnaire.gestionnaires
gestionnaires_to_add -= @groupe_gestionnaire.gestionnaires
gestionnaires_to_add.each { @groupe_gestionnaire.add_gestionnaire(_1) }
if invalid_emails.present?
flash[:alert] = I18n.t('activerecord.wrong_address',
count: invalid_emails.size,
emails: invalid_emails.join(', '))
end
if gestionnaires_duplicate.present?
flash[:alert] = I18n.t('activerecord.errors.duplicate_email',
count: invalid_emails.size,
emails: gestionnaires_duplicate.map(&:email).join(', '))
end
if gestionnaires_to_add.present?
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_gestionnaire.create')
GroupeGestionnaireMailer
.notify_added_gestionnaires(@groupe_gestionnaire, gestionnaires_to_add, current_gestionnaire.email)
.deliver_later
end
@gestionnaire = gestionnaires_to_add[0]
end
def destroy
@gestionnaire, flash[:alert], flash[:notice] = @groupe_gestionnaire.remove(params[:id], current_gestionnaire)
if !@groupe_gestionnaire.is_root? || @groupe_gestionnaire.gestionnaires.one?
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_gestionnaire.destroy_at_least_one')
else
@gestionnaire = Gestionnaire.find(params[:id])
if !@groupe_gestionnaire.in?(@gestionnaire.groupe_gestionnaires) || !@gestionnaire.groupe_gestionnaires.destroy(@groupe_gestionnaire)
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_gestionnaire.not_in_groupe_gestionnaire', email: @gestionnaire.email)
else
if @gestionnaire.groupe_gestionnaires.empty?
@gestionnaire.destroy
end
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_gestionnaire.destroy', email: @gestionnaire.email)
GroupeGestionnaireMailer
.notify_removed_gestionnaire(@groupe_gestionnaire, @gestionnaire.email, current_gestionnaire.email)
.deliver_later
end
end
end
end
end

View file

@ -1,25 +1,70 @@
module Manager
class GroupeGestionnairesController < Manager::ApplicationController
def add_gestionnaire
_gestionnaires, flash[:alert], flash[:notice] = groupe_gestionnaire.add_gestionnaires(emails: (params['emails'].presence || '').split(','), current_user: current_super_admin)
groupe_gestionnaire = GroupeGestionnaire.find(params[:id])
emails = [params['emails'].presence || ''].to_json
emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) }
gestionnaires_to_add, valid_emails, invalid_emails = Gestionnaire.find_all_by_identifier_with_emails(emails:)
not_found_emails = valid_emails - gestionnaires_to_add.map(&:email)
# Send invitations to users without account
if not_found_emails.present?
gestionnaires_to_add += not_found_emails.map do |email|
user = User.create_or_promote_to_gestionnaire(email, SecureRandom.hex)
user.invite_gestionnaire!(groupe_gestionnaire)
user.gestionnaire
end
end
# We dont't want to assign a user to an groupe_gestionnaire if they are already assigned to it
gestionnaires_duplicate = gestionnaires_to_add & groupe_gestionnaire.gestionnaires
gestionnaires_to_add -= groupe_gestionnaire.gestionnaires
gestionnaires_to_add.each { groupe_gestionnaire.add_gestionnaire(_1) }
if invalid_emails.present?
flash[:alert] = I18n.t('activerecord.wrong_address',
count: invalid_emails.size,
emails: invalid_emails.join(', '))
end
if gestionnaires_duplicate.present?
flash[:alert] = I18n.t('activerecord.errors.duplicate_email',
count: invalid_emails.size,
emails: gestionnaires_duplicate.map(&:email).join(', '))
end
if gestionnaires_to_add.present?
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_gestionnaire.create')
GroupeGestionnaireMailer
.notify_added_gestionnaires(groupe_gestionnaire, gestionnaires_to_add, current_super_admin.email)
.deliver_later
end
redirect_to manager_groupe_gestionnaire_path(groupe_gestionnaire)
end
def remove_gestionnaire
_gestionnaire, flash[:alert], flash[:notice] = groupe_gestionnaire.remove(gestionnaire_id, current_super_admin)
groupe_gestionnaire = GroupeGestionnaire.find(params[:id])
if !groupe_gestionnaire.is_root? || groupe_gestionnaire.gestionnaires.one?
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_gestionnaire.destroy_at_least_one')
else
gestionnaire = Gestionnaire.find(params[:gestionnaire][:id])
if !groupe_gestionnaire.in?(gestionnaire.groupe_gestionnaires) || !gestionnaire.groupe_gestionnaires.destroy(groupe_gestionnaire)
flash[:alert] = I18n.t('groupe_gestionnaires.flash.alert.groupe_gestionnaire_gestionnaire.not_in_groupe_gestionnaire', email: gestionnaire.email)
else
if gestionnaire.groupe_gestionnaires.empty?
gestionnaire.destroy
end
flash[:notice] = I18n.t('groupe_gestionnaires.flash.notice.groupe_gestionnaire_gestionnaire.destroy', email: gestionnaire.email)
GroupeGestionnaireMailer
.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire.email, current_super_admin.email)
.deliver_later
end
end
redirect_to manager_groupe_gestionnaire_path(groupe_gestionnaire)
end
private
def groupe_gestionnaire
@groupe_gestionnaire ||= GroupeGestionnaire.find(params[:id])
end
def gestionnaire_id
params[:gestionnaire][:id]
end
end
end

View file

@ -1,12 +1,12 @@
class GroupeGestionnaireMailer < ApplicationMailer
layout 'mailers/layout'
def notify_removed_gestionnaire(groupe_gestionnaire, removed_gestionnaire, current_super_admin_email)
def notify_removed_gestionnaire(groupe_gestionnaire, removed_gestionnaire_email, current_super_admin_email)
@groupe_gestionnaire = groupe_gestionnaire
@current_super_admin_email = current_super_admin_email
subject = "Vous avez été retiré(e) du groupe gestionnaire \"#{groupe_gestionnaire.name}\""
mail(to: removed_gestionnaire.email, subject: subject)
mail(to: removed_gestionnaire_email, subject: subject)
end
def notify_added_gestionnaires(groupe_gestionnaire, added_gestionnaires, current_super_admin_email)
@ -19,6 +19,24 @@ class GroupeGestionnaireMailer < ApplicationMailer
mail(bcc: added_gestionnaire_emails, subject: subject)
end
def notify_removed_administrateur(groupe_gestionnaire, removed_administrateur_email, current_super_admin_email)
@groupe_gestionnaire = groupe_gestionnaire
@current_super_admin_email = current_super_admin_email
subject = "Vous avez été retiré(e) du groupe gestionnaire \"#{groupe_gestionnaire.name}\""
mail(to: removed_administrateur_email, subject: subject)
end
def notify_added_administrateurs(groupe_gestionnaire, added_administrateurs, current_super_admin_email)
added_administrateur_emails = added_administrateurs.map(&:email)
@groupe_gestionnaire = groupe_gestionnaire
@current_super_admin_email = current_super_admin_email
subject = "Vous avez été ajouté(e) en tant qu'administrateur du groupe gestionnaire \"#{groupe_gestionnaire.name}\""
mail(bcc: added_administrateur_emails, subject: subject)
end
def self.critical_email?(action_name)
false
end

View file

@ -1,4 +1,5 @@
class Administrateur < ApplicationRecord
include UserFindByConcern
UNUSED_ADMIN_THRESHOLD = ENV.fetch('UNUSED_ADMIN_THRESHOLD') { 6 }.to_i.months
has_and_belongs_to_many :instructeurs
@ -27,10 +28,6 @@ class Administrateur < ApplicationRecord
.merge(APIToken.where(last_v2_authenticated_at: nil).or(APIToken.where(last_v2_authenticated_at: ..UNUSED_ADMIN_THRESHOLD.ago)))
end
def self.by_email(email)
Administrateur.find_by(users: { email: email })
end
def email
user&.email
end

View file

@ -0,0 +1,23 @@
module UserFindByConcern
extend ActiveSupport::Concern
included do
def self.by_email(email)
find_by(users: { email: email })
end
def self.find_all_by_identifier(ids: [], emails: [])
find_all_by_identifier_with_emails(ids:, emails:).first
end
def self.find_all_by_identifier_with_emails(ids: [], emails: [])
valid_emails, invalid_emails = emails.partition { Devise.email_regexp.match?(_1) }
[
where(id: ids).or(where(users: { email: valid_emails })).distinct(:id),
valid_emails,
invalid_emails
]
end
end
end

View file

@ -1,4 +1,5 @@
class Gestionnaire < ApplicationRecord
include UserFindByConcern
has_and_belongs_to_many :groupe_gestionnaires
belongs_to :user
@ -7,10 +8,6 @@ class Gestionnaire < ApplicationRecord
default_scope { eager_load(:user) }
def self.by_email(email)
find_by(users: { email: email })
end
def email
user&.email
end
@ -19,20 +16,6 @@ class Gestionnaire < ApplicationRecord
user&.active?
end
def self.find_all_by_identifier(ids: [], emails: [])
find_all_by_identifier_with_emails(ids:, emails:).first
end
def self.find_all_by_identifier_with_emails(ids: [], emails: [])
valid_emails, invalid_emails = emails.partition { URI::MailTo::EMAIL_REGEXP.match?(_1) }
[
where(id: ids).or(where(users: { email: valid_emails })).distinct(:id),
valid_emails,
invalid_emails
]
end
def can_be_deleted?
groupe_gestionnaires.roots.each do |rt|
return false unless rt.gestionnaires.size > 1

View file

@ -4,75 +4,18 @@ class GroupeGestionnaire < ApplicationRecord
has_ancestry
def add(gestionnaire)
def add_gestionnaire(gestionnaire)
return if gestionnaire.nil?
return if in?(gestionnaire.groupe_gestionnaires)
gestionnaires << gestionnaire
end
def remove(gestionnaire_id, current_user)
if !self.is_root? || self.gestionnaires.one?
alert = "Suppression impossible : il doit y avoir au moins un gestionnaire dans le groupe racine"
else
gestionnaire = Gestionnaire.find(gestionnaire_id)
def add_administrateur(administrateur)
return if administrateur.nil?
return if id == administrateur.groupe_gestionnaire_id
if gestionnaire.nil? || !in?(gestionnaire.groupe_gestionnaires) || !gestionnaire.groupe_gestionnaires.destroy(self)
alert = "Le gestionnaire « #{gestionnaire.email} » nest pas dans le groupe."
else
if gestionnaire.groupe_gestionnaires.empty?
gestionnaire.destroy
end
notice = "Le gestionnaire « #{gestionnaire.email} » a été retiré du groupe."
GroupeGestionnaireMailer
.notify_removed_gestionnaire(self, gestionnaire, current_user.email)
.deliver_later
end
end
[gestionnaire, alert, notice]
end
def add_gestionnaires(ids: [], emails: [], current_user: nil)
emails = emails.to_json
emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) }
gestionnaires_to_add, valid_emails, invalid_emails = Gestionnaire.find_all_by_identifier_with_emails(ids:, emails:)
not_found_emails = valid_emails - gestionnaires_to_add.map(&:email)
# Send invitations to users without account
if not_found_emails.present?
gestionnaires_to_add += not_found_emails.map do |email|
user = User.create_or_promote_to_gestionnaire(email, SecureRandom.hex)
user.invite_gestionnaire!(self)
user.gestionnaire
end
end
# We dont't want to assign a user to an groupe_gestionnaire if they are already assigned to it
gestionnaires_duplicate = gestionnaires_to_add & gestionnaires
gestionnaires_to_add -= gestionnaires
gestionnaires_to_add.each { add(_1) }
if invalid_emails.present?
alert = I18n.t('activerecord.wrong_address',
count: invalid_emails.size,
emails: invalid_emails)
end
if gestionnaires_duplicate.present?
alert = I18n.t('activerecord.errors.duplicate_email',
count: invalid_emails.size,
emails: gestionnaires_duplicate.map(&:email))
end
if gestionnaires_to_add.present?
notice = "Les gestionnaires ont bien été affectés au groupe gestionnaire"
GroupeGestionnaireMailer
.notify_added_gestionnaires(self, gestionnaires_to_add, current_user.email)
.deliver_later
end
[gestionnaires_to_add, alert, notice]
administrateurs << administrateur
end
def can_be_deleted?(current_user)

View file

@ -1,4 +1,5 @@
class Instructeur < ApplicationRecord
include UserFindByConcern
has_and_belongs_to_many :administrateurs
has_many :assign_to, dependent: :destroy
@ -36,24 +37,6 @@ class Instructeur < ApplicationRecord
default_scope { eager_load(:user) }
def self.by_email(email)
find_by(users: { email: email })
end
def self.find_all_by_identifier(ids: [], emails: [])
find_all_by_identifier_with_emails(ids:, emails:).first
end
def self.find_all_by_identifier_with_emails(ids: [], emails: [])
valid_emails, invalid_emails = emails.partition { URI::MailTo::EMAIL_REGEXP.match?(_1) }
[
where(id: ids).or(where(users: { email: valid_emails })).distinct(:id),
valid_emails,
invalid_emails
]
end
def email
user.email
end

View file

@ -0,0 +1,13 @@
= form_for groupe_gestionnaire.administrateurs.new(user: User.new),
url: { controller: 'groupe_gestionnaire_administrateurs' },
html: { id: "new_administrateur" },
data: { turbo: true, turbo_force: :server } do |f|
.fr-input-group
= f.label :email, class: "fr-label" do
Ajouter un administrateur
%span.fr-hint-text
= "Renseignez lemail dun administrateur pour lui permettre de gérer le groupe « #{groupe_gestionnaire.name} ». Exemple : marie.dupont@exemple.fr"
= f.email_field :email, required: true, class: "fr-input", autofocus: true
= f.submit 'Ajouter comme administrateur', class: 'fr-btn'

View file

@ -0,0 +1,5 @@
- if @administrateur.present?
= turbo_stream.update 'administrateurs' do
= render GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire)
= turbo_stream.replace "new_administrateur", partial: 'add_admin_form', locals: { groupe_gestionnaire: @groupe_gestionnaire }
= turbo_stream.focus 'administrateur_email'

View file

@ -0,0 +1,6 @@
= turbo_stream.update 'administrateurs' do
= render GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire)
- if @groupe_gestionnaire.administrateurs.one?
= turbo_stream.focus 'administrateur_email'
- else
= turbo_stream.focus_all '#administrateurs tr:first-child input[type="submit"]'

View file

@ -0,0 +1,22 @@
= render 'gestionnaires/groupe_gestionnaires/main_navigation'
= render partial: 'gestionnaires/breadcrumbs',
locals: { steps: [['Groupes gestionnaires', gestionnaire_groupe_gestionnaires_path],
["#{@groupe_gestionnaire.name.truncate_words(10)}", gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire)],
['Administrateurs']], preview: false }
.container
%h1 Gérer les administrateurs de « #{@groupe_gestionnaire.name} »
%table.table
%thead
%tr
%th= 'Adresse email'
%th= 'Enregistré le'
%th= 'État'
%th
%th
%tbody#administrateurs
= render(GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire))
.fr-mt-4w
= render 'add_admin_form', groupe_gestionnaire: @groupe_gestionnaire

View file

@ -0,0 +1,6 @@
= turbo_stream.update 'administrateurs' do
= render GroupeGestionnaire::GroupeGestionnaireAdministrateurs::AdministrateurComponent.with_collection(@groupe_gestionnaire.administrateurs.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire)
- if @groupe_gestionnaire.administrateurs.one?
= turbo_stream.focus 'administrateur_email'
- else
= turbo_stream.focus_all '#administrateurs tr:first-child input[type="submit"]'

View file

@ -1,5 +1,6 @@
= render 'gestionnaires/groupe_gestionnaires/main_navigation'
= render partial: 'gestionnaires/breadcrumbs',
locals: { steps: [['Groupes gestionnaire', gestionnaire_groupe_gestionnaires_path],
locals: { steps: [['Groupes gestionnaires', gestionnaire_groupe_gestionnaires_path],
["#{@groupe_gestionnaire.name.truncate_words(10)}", gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire)],
['Groupes enfants']], preview: false }

View file

@ -1,5 +1,6 @@
= render 'gestionnaires/groupe_gestionnaires/main_navigation'
= render partial: 'gestionnaires/breadcrumbs',
locals: { steps: [['Groupes gestionnaire', gestionnaire_groupe_gestionnaires_path],
locals: { steps: [['Groupes gestionnaires', gestionnaire_groupe_gestionnaires_path],
["#{@groupe_gestionnaire.name.truncate_words(10)}", gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire)],
['Gestionnaires']], preview: false }

View file

@ -1,4 +1,4 @@
- content_for(:main_navigation) do
%nav#header-navigation.fr-nav{ role: 'navigation', 'aria-label': 'Menu principal gestionnaire' }
%ul.fr-nav__list
%li.fr-nav__item= link_to 'Mes groupes gestionnaire', gestionnaire_groupe_gestionnaires_path, class:'fr-nav__link', 'aria-current': current_page?(controller: 'groupe_gestionnaires', action: :index) ? 'page' : nil
%li.fr-nav__item= link_to 'Mes groupes gestionnaires', gestionnaire_groupe_gestionnaires_path, class:'fr-nav__link', 'aria-current': current_page?(controller: 'groupe_gestionnaires', action: :index) ? 'page' : nil

View file

@ -1,7 +1,8 @@
- content_for(:root_class, 'scroll-margins-for-sticky-footer')
= render 'main_navigation'
= render partial: 'gestionnaires/breadcrumbs',
locals: { steps: [['Groupes gestionnaire', gestionnaire_groupe_gestionnaires_path],
locals: { steps: [['Groupes gestionnaires', gestionnaire_groupe_gestionnaires_path],
["#{@groupe_gestionnaire.name.truncate_words(10)}", gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire)],
['Edit']] }

View file

@ -1,6 +1,9 @@
= render 'main_navigation'
.sub-header
= render partial: 'gestionnaires/breadcrumbs',
locals: { steps: [['Groupes gestionnaires', gestionnaire_groupe_gestionnaires_path]] }
.fr-container.procedure-admin-container
.fr-container#groupe_gestionnaire
%table.fr-table.width-100.mt-3

View file

@ -1,5 +1,7 @@
= render 'main_navigation'
= render partial: 'gestionnaires/breadcrumbs',
locals: { steps: [['Groupes gestionnaire', gestionnaire_groupe_gestionnaires_path],
locals: { steps: [['Groupes gestionnaires', gestionnaire_groupe_gestionnaires_path],
["#{@groupe_gestionnaire.name.truncate_words(10)}"]],
metadatas: true }
@ -18,4 +20,5 @@
%a{ href: gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire.groupe_gestionnaire) }= @groupe_gestionnaire.groupe_gestionnaire.name
.fr-grid-row.fr-grid-row--gutters.fr-mb-5w
= render GroupeGestionnaire::Card::GestionnairesComponent.new(groupe_gestionnaire: @groupe_gestionnaire)
= render GroupeGestionnaire::Card::AdministrateursComponent.new(groupe_gestionnaire: @groupe_gestionnaire)
= render GroupeGestionnaire::Card::ChildrenComponent.new(groupe_gestionnaire: @groupe_gestionnaire)

View file

@ -0,0 +1,6 @@
%p= t(:hello, scope: [:views, :shared, :greetings])
%p
= t(".email_body", groupe_gestionnaire_name: @groupe_gestionnaire.name, email: @current_super_admin_email, application_name: APPLICATION_NAME)
= render partial: "layouts/mailers/signature"

View file

@ -0,0 +1,6 @@
%p= t(:hello, scope: [:views, :shared, :greetings])
%p
= t(".email_body", groupe_gestionnaire_name: @groupe_gestionnaire.name, email: @current_super_admin_email, application_name: APPLICATION_NAME)
= render partial: "layouts/mailers/signature"

View file

@ -886,6 +886,23 @@ fr:
invalid_password: "Mauvais mot de passe"
connection_done: "Les comptes FranceConnect et %{application_name} sont à présent fusionnés"
merger_token_expired: "Le délai pour fusionner les comptes FranceConnect et %{application_name} est expirée. Veuillez recommencer la procédure pour vous fusionner les comptes."
groupe_gestionnaires:
flash:
alert:
groupe_gestionnaire_administrateur:
cannot_be_deleted: "L'administrateur « %{email} » ne peut pas être supprimé du groupe gestionnaire."
not_in_groupe_gestionnaire: "L'administrateur « %{email} » nest pas dans le groupe gestionnaire."
groupe_gestionnaire_gestionnaire:
destroy_at_least_one: "Suppression impossible : il doit y avoir au moins un gestionnaire dans le groupe racine"
not_in_groupe_gestionnaire: "Le gestionnaire « %{email} » nest pas dans le groupe gestionnaire."
notice:
groupe_gestionnaire_administrateur:
create: "Les administrateurs ont bien été affectés au groupe gestionnaire"
destroy: "L'administrateur « %{email} » a été supprimé."
remove: "L'administrateur « %{email} » a été retiré du groupe gestionnaire."
groupe_gestionnaire_gestionnaire:
create: "Les gestionnaires ont bien été affectés au groupe gestionnaire"
destroy: "Le gestionnaire « %{email} » a été retiré du groupe gestionnaire."
shared:
procedures:
no_siret: "Vous navez pas renseigné le siret du service pour certaines de vos démarches. Merci de les modifier."

View file

@ -11,6 +11,12 @@ fr:
duplicate_email:
one: "%{emails} est déjà gestionnaire de ce groupe"
other: "%{emails} sont déjà gestionnaires de ce groupe"
administrateurs_already_in_groupe_gestionnaire:
one: Cet administrateur est déjà dans un groupe gestionnaire dont vous n'avez pas la gestion.
other: Ces administrateurs sont déjà dans un groupe gestionnaire dont vous n'avez pas la gestion.
wrong_address:
one: "%{emails} nest pas une adresse email valide"
other: "%{emails} ne sont pas des adresses emails valides"
not_found:
one: "%{emails} n'a pas encore de compte."
other: "%{emails} n'ont pas encore de compte."

View file

@ -0,0 +1,4 @@
en:
groupe_gestionnaire_mailer:
notify_added_administrateurs:
email_body: "You were assigned as administrateur on the admins group %{groupe_gestionnaire_name} on %{application_name} by « %{email} »"

View file

@ -0,0 +1,4 @@
fr:
groupe_gestionnaire_mailer:
notify_added_administrateurs:
email_body: "Vous venez dêtre nommé administrateur du groupe gestionnaire %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »."

View file

@ -0,0 +1,4 @@
en:
groupe_gestionnaire_mailer:
notify_removed_administrateur:
email_body: "You were removed from the admins group %{groupe_gestionnaire_name} on %{application_name} by « %{email} »"

View file

@ -0,0 +1,4 @@
fr:
groupe_gestionnaire_mailer:
notify_removed_administrateur:
email_body: "Vous venez dêtre supprimé(e) du groupe gestionnaire %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »."

View file

@ -1,4 +1,4 @@
fr:
groupe_gestionnaire_mailer:
notify_removed_gestionnaire:
email_body: "Vous venez dêtre supprimé(e) du groupe %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »."
email_body: "Vous venez dêtre supprimé(e) du groupe gestionnaire %{groupe_gestionnaire_name} sur %{application_name} par « %{email} »."

View file

@ -506,6 +506,9 @@ Rails.application.routes.draw do
scope module: 'gestionnaires', as: 'gestionnaire' do
resources :groupe_gestionnaires, path: 'groupes', only: [:index, :show, :create, :edit, :update, :destroy] do
resources :gestionnaires, controller: 'groupe_gestionnaire_gestionnaires', only: [:index, :create, :destroy]
resources :administrateurs, controller: 'groupe_gestionnaire_administrateurs', only: [:index, :create, :destroy] do
delete :remove, on: :member
end
resources :children, controller: 'groupe_gestionnaire_children', only: [:index, :create, :destroy]
end
end

View file

@ -0,0 +1,102 @@
describe Gestionnaires::GroupeGestionnaireAdministrateursController, type: :controller do
let(:gestionnaire) { create(:gestionnaire).tap { _1.user.update(last_sign_in_at: Time.zone.now) } }
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
before { sign_in gestionnaire.user }
describe '#create' do
before do
post :create,
params: {
groupe_gestionnaire_id: groupe_gestionnaire.id,
administrateur: { email: new_administrateur_email }
},
format: :turbo_stream
end
context 'of a new administrateur' do
let(:new_administrateur_email) { 'new_administrateur@mail.com' }
it { expect(groupe_gestionnaire.reload.administrateurs.map(&:email)).to include(new_administrateur_email) }
it { expect(flash.notice).to eq("Les administrateurs ont bien été affectés au groupe gestionnaire") }
end
end
describe '#destroy' do
let(:gestionnaire) { create(:gestionnaire) }
let(:new_administrateur) { create(:administrateur) }
before do
groupe_gestionnaire.administrateurs << new_administrateur
end
def destroy(administrateur)
delete :destroy,
params: {
groupe_gestionnaire_id: groupe_gestionnaire.id,
id: administrateur.id
},
format: :turbo_stream
end
context 'when administrateur is in the groupe_gestionnaire' do
before { destroy(new_administrateur) }
it { expect(groupe_gestionnaire.reload.administrateurs.count).to eq(0) }
it { expect(flash.notice).to eq("L'administrateur « #{new_administrateur.email} » a été supprimé.") }
end
context 'when administrateur has some procedure' do
let(:administrateur_with_procedure) { create(:administrateur) }
let!(:procedure) { create(:procedure_with_dossiers, administrateur: administrateur_with_procedure) }
before do
groupe_gestionnaire.administrateurs << administrateur_with_procedure
destroy(administrateur_with_procedure)
end
it { expect(groupe_gestionnaire.reload.administrateurs.count).to eq(2) }
it { expect(flash.alert).to eq("L'administrateur « #{administrateur_with_procedure.email} » ne peut pas être supprimé du groupe gestionnaire.") }
end
context 'when administrateur is not in the groupe_gestionnaire' do
let(:other_administrateur) { create(:administrateur) }
before { destroy(other_administrateur) }
it { expect(groupe_gestionnaire.reload.administrateurs.count).to eq(1) }
it { expect(flash.alert).to eq("L'administrateur « #{other_administrateur.email} » nest pas dans le groupe gestionnaire.") }
end
end
describe '#remove' do
let(:gestionnaire) { create(:gestionnaire) }
let(:new_administrateur) { create(:administrateur) }
before do
groupe_gestionnaire.administrateurs << new_administrateur
end
def remove(administrateur)
delete :remove,
params: {
groupe_gestionnaire_id: groupe_gestionnaire.id,
id: administrateur.id
},
format: :turbo_stream
end
context 'when administrateur is in the groupe_gestionnaire' do
before { remove(new_administrateur) }
it { expect(groupe_gestionnaire.reload.administrateurs.count).to eq(0) }
it { expect(flash.notice).to eq("L'administrateur « #{new_administrateur.email} » a été retiré du groupe gestionnaire.") }
end
context 'when administrateur is not in the groupe_gestionnaire' do
let(:other_administrateur) { create(:administrateur) }
before { remove(other_administrateur) }
it { expect(groupe_gestionnaire.reload.administrateurs.count).to eq(1) }
it { expect(flash.alert).to eq("L'administrateur « #{other_administrateur.email} » nest pas dans le groupe gestionnaire.") }
end
end
end

View file

@ -44,7 +44,7 @@ describe Gestionnaires::GroupeGestionnaireGestionnairesController, type: :contro
it { expect(groupe_gestionnaire.gestionnaires).to include(gestionnaire) }
it { expect(groupe_gestionnaire.reload.gestionnaires.count).to eq(1) }
it { expect(flash.notice).to eq("Le gestionnaire « #{new_gestionnaire.email} » a été retiré du groupe.") }
it { expect(flash.notice).to eq("Le gestionnaire « #{new_gestionnaire.email} » a été retiré du groupe gestionnaire.") }
end
context 'when there is only one gestionnaire' do

View file

@ -6,9 +6,9 @@ RSpec.describe GroupeGestionnaireMailer, type: :mailer do
let(:current_super_admin_email) { 'toto@email.com' }
subject { described_class.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire_to_remove, current_super_admin_email) }
subject { described_class.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire_to_remove.email, current_super_admin_email) }
it { expect(subject.body).to include('Vous venez dêtre supprimé(e) du groupe') }
it { expect(subject.body).to include('Vous venez dêtre supprimé(e) du groupe gestionnaire') }
it { expect(subject.to).to match_array(['int3@g']) }
end
@ -21,9 +21,37 @@ RSpec.describe GroupeGestionnaireMailer, type: :mailer do
subject { described_class.notify_added_gestionnaires(groupe_gestionnaire, gestionnaires_to_add, current_super_admin_email) }
before { gestionnaires_to_add.each { groupe_gestionnaire.add(_1) } }
before { gestionnaires_to_add.each { groupe_gestionnaire.add_gestionnaire(_1) } }
it { expect(subject.body).to include('Vous venez dêtre nommé gestionnaire du groupe gestionnaire') }
it { expect(subject.bcc).to match_array(['int3@g', 'int4@g']) }
end
describe '#notify_removed_administrateur' do
let(:groupe_gestionnaire) { create(:groupe_gestionnaire) }
let(:administrateur_to_remove) { create(:administrateur, email: 'int3@g') }
let(:current_super_admin_email) { 'toto@email.com' }
subject { described_class.notify_removed_administrateur(groupe_gestionnaire, administrateur_to_remove.email, current_super_admin_email) }
it { expect(subject.body).to include('Vous venez dêtre supprimé(e) du groupe gestionnaire') }
it { expect(subject.to).to match_array(['int3@g']) }
end
describe '#notify_added_administrateurs' do
let(:groupe_gestionnaire) { create(:groupe_gestionnaire) }
let(:administrateurs_to_add) { [create(:administrateur, email: 'int3@g'), create(:administrateur, email: 'int4@g')] }
let(:current_super_admin_email) { 'toto@email.com' }
subject { described_class.notify_added_administrateurs(groupe_gestionnaire, administrateurs_to_add, current_super_admin_email) }
before { administrateurs_to_add.each { groupe_gestionnaire.add_administrateur(_1) } }
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
end

View file

@ -1,18 +1,32 @@
class GroupeGestionnaireMailerPreview < ActionMailer::Preview
def notify_removed_gestionnaire
groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe d\'admin')
groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire')
current_super_admin_email = 'admin@dgfip.com'
gestionnaire = Gestionnaire.new(user: user)
GroupeGestionnaireMailer.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire, current_super_admin_email)
GroupeGestionnaireMailer.notify_removed_gestionnaire(groupe_gestionnaire, gestionnaire.email, current_super_admin_email)
end
def notify_added_gestionnaires
groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe d\'admin')
groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire')
current_super_admin_email = 'admin@dgfip.com'
gestionnaires = [Gestionnaire.new(user: user)]
GroupeGestionnaireMailer.notify_added_gestionnaires(groupe_gestionnaire, gestionnaires, current_super_admin_email)
end
def notify_removed_administrateur
groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire')
current_super_admin_email = 'admin@dgfip.com'
administrateur = Administrateur.new(user: user)
GroupeGestionnaireMailer.notify_removed_administrateur(groupe_gestionnaire, administrateur.email, current_super_admin_email)
end
def notify_added_administrateurs
groupe_gestionnaire = GroupeGestionnaire.new(name: 'un groupe gestionnaire')
current_super_admin_email = 'admin@dgfip.com'
administrateurs = [Administrateur.new(user: user)]
GroupeGestionnaireMailer.notify_added_administrateurs(groupe_gestionnaire, administrateurs, current_super_admin_email)
end
private
def user

View file

@ -4,11 +4,11 @@ describe GroupeGestionnaire, type: :model do
it { is_expected.to have_and_belong_to_many(:gestionnaires) }
end
describe "#add" do
describe "#add_gestionnaire" do
let(:groupe_gestionnaire) { create(:groupe_gestionnaire) }
let(:gestionnaire) { create(:gestionnaire) }
subject { groupe_gestionnaire.add(gestionnaire) }
subject { groupe_gestionnaire.add_gestionnaire(gestionnaire) }
it 'adds the gestionnaire to the groupe gestionnaire' do
subject
@ -16,46 +16,16 @@ describe GroupeGestionnaire, type: :model do
end
end
describe "#add_gestionnaires" do
describe "#add_administrateur" do
let(:groupe_gestionnaire) { create(:groupe_gestionnaire) }
let(:gestionnaire) { create(:gestionnaire) }
let(:gestionnaire_to_add) { create(:gestionnaire) }
let(:administrateur) { create(:administrateur) }
it 'adds the gestionnaire by id' do
groupe_gestionnaire.add_gestionnaires(ids: [gestionnaire_to_add.id], current_user: gestionnaire)
expect(groupe_gestionnaire.reload.gestionnaires).to include(gestionnaire_to_add)
end
subject { groupe_gestionnaire.add_administrateur(administrateur) }
it 'adds the existing gestionnaire by email' do
groupe_gestionnaire.add_gestionnaires(emails: [gestionnaire_to_add.email], current_user: gestionnaire)
expect(groupe_gestionnaire.reload.gestionnaires).to include(gestionnaire_to_add)
end
it 'adds the new gestionnaire by email' do
groupe_gestionnaire.add_gestionnaires(emails: ['new_gestionnaire@ds.fr'], current_user: gestionnaire)
expect(groupe_gestionnaire.reload.gestionnaires.last.email).to eq('new_gestionnaire@ds.fr')
end
end
describe "#remove" do
let(:gestionnaire) { create(:gestionnaire) }
let(:gestionnaire_to_remove) { create(:gestionnaire) }
let(:groupe_gestionnaire) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire, gestionnaire_to_remove]) }
it 'removes the gestionnaire by id' do
expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(2)
groupe_gestionnaire.remove(gestionnaire_to_remove.id, gestionnaire)
expect(groupe_gestionnaire.reload.gestionnaires).not_to include(gestionnaire_to_remove)
expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(1)
end
it 'does not remove the gestionnaire if last' do
expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(2)
groupe_gestionnaire.remove(gestionnaire.id, gestionnaire)
expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(1)
groupe_gestionnaire.remove(gestionnaire_to_remove.id, gestionnaire)
expect(groupe_gestionnaire.reload.gestionnaires).to include(gestionnaire_to_remove)
expect(groupe_gestionnaire.reload.gestionnaires.size).to eq(1)
it 'adds the administrateur to the groupe gestionnaire' do
subject
expect(groupe_gestionnaire.reload.administrateurs).to include(administrateur)
end
end
end