feat: US4.3.9 children management
This commit is contained in:
parent
9ea59814ad
commit
528c51cb5e
29 changed files with 212 additions and 31 deletions
1
Gemfile
1
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class GroupeGestionnaire::Card::ChildrenComponent < ApplicationComponent
|
||||
def initialize(groupe_gestionnaire:)
|
||||
@groupe_gestionnaire = groupe_gestionnaire
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fr:
|
||||
title:
|
||||
one: Groupe enfants
|
||||
other: Groupes enfants
|
|
@ -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')
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
%tr{ id: dom_id(@child) }
|
||||
%td
|
||||
= link_to name, gestionnaire_groupe_gestionnaire_path(@child)
|
||||
%td= created_at
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,17 @@
|
|||
module Manager
|
||||
class GestionnairesController < Manager::ApplicationController
|
||||
def delete
|
||||
gestionnaire = Gestionnaire.find(params[:id])
|
||||
|
||||
if !gestionnaire.can_be_deleted?
|
||||
fail "Impossible de supprimer ce gestionnaire car il est gestionnaire d'un groupe racine"
|
||||
end
|
||||
gestionnaire.destroy!
|
||||
|
||||
logger.info("Le gestionnaire #{gestionnaire.id} est supprimé par #{current_super_admin.id}")
|
||||
flash[:notice] = "Le gestionnaire #{gestionnaire.id} est supprimé"
|
||||
|
||||
redirect_to manager_gestionnaires_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
class GroupeGestionnaire < ApplicationRecord
|
||||
belongs_to :groupe_gestionnaire, optional: true # parent
|
||||
has_many :children, class_name: "GroupeGestionnaire", inverse_of: :groupe_gestionnaire
|
||||
# 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 +14,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 +67,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)
|
||||
|
|
|
@ -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'
|
|
@ -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'
|
|
@ -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
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) %>
|
||||
</div>
|
||||
<div>
|
||||
<%= button_to "Supprimer", delete_manager_gestionnaire_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression du gestionnaire ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Ce gestionnaire ne peut etre supprimé car il est le seul gestionnaire d'un groupe racine" %>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ REDIS_CACHE_SSL_VERIFY_NONE="enabled"
|
|||
# can be debug, info, warn, error, fatal, and unknown
|
||||
DS_LOG_LEVEL='info'
|
||||
|
||||
# Admins group usage (gestionnaire de groupes d'administrateurs)
|
||||
# GroupeGestionnaire
|
||||
# can be removed if needed when EVERY PARTS of the feature will be merged / only used in routes.rb
|
||||
ADMINS_GROUP_ENABLED="disabled"
|
||||
BULK_EMAIL_QUEUE="low_priority"
|
||||
|
|
3
config/initializers/ancestry.rb
Normal file
3
config/initializers/ancestry.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
# use the newer format
|
||||
Ancestry.default_ancestry_format = :materialized_path2
|
|
@ -57,7 +57,9 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
if ENV['ADMINS_GROUP_ENABLED'] == 'enabled' || Rails.env.test? # can be removed if needed when EVERY PARTS of the feature will be merged / from env.example.optional
|
||||
resources :gestionnaires, only: [:index, :show, :edit, :update, :destroy]
|
||||
resources :gestionnaires, only: [:index, :show, :edit, :update] do
|
||||
delete 'delete', on: :member
|
||||
end
|
||||
|
||||
resources :groupe_gestionnaires, path: 'groupe_administrateurs', only: [:index, :show, :new, :create, :edit, :update] do
|
||||
post 'add_gestionnaire', on: :member
|
||||
|
@ -491,6 +493,7 @@ 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 :children, controller: 'groupe_gestionnaire_children', only: [:index, :create, :destroy]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
class AddAncestryToGroupeGestionnaires < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_column :groupe_gestionnaires, :ancestry, :string, collation: 'C', null: false, default: '/'
|
||||
add_index :groupe_gestionnaires, :ancestry, algorithm: :concurrently
|
||||
end
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
describe Gestionnaires::GroupeGestionnaireChildrenController, 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]) }
|
||||
|
||||
describe "#index" do
|
||||
render_views
|
||||
subject { get :index, params: { groupe_gestionnaire_id: groupe_gestionnaire.id } }
|
||||
|
||||
context "when not logged" do
|
||||
before { subject }
|
||||
it { expect(response).to redirect_to(new_user_session_path) }
|
||||
end
|
||||
|
||||
context "when logged in" do
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, ancestry: "/#{groupe_gestionnaire.id}/", gestionnaires: [gestionnaire]) }
|
||||
before do
|
||||
sign_in(gestionnaire.user)
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(response).to have_http_status(:ok) }
|
||||
it { expect(assigns(:groupe_gestionnaire).children).to include(child_groupe_gestionnaire) }
|
||||
it { expect(response.body).to include(child_groupe_gestionnaire.name) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
before do
|
||||
sign_in gestionnaire.user
|
||||
post :create,
|
||||
params: {
|
||||
groupe_gestionnaire_id: groupe_gestionnaire.id,
|
||||
groupe_gestionnaire: { name: new_child_group_name }
|
||||
},
|
||||
format: :turbo_stream
|
||||
end
|
||||
|
||||
context 'of a child group' do
|
||||
let(:new_child_group_name) { 'child group' }
|
||||
|
||||
it { expect(groupe_gestionnaire.reload.children.map(&:name)).to include(new_child_group_name) }
|
||||
it { expect(flash.notice).to eq("Le groupe enfants a bien été créé") }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,7 +18,7 @@ describe Gestionnaires::GroupeGestionnaireGestionnairesController, type: :contro
|
|||
let(:new_gestionnaire_email) { 'new_gestionnaire@mail.com' }
|
||||
|
||||
it { expect(groupe_gestionnaire.reload.gestionnaires.map(&:email)).to include(new_gestionnaire_email) }
|
||||
it { expect(flash.notice).to eq("Les gestionnaires ont bien été affectés au groupe d'administrateurs") }
|
||||
it { expect(flash.notice).to eq("Les gestionnaires ont bien été affectés au groupe gestionnaire") }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ describe Gestionnaires::GroupeGestionnairesController, type: :controller do
|
|||
describe "#show" do
|
||||
subject { get :show, params: { id: child_groupe_gestionnaire.id } }
|
||||
let!(:groupe_gestionnaire_root) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire_root, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, ancestry: "/#{groupe_gestionnaire_root.id}/", gestionnaires: [gestionnaire]) }
|
||||
|
||||
context "when not logged" do
|
||||
before { subject }
|
||||
|
@ -49,7 +49,7 @@ describe Gestionnaires::GroupeGestionnairesController, type: :controller do
|
|||
describe "#edit" do
|
||||
subject { get :edit, params: { id: child_groupe_gestionnaire.id } }
|
||||
let!(:groupe_gestionnaire_root) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire_root, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, ancestry: "/#{groupe_gestionnaire_root.id}/", gestionnaires: [gestionnaire]) }
|
||||
|
||||
context "when not logged" do
|
||||
before { subject }
|
||||
|
@ -70,7 +70,7 @@ describe Gestionnaires::GroupeGestionnairesController, type: :controller do
|
|||
describe "#update" do
|
||||
subject { post :update, params: { id: child_groupe_gestionnaire.id, groupe_gestionnaire: { name: 'new child name' } } }
|
||||
let!(:groupe_gestionnaire_root) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire_root, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, ancestry: "/#{groupe_gestionnaire_root.id}/", gestionnaires: [gestionnaire]) }
|
||||
|
||||
context "when not logged" do
|
||||
before { subject }
|
||||
|
@ -91,7 +91,7 @@ describe Gestionnaires::GroupeGestionnairesController, type: :controller do
|
|||
describe "#destroy" do
|
||||
subject { post :destroy, params: { id: child_groupe_gestionnaire.id } }
|
||||
let!(:groupe_gestionnaire_root) { create(:groupe_gestionnaire, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, groupe_gestionnaire: groupe_gestionnaire_root, gestionnaires: [gestionnaire]) }
|
||||
let!(:child_groupe_gestionnaire) { create(:groupe_gestionnaire, ancestry: "/#{groupe_gestionnaire_root.id}/", gestionnaires: [gestionnaire]) }
|
||||
|
||||
context "when not logged" do
|
||||
before { subject }
|
||||
|
|
|
@ -22,4 +22,16 @@ describe Manager::GestionnairesController, type: :controller do
|
|||
|
||||
it { expect(response.body).to include(gestionnaire.email) }
|
||||
end
|
||||
|
||||
describe '#delete' do
|
||||
before { sign_in super_admin }
|
||||
|
||||
subject { delete :delete, params: { id: gestionnaire.id } }
|
||||
|
||||
it 'deletes the gestionnaire' do
|
||||
subject
|
||||
|
||||
expect(Gestionnaire.find_by(id: gestionnaire.id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
describe GroupeGestionnaire, type: :model do
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:groupe_gestionnaire).optional }
|
||||
it { is_expected.to have_many(:children) }
|
||||
it { is_expected.to have_many(:administrateurs) }
|
||||
it { is_expected.to have_and_belong_to_many(:gestionnaires) }
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue