admin can modify procedure labels

This commit is contained in:
Lisa Durand 2024-10-16 14:31:08 +02:00
parent 35dee477ea
commit 725a97da7e
No known key found for this signature in database
GPG key ID: 0DF91F2CA1E8B816
16 changed files with 388 additions and 5 deletions

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class Procedure::Card::LabelsComponent < ApplicationComponent
def initialize(procedure:)
@procedure = procedure
end
end

View file

@ -0,0 +1,3 @@
---
fr:
title: Labels

View file

@ -0,0 +1,17 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to admin_procedure_procedure_labels_path(@procedure), class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.procedure_labels.present?
%p.fr-badge.fr-badge--info
Configuré
%div
.line-count.fr-my-1w
%p.fr-tag= @procedure.procedure_labels.size
- else
%p.fr-badge
Non configuré
%h3.fr-h6
= t('.title')
%p.fr-tile-subtitle Gérer les labels utilisables par les instructeurs
%p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit')

View file

@ -0,0 +1,66 @@
# frozen_string_literal: true
module Administrateurs
class ProcedureLabelsController < AdministrateurController
before_action :retrieve_procedure
before_action :set_colors_collection, only: [:edit, :new, :create, :update]
def index
@labels = @procedure.procedure_labels
end
def edit
@label = label
end
def new
@label = ProcedureLabel.new
end
def create
@label = @procedure.procedure_labels.build(procedure_label_params)
if @label.save
flash.notice = 'Le label a bien été créé'
redirect_to admin_procedure_procedure_labels_path(@procedure)
else
flash.alert = @label.errors.full_messages
render :new
end
end
def update
@label = label
@label.update(procedure_label_params)
if @label.valid?
flash.notice = 'Le label a bien été modifié'
redirect_to admin_procedure_procedure_labels_path(@procedure)
else
flash.alert = @label.errors.full_messages
render :edit
end
end
def destroy
@label = label
@label.destroy!
flash.notice = 'Le label a bien été supprimé'
redirect_to admin_procedure_procedure_labels_path(@procedure)
end
private
def procedure_label_params
params.require(:procedure_label).permit(:name, :color)
end
def label
@procedure.procedure_labels.find(params[:id])
end
def set_colors_collection
@colors_collection = ProcedureLabel.colors.values
end
end
end

View file

@ -130,7 +130,7 @@ module DossierHelper
end end
def tag_label(name, color) def tag_label(name, color)
tag.span(name, class: "fr-tag fr-tag--sm fr-tag--#{color}") tag.span(name, class: "fr-tag fr-tag--sm fr-tag--#{ProcedureLabel.colors.fetch(color.underscore)}")
end end
def demandeur_dossier(dossier) def demandeur_dossier(dossier)

View file

@ -4,11 +4,33 @@ class Label < ApplicationRecord
belongs_to :procedure belongs_to :procedure
has_many :dossier_labels, dependent: :destroy has_many :dossier_labels, dependent: :destroy
NAME_MAX_LENGTH = 30
GENERIC_LABELS = [ GENERIC_LABELS = [
{ name: 'à relancer', color: 'brown-caramel' }, { name: 'à relancer', color: 'brown_caramel' },
{ name: 'complet', color: 'green-bourgeon' }, { name: 'complet', color: 'green_bourgeon' },
{ name: 'prêt pour validation', color: 'green-archipel' } { name: 'prêt pour validation', color: 'green_archipel' }
] ]
enum color: {
green_tilleul_verveine: "green-tilleul-verveine",
green_bourgeon: "green-bourgeon",
green_emeraude: "green-emeraude",
green_menthe: "green-menthe",
green_archipel: "green-archipel",
blue_ecume: "blue-ecume",
blue_cumulus: "blue-cumulus",
purple_glycine: "purple-glycine",
pink_macaron: "pink-macaron",
pink_tuile: "pink-tuile",
yellow_tournesol: "yellow-tournesol",
yellow_moutarde: "yellow-moutarde",
orange_terre_battue: "orange-terre-battue",
brown_cafe_creme: "brown-cafe-creme",
brown_caramel: "brown-caramel",
brown_opera: "brown-opera",
beige_gris_galet: "beige-gris-galet"
}
validates :name, :color, presence: true validates :name, :color, presence: true
validates :name, length: { maximum: NAME_MAX_LENGTH }
end end

View file

@ -0,0 +1,8 @@
= form_with model: label, url: admin_procedure_procedure_labels_path(@procedure, id: @label.id), local: true do |f|
= render Dsfr::InputComponent.new(form: f, attribute: :name, input_type: :text_field, opts: { maxlength: ProcedureLabel::NAME_MAX_LENGTH})
= f.label :color, class: 'fr-label' do
= t('activerecord.attributes.procedure_label.color')
= render EditableChamp::AsteriskMandatoryComponent.new
= f.select :color, options_for_select(@colors_collection, selected: @label.color), {prompt: 'Choisir une couleur'}, {class: 'fr-select'}
= render Procedure::FixedFooterComponent.new(procedure: @procedure, form: f)

View file

@ -0,0 +1,16 @@
= render partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_path],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
['gestion des labels', admin_procedure_procedure_labels_path(procedure_id: @procedure.id)],
['Modifier le label']] }
.fr-container
.fr-mb-3w
= link_to "Liste de tous les labels", admin_procedure_procedure_labels_path(procedure_id: @procedure.id), class: "fr-link fr-icon-arrow-left-line fr-link--icon-left"
%h1.fr-h2
Modifier le label
= render partial: 'form',
locals: { label: @label, procedure_id: @procedure.id }

View file

@ -0,0 +1,35 @@
= render partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_path],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
['Labels']] }
.fr-container
%h1.fr-h2 Labels
= link_to "Nouveau label", new_admin_procedure_procedure_label_path(procedure_id: @procedure.id), class: "fr-btn fr-btn--primary fr-btn--icon-left fr-icon-add-circle-line mb-3"
- if @procedure.procedure_labels.present?
.fr-table.fr-table--layout-fixed.fr-table--bordered
%table
%caption Liste des labels
%thead
%tr
%th{ scope: "col" }
Nom
%th.change{ scope: "col" }
Actions
%tbody
- @labels.each do |label|
%tr
%td
= tag_label(label.name, label.color)
%td.change
= link_to('Modifier', edit_admin_procedure_procedure_label_path(procedure_id: @procedure.id, id: label.id), class: 'fr-btn fr-btn--sm fr-btn--secondary fr-btn--icon-left fr-icon-pencil-line')
= link_to 'Supprimer',
admin_procedure_procedure_label_path(procedure_id: @procedure.id, id: label.id),
method: :delete,
data: { confirm: "Confirmez vous la suppression de #{label.name}" },
class: 'fr-btn fr-btn--sm fr-btn--secondary fr-btn--icon-left fr-icon-delete-line fr-ml-1w'
= render Procedure::FixedFooterComponent.new(procedure: @procedure)

View file

@ -0,0 +1,16 @@
= render partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_path],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
['gestion des labels', admin_procedure_procedure_labels_path(procedure_id: @procedure.id)],
['Nouveau label']] }
.fr-container
.fr-mb-3w
= link_to "Liste de tous les labels", admin_procedure_procedure_labels_path(procedure_id: @procedure.id), class: "fr-link fr-icon-arrow-left-line fr-link--icon-left"
%h1.fr-h2
Créer un nouveau label
= render partial: 'form',
locals: { label: @label, procedure_id: @procedure.id }

View file

@ -98,3 +98,4 @@
= render Procedure::Card::DossierSubmittedMessageComponent.new(procedure: @procedure) = render Procedure::Card::DossierSubmittedMessageComponent.new(procedure: @procedure)
= render Procedure::Card::ChorusComponent.new(procedure: @procedure) = render Procedure::Card::ChorusComponent.new(procedure: @procedure)
= render Procedure::Card::AccuseLectureComponent.new(procedure: @procedure) = render Procedure::Card::AccuseLectureComponent.new(procedure: @procedure)
= render Procedure::Card::LabelsComponent.new(procedure: @procedure)

View file

@ -44,4 +44,11 @@
.fr-fieldset__element .fr-fieldset__element
.fr-checkbox-group.fr-checkbox-group--sm.fr-mb-1w .fr-checkbox-group.fr-checkbox-group--sm.fr-mb-1w
= b.check_box(checked: DossierLabel.find_by(dossier_id: dossier.id, label_id: b.value).present? ) = b.check_box(checked: DossierLabel.find_by(dossier_id: dossier.id, label_id: b.value).present? )
= b.label(class: "fr-label fr-tag fr-tag--sm fr-tag--#{b.object.color}") { b.text } = b.label(class: "fr-label fr-tag fr-tag--sm fr-tag--#{Label.colors.fetch(b.object.color)}") { b.text }
%hr
%p.fr-text--sm.fr-text-mention--grey.fr-mb-0
%b Besoin d'autres labels ?
%br
Contactez les
= link_to 'administrateurs de la démarche', administrateurs_instructeur_procedure_path(dossier.procedure), class: 'fr-link fr-link--sm', **external_link_attributes

View file

@ -0,0 +1,6 @@
fr:
activerecord:
attributes:
procedure_label:
color: Couleur
name: Nom

View file

@ -708,6 +708,8 @@ Rails.application.routes.draw do
get 'preview', on: :member get 'preview', on: :member
end end
resources :procedure_labels, controller: 'procedure_labels'
resource :attestation_template, only: [:show, :edit, :update, :create] do resource :attestation_template, only: [:show, :edit, :update, :create] do
get 'preview', on: :member get 'preview', on: :member
end end

View file

@ -0,0 +1,168 @@
# frozen_string_literal: true
describe Administrateurs::ProcedureLabelsController, type: :controller do
let(:admin) { administrateurs(:default_admin) }
let(:procedure) { create(:procedure, administrateur: admin) }
let(:admin_2) { create(:administrateur) }
let(:procedure_2) { create(:procedure, administrateur: admin_2) }
describe '#index' do
render_views
let!(:label_1) { create(:procedure_label, procedure:) }
let!(:label_2) { create(:procedure_label, procedure:) }
let!(:label_3) { create(:procedure_label, procedure:) }
before do
sign_in(admin.user)
end
subject { get :index, params: { procedure_id: procedure.id } }
it 'displays all procedure labels' do
subject
expect(response.body).to have_link("Nouveau label")
expect(response.body).to have_link("Modifier", count: 3)
expect(response.body).to have_link("Supprimer", count: 3)
end
end
describe '#create' do
before do
sign_in(admin.user)
end
subject { post :create, params: params }
context 'when submitting a new label' do
let(:params) do
{
procedure_label: {
name: 'Nouveau label',
color: 'green-bourgeon'
},
procedure_id: procedure.id
}
end
it { expect { subject }.to change { ProcedureLabel.count } .by(1) }
it 'creates a new label' do
subject
expect(flash.alert).to be_nil
expect(flash.notice).to eq('Le label a bien été créé')
expect(ProcedureLabel.last.name).to eq('Nouveau label')
expect(ProcedureLabel.last.color).to eq('green_bourgeon')
expect(procedure.procedure_labels.last).to eq(ProcedureLabel.last)
end
end
context 'when submitting an invalid label' do
let(:params) { { procedure_label: { name: 'Nouveau label' }, procedure_id: procedure.id } }
it { expect { subject }.not_to change { ProcedureLabel.count } }
it 'does not create a new label' do
subject
expect(flash.alert).to eq(["Le champ « Couleur » doit être rempli"])
expect(response).to render_template(:new)
expect(assigns(:label).name).to eq('Nouveau label')
end
end
context 'when submitting a label for a not own procedure' do
let(:params) do
{
procedure_label: {
name: 'Nouveau label',
color: 'green-bourgeon'
},
procedure_id: procedure_2.id
}
end
it { expect { subject }.not_to change { ProcedureLabel.count } }
it 'does not create a new label' do
subject
expect(flash.alert).to eq("Démarche inexistante")
expect(response.status).to eq(404)
end
end
end
describe '#update' do
let!(:label) { create(:procedure_label, procedure:) }
let(:label_params) { { name: 'Nouveau nom' } }
let(:params) { { id: label.id, procedure_label: label_params, procedure_id: procedure.id } }
before do
sign_in(admin.user)
end
subject { patch :update, params: }
context 'when updating a label' do
it 'updates correctly' do
subject
expect(flash.alert).to be_nil
expect(flash.notice).to eq('Le label a bien été modifié')
expect(label.reload.name).to eq('Nouveau nom')
expect(label.reload.color).to eq('green_bourgeon')
expect(label.reload.updated_at).not_to eq(label.reload.created_at)
expect(response).to redirect_to(admin_procedure_procedure_labels_path(procedure_id: procedure.id))
end
end
context 'when updating a service with invalid data' do
let(:label_params) { { name: '' } }
it 'does not update' do
subject
expect(flash.alert).not_to be_nil
expect(response).to render_template(:edit)
expect(label.reload.updated_at).to eq(label.reload.created_at)
end
end
context 'when updating a label for a not own procedure' do
let(:params) { { id: label.id, procedure_label: label_params, procedure_id: procedure_2.id } }
it 'does not update' do
subject
expect(label.reload.updated_at).to eq(label.reload.created_at)
end
end
end
describe '#destroy' do
let(:label) { create(:procedure_label, procedure:) }
before do
sign_in(admin.user)
end
subject { delete :destroy, params: }
context "when deleting a label" do
let(:params) { { id: label.id, procedure_id: procedure.id } }
it "delete the label" do
subject
expect { label.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(flash.notice).to eq('Le label a bien été supprimé')
expect(response).to redirect_to((admin_procedure_procedure_labels_path(procedure_id: procedure.id)))
end
end
context 'when deleting a label for a not own procedure' do
let(:params) { { id: label.id, procedure_id: procedure_2.id } }
it 'does not delete' do
subject
expect(flash.alert).to eq("Démarche inexistante")
expect(response.status).to eq(404)
expect { label.reload }.not_to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :procedure_label do
name { 'Un label' }
color { 'green-bourgeon' }
association :procedure
end
end