diff --git a/Gemfile b/Gemfile index 30875b83d..5c641d9b2 100644 --- a/Gemfile +++ b/Gemfile @@ -12,6 +12,7 @@ gem 'addressable' gem 'administrate' gem 'administrate-field-enum' # Allow using Field::Enum in administrate gem 'after_party' +gem 'ancestry' gem 'anchored' gem 'bcrypt' gem 'bootsnap', '>= 1.4.4', require: false # Reduces boot times through caching; required in config/boot.rb diff --git a/Gemfile.lock b/Gemfile.lock index ffa4f43e6..28f64329f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -100,6 +100,8 @@ GEM administrate (~> 0.12) aes_key_wrap (1.1.0) after_party (1.11.2) + ancestry (4.3.3) + activerecord (>= 5.2.6) anchored (1.1.0) ast (2.4.2) attr_required (1.0.1) @@ -815,6 +817,7 @@ DEPENDENCIES administrate administrate-field-enum after_party + ancestry anchored axe-core-rspec bcrypt diff --git a/app/components/groupe_gestionnaire/card/children_component.rb b/app/components/groupe_gestionnaire/card/children_component.rb new file mode 100644 index 000000000..8d5178157 --- /dev/null +++ b/app/components/groupe_gestionnaire/card/children_component.rb @@ -0,0 +1,5 @@ +class GroupeGestionnaire::Card::ChildrenComponent < ApplicationComponent + def initialize(groupe_gestionnaire:) + @groupe_gestionnaire = groupe_gestionnaire + end +end diff --git a/app/components/groupe_gestionnaire/card/children_component/children_component.fr.yml b/app/components/groupe_gestionnaire/card/children_component/children_component.fr.yml new file mode 100644 index 000000000..31bded142 --- /dev/null +++ b/app/components/groupe_gestionnaire/card/children_component/children_component.fr.yml @@ -0,0 +1,5 @@ +--- +fr: + title: + one: Groupe enfants + other: Groupes enfants diff --git a/app/components/groupe_gestionnaire/card/children_component/children_component.html.haml b/app/components/groupe_gestionnaire/card/children_component/children_component.html.haml new file mode 100644 index 000000000..0147657ab --- /dev/null +++ b/app/components/groupe_gestionnaire/card/children_component/children_component.html.haml @@ -0,0 +1,12 @@ +.fr-col-6.fr-col-md-4.fr-col-lg-3 + = link_to gestionnaire_groupe_gestionnaire_children_path(@groupe_gestionnaire), id: 'gestionnaires', class: 'fr-tile fr-enlarge-link' do + .fr-tile__body.flex.column.align-center.justify-between + %div + %span.icon.accept + %p.fr-tile-status-accept Validé + %div + .line-count.fr-my-1w + %p.fr-tag= @groupe_gestionnaire.children.size + %h3.fr-h6 + = t('.title', count: @groupe_gestionnaire.children.size) + %p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit') diff --git a/app/components/groupe_gestionnaire/card/gestionnaires_component/gestionnaires_component.html.haml b/app/components/groupe_gestionnaire/card/gestionnaires_component/gestionnaires_component.html.haml index 39a24b2d6..2cdd84805 100644 --- a/app/components/groupe_gestionnaire/card/gestionnaires_component/gestionnaires_component.html.haml +++ b/app/components/groupe_gestionnaire/card/gestionnaires_component/gestionnaires_component.html.haml @@ -9,5 +9,4 @@ %p.fr-tag= @groupe_gestionnaire.gestionnaires.size %h3.fr-h6 = t('.title', count: @groupe_gestionnaire.gestionnaires.size) - %p.fr-tile-subtitle Gestion de la démarche %p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit') diff --git a/app/components/groupe_gestionnaire/groupe_gestionnaire_children/child_component.rb b/app/components/groupe_gestionnaire/groupe_gestionnaire_children/child_component.rb new file mode 100644 index 000000000..3afa79352 --- /dev/null +++ b/app/components/groupe_gestionnaire/groupe_gestionnaire_children/child_component.rb @@ -0,0 +1,16 @@ +class GroupeGestionnaire::GroupeGestionnaireChildren::ChildComponent < ApplicationComponent + include ApplicationHelper + + def initialize(groupe_gestionnaire:, child:) + @groupe_gestionnaire = groupe_gestionnaire + @child = child + end + + def name + @child.name + end + + def created_at + try_format_datetime(@child.created_at) + end +end diff --git a/app/components/groupe_gestionnaire/groupe_gestionnaire_children/child_component/child_component.html.haml b/app/components/groupe_gestionnaire/groupe_gestionnaire_children/child_component/child_component.html.haml new file mode 100644 index 000000000..5454602e3 --- /dev/null +++ b/app/components/groupe_gestionnaire/groupe_gestionnaire_children/child_component/child_component.html.haml @@ -0,0 +1,4 @@ +%tr{ id: dom_id(@child) } + %td + = link_to name, gestionnaire_groupe_gestionnaire_path(@child) + %td= created_at diff --git a/app/controllers/gestionnaires/gestionnaire_controller.rb b/app/controllers/gestionnaires/gestionnaire_controller.rb index d4cf10d34..080bc3117 100644 --- a/app/controllers/gestionnaires/gestionnaire_controller.rb +++ b/app/controllers/gestionnaires/gestionnaire_controller.rb @@ -8,8 +8,10 @@ module Gestionnaires def retrieve_groupe_gestionnaire id = params[:groupe_gestionnaire_id] || params[:id] - - @groupe_gestionnaire = current_gestionnaire.groupe_gestionnaires.find(id) + @groupe_gestionnaire = GroupeGestionnaire.find(id) + if ((@groupe_gestionnaire.ancestor_ids + [@groupe_gestionnaire.id]) & current_gestionnaire.groupe_gestionnaire_ids).empty? + raise(ActiveRecord::RecordNotFound) + end Sentry.configure_scope do |scope| scope.set_tags(groupe_gestionnaire: @groupe_gestionnaire.id) diff --git a/app/controllers/gestionnaires/groupe_gestionnaire_children_controller.rb b/app/controllers/gestionnaires/groupe_gestionnaire_children_controller.rb new file mode 100644 index 000000000..081d48aaa --- /dev/null +++ b/app/controllers/gestionnaires/groupe_gestionnaire_children_controller.rb @@ -0,0 +1,16 @@ +module Gestionnaires + class GroupeGestionnaireChildrenController < GestionnaireController + before_action :retrieve_groupe_gestionnaire, except: [:new] + + def index + end + + def create + if (@child = @groupe_gestionnaire.children.create!(name: params.require(:groupe_gestionnaire)[:name])) + flash[:notice] = "Le groupe enfants a bien été créé" + else + flash[:alert] = @child.errors.full_messages + end + end + end +end diff --git a/app/controllers/manager/gestionnaires_controller.rb b/app/controllers/manager/gestionnaires_controller.rb index 859bf2749..3b2241de7 100644 --- a/app/controllers/manager/gestionnaires_controller.rb +++ b/app/controllers/manager/gestionnaires_controller.rb @@ -1,4 +1,17 @@ module Manager class GestionnairesController < Manager::ApplicationController + def delete + gestionnaire = Gestionnaire.find(params[:id]) + + if !gestionnaire.can_be_deleted? + flash[:alert] = "Impossible de supprimer ce gestionnaire car il est gestionnaire d'un groupe racine" + else + gestionnaire.destroy! + logger.info("Le gestionnaire #{gestionnaire.id} est supprimé par #{current_super_admin.id}") + flash[:notice] = "Le gestionnaire #{gestionnaire.id} est supprimé" + end + + redirect_to manager_gestionnaires_path + end end end diff --git a/app/mailers/groupe_gestionnaire_mailer.rb b/app/mailers/groupe_gestionnaire_mailer.rb index cecde91af..6717d6da4 100644 --- a/app/mailers/groupe_gestionnaire_mailer.rb +++ b/app/mailers/groupe_gestionnaire_mailer.rb @@ -4,7 +4,7 @@ class GroupeGestionnaireMailer < ApplicationMailer def notify_removed_gestionnaire(groupe_gestionnaire, removed_gestionnaire, current_super_admin_email) @groupe_gestionnaire = groupe_gestionnaire @current_super_admin_email = current_super_admin_email - subject = "Vous avez été retiré(e) du groupe d'administrateur \"#{groupe_gestionnaire.name}\"" + subject = "Vous avez été retiré(e) du groupe gestionnaire \"#{groupe_gestionnaire.name}\"" mail(to: removed_gestionnaire.email, subject: subject) end diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index cfd05a728..ed08f4ef1 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -34,7 +34,10 @@ class Gestionnaire < ApplicationRecord end def can_be_deleted? - !(root_groupe_gestionnaire = groupe_gestionnaires.where(groupe_gestionnaire: nil).first) || root_groupe_gestionnaire.gestionnaires.size > 1 + groupe_gestionnaires.roots.each do |rt| + return false unless rt.gestionnaires.size > 1 + end + true end def registration_state diff --git a/app/models/groupe_gestionnaire.rb b/app/models/groupe_gestionnaire.rb index 97fec6084..fe7f3e9a0 100644 --- a/app/models/groupe_gestionnaire.rb +++ b/app/models/groupe_gestionnaire.rb @@ -1,12 +1,8 @@ class GroupeGestionnaire < ApplicationRecord - belongs_to :groupe_gestionnaire, optional: true # parent - has_many :children, class_name: "GroupeGestionnaire", inverse_of: :groupe_gestionnaire has_many :administrateurs has_and_belongs_to_many :gestionnaires - def root_groupe_gestionnaire? - groupe_gestionnaire.nil? - end + has_ancestry def add(gestionnaire) return if gestionnaire.nil? @@ -16,7 +12,7 @@ class GroupeGestionnaire < ApplicationRecord end def remove(gestionnaire_id, current_user) - if !self.root_groupe_gestionnaire? || self.gestionnaires.one? + 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) @@ -69,7 +65,7 @@ class GroupeGestionnaire < ApplicationRecord end if gestionnaires_to_add.present? - notice = "Les gestionnaires ont bien été affectés au groupe d'administrateurs" + notice = "Les gestionnaires ont bien été affectés au groupe gestionnaire" GroupeGestionnaireMailer .notify_added_gestionnaires(self, gestionnaires_to_add, current_user.email) diff --git a/app/views/gestionnaires/groupe_gestionnaire_children/_add_admin_form.html.haml b/app/views/gestionnaires/groupe_gestionnaire_children/_add_admin_form.html.haml new file mode 100644 index 000000000..335d221a4 --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_children/_add_admin_form.html.haml @@ -0,0 +1,13 @@ += form_for groupe_gestionnaire.children.new, + url: { controller: 'groupe_gestionnaire_children' }, + html: { id: "new_child" }, + data: { turbo: true, turbo_force: :server } do |f| + .fr-input-group + = f.label :email, class: "fr-label" do + Ajouter un groupe enfants + %span.fr-hint-text + = "Renseignez le nom du nouveau groupe enfants de « #{groupe_gestionnaire.name} »." + + = f.text_field :name, required: true, class: "fr-input", autofocus: true + + = f.submit 'Ajouter un nouveau groupe', class: 'fr-btn' diff --git a/app/views/gestionnaires/groupe_gestionnaire_children/create.turbo_stream.haml b/app/views/gestionnaires/groupe_gestionnaire_children/create.turbo_stream.haml new file mode 100644 index 000000000..6abe9b64a --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_children/create.turbo_stream.haml @@ -0,0 +1,5 @@ +- if @child.present? + = turbo_stream.update 'children' do + = render GroupeGestionnaire::GroupeGestionnaireChildren::ChildComponent.with_collection(@groupe_gestionnaire.children, groupe_gestionnaire: @groupe_gestionnaire) + = turbo_stream.replace "new_child", partial: 'add_admin_form', locals: { groupe_gestionnaire: @groupe_gestionnaire } + = turbo_stream.focus 'groupe_gestionnaire_name' diff --git a/app/views/gestionnaires/groupe_gestionnaire_children/index.html.haml b/app/views/gestionnaires/groupe_gestionnaire_children/index.html.haml new file mode 100644 index 000000000..e3bd426e2 --- /dev/null +++ b/app/views/gestionnaires/groupe_gestionnaire_children/index.html.haml @@ -0,0 +1,18 @@ += render partial: 'gestionnaires/breadcrumbs', + locals: { steps: [['Groupes gestionnaire', gestionnaire_groupe_gestionnaires_path], + ["#{@groupe_gestionnaire.name.truncate_words(10)}", gestionnaire_groupe_gestionnaire_path(@groupe_gestionnaire)], + ['Groupes enfants']], preview: false } + +.container + %h1 Gérer les groupes enfants de « #{@groupe_gestionnaire.name} » + + %table.table + %thead + %tr + %th= 'Nom' + %th= 'Enregistré le' + %tbody#children + = render(GroupeGestionnaire::GroupeGestionnaireChildren::ChildComponent.with_collection(@groupe_gestionnaire.children, groupe_gestionnaire: @groupe_gestionnaire)) + + .fr-mt-4w + = render 'add_admin_form', groupe_gestionnaire: @groupe_gestionnaire diff --git a/app/views/gestionnaires/groupe_gestionnaire_gestionnaires/index.html.haml b/app/views/gestionnaires/groupe_gestionnaire_gestionnaires/index.html.haml index 9f54c3be1..20895ad41 100644 --- a/app/views/gestionnaires/groupe_gestionnaire_gestionnaires/index.html.haml +++ b/app/views/gestionnaires/groupe_gestionnaire_gestionnaires/index.html.haml @@ -8,9 +8,11 @@ %table.table %thead - %th= 'Adresse email' - %th= 'Enregistré le' - %th= 'État' + %tr + %th= 'Adresse email' + %th= 'Enregistré le' + %th= 'État' + %th %tbody#gestionnaires = render(GroupeGestionnaire::GroupeGestionnaireGestionnaires::GestionnaireComponent.with_collection(@groupe_gestionnaire.gestionnaires.order('users.email'), groupe_gestionnaire: @groupe_gestionnaire)) diff --git a/app/views/gestionnaires/groupe_gestionnaires/show.html.haml b/app/views/gestionnaires/groupe_gestionnaires/show.html.haml index d22d6f0be..124d12dd8 100644 --- a/app/views/gestionnaires/groupe_gestionnaires/show.html.haml +++ b/app/views/gestionnaires/groupe_gestionnaires/show.html.haml @@ -18,3 +18,4 @@ %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::ChildrenComponent.new(groupe_gestionnaire: @groupe_gestionnaire) diff --git a/app/views/manager/gestionnaires/show.html.erb b/app/views/manager/gestionnaires/show.html.erb index 5fabd146b..795746d26 100644 --- a/app/views/manager/gestionnaires/show.html.erb +++ b/app/views/manager/gestionnaires/show.html.erb @@ -30,13 +30,9 @@ as well as a link to its edit page. class: "button", ) if accessible_action?(page.resource, :edit) %> - <%= link_to( - t("administrate.actions.destroy"), - [namespace, page.resource], - class: "button button--danger", - method: :delete, - data: { confirm: t("administrate.actions.confirm") } - ) if accessible_action?(page.resource, :destroy) %> + +