From 725a97da7e41d94f6ac593c1f872cbc6c1fd8596 Mon Sep 17 00:00:00 2001 From: Lisa Durand <lisa.c.durand@gmail.com> Date: Wed, 16 Oct 2024 14:31:08 +0200 Subject: [PATCH] admin can modify procedure labels --- .../procedure/card/labels_component.rb | 7 + .../labels_component/labels_component.fr.yml | 3 + .../labels_component.html.haml | 17 ++ .../procedure_labels_controller.rb | 66 +++++++ app/helpers/dossier_helper.rb | 2 +- app/models/label.rb | 28 ++- .../procedure_labels/_form.html.haml | 8 + .../procedure_labels/edit.html.haml | 16 ++ .../procedure_labels/index.html.haml | 35 ++++ .../procedure_labels/new.html.haml | 16 ++ .../administrateurs/procedures/show.html.haml | 1 + .../dossiers/_header_top.html.haml | 9 +- config/locales/models/procedure_label/fr.yml | 6 + config/routes.rb | 2 + .../procedure_labels_controller_spec.rb | 168 ++++++++++++++++++ spec/factories/procedure_label.rb | 9 + 16 files changed, 388 insertions(+), 5 deletions(-) create mode 100644 app/components/procedure/card/labels_component.rb create mode 100644 app/components/procedure/card/labels_component/labels_component.fr.yml create mode 100644 app/components/procedure/card/labels_component/labels_component.html.haml create mode 100644 app/controllers/administrateurs/procedure_labels_controller.rb create mode 100644 app/views/administrateurs/procedure_labels/_form.html.haml create mode 100644 app/views/administrateurs/procedure_labels/edit.html.haml create mode 100644 app/views/administrateurs/procedure_labels/index.html.haml create mode 100644 app/views/administrateurs/procedure_labels/new.html.haml create mode 100644 config/locales/models/procedure_label/fr.yml create mode 100644 spec/controllers/administrateurs/procedure_labels_controller_spec.rb create mode 100644 spec/factories/procedure_label.rb diff --git a/app/components/procedure/card/labels_component.rb b/app/components/procedure/card/labels_component.rb new file mode 100644 index 000000000..4b484db35 --- /dev/null +++ b/app/components/procedure/card/labels_component.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Procedure::Card::LabelsComponent < ApplicationComponent + def initialize(procedure:) + @procedure = procedure + end +end diff --git a/app/components/procedure/card/labels_component/labels_component.fr.yml b/app/components/procedure/card/labels_component/labels_component.fr.yml new file mode 100644 index 000000000..31f598407 --- /dev/null +++ b/app/components/procedure/card/labels_component/labels_component.fr.yml @@ -0,0 +1,3 @@ +--- +fr: + title: Labels diff --git a/app/components/procedure/card/labels_component/labels_component.html.haml b/app/components/procedure/card/labels_component/labels_component.html.haml new file mode 100644 index 000000000..456f74ec7 --- /dev/null +++ b/app/components/procedure/card/labels_component/labels_component.html.haml @@ -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') diff --git a/app/controllers/administrateurs/procedure_labels_controller.rb b/app/controllers/administrateurs/procedure_labels_controller.rb new file mode 100644 index 000000000..b77800116 --- /dev/null +++ b/app/controllers/administrateurs/procedure_labels_controller.rb @@ -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 diff --git a/app/helpers/dossier_helper.rb b/app/helpers/dossier_helper.rb index 1ef31e14d..ba6a1004c 100644 --- a/app/helpers/dossier_helper.rb +++ b/app/helpers/dossier_helper.rb @@ -130,7 +130,7 @@ module DossierHelper end 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 def demandeur_dossier(dossier) diff --git a/app/models/label.rb b/app/models/label.rb index 6c4e73e13..bebfddf21 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -4,11 +4,33 @@ class Label < ApplicationRecord belongs_to :procedure has_many :dossier_labels, dependent: :destroy + NAME_MAX_LENGTH = 30 GENERIC_LABELS = [ - { name: 'à relancer', color: 'brown-caramel' }, - { name: 'complet', color: 'green-bourgeon' }, - { name: 'prêt pour validation', color: 'green-archipel' } + { name: 'à relancer', color: 'brown_caramel' }, + { name: 'complet', color: 'green_bourgeon' }, + { 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, length: { maximum: NAME_MAX_LENGTH } end diff --git a/app/views/administrateurs/procedure_labels/_form.html.haml b/app/views/administrateurs/procedure_labels/_form.html.haml new file mode 100644 index 000000000..a37eb26c5 --- /dev/null +++ b/app/views/administrateurs/procedure_labels/_form.html.haml @@ -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) diff --git a/app/views/administrateurs/procedure_labels/edit.html.haml b/app/views/administrateurs/procedure_labels/edit.html.haml new file mode 100644 index 000000000..fd712d69a --- /dev/null +++ b/app/views/administrateurs/procedure_labels/edit.html.haml @@ -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 } diff --git a/app/views/administrateurs/procedure_labels/index.html.haml b/app/views/administrateurs/procedure_labels/index.html.haml new file mode 100644 index 000000000..bb5f219e0 --- /dev/null +++ b/app/views/administrateurs/procedure_labels/index.html.haml @@ -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) diff --git a/app/views/administrateurs/procedure_labels/new.html.haml b/app/views/administrateurs/procedure_labels/new.html.haml new file mode 100644 index 000000000..a6bed20ad --- /dev/null +++ b/app/views/administrateurs/procedure_labels/new.html.haml @@ -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 } diff --git a/app/views/administrateurs/procedures/show.html.haml b/app/views/administrateurs/procedures/show.html.haml index 4414f921f..877f4ef98 100644 --- a/app/views/administrateurs/procedures/show.html.haml +++ b/app/views/administrateurs/procedures/show.html.haml @@ -98,3 +98,4 @@ = render Procedure::Card::DossierSubmittedMessageComponent.new(procedure: @procedure) = render Procedure::Card::ChorusComponent.new(procedure: @procedure) = render Procedure::Card::AccuseLectureComponent.new(procedure: @procedure) + = render Procedure::Card::LabelsComponent.new(procedure: @procedure) diff --git a/app/views/instructeurs/dossiers/_header_top.html.haml b/app/views/instructeurs/dossiers/_header_top.html.haml index c0f406abb..7fe1fb2f8 100644 --- a/app/views/instructeurs/dossiers/_header_top.html.haml +++ b/app/views/instructeurs/dossiers/_header_top.html.haml @@ -44,4 +44,11 @@ .fr-fieldset__element .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.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 diff --git a/config/locales/models/procedure_label/fr.yml b/config/locales/models/procedure_label/fr.yml new file mode 100644 index 000000000..deabcbd8e --- /dev/null +++ b/config/locales/models/procedure_label/fr.yml @@ -0,0 +1,6 @@ +fr: + activerecord: + attributes: + procedure_label: + color: Couleur + name: Nom diff --git a/config/routes.rb b/config/routes.rb index 5b6dd9634..0bf0c4602 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -708,6 +708,8 @@ Rails.application.routes.draw do get 'preview', on: :member end + resources :procedure_labels, controller: 'procedure_labels' + resource :attestation_template, only: [:show, :edit, :update, :create] do get 'preview', on: :member end diff --git a/spec/controllers/administrateurs/procedure_labels_controller_spec.rb b/spec/controllers/administrateurs/procedure_labels_controller_spec.rb new file mode 100644 index 000000000..4d93b4271 --- /dev/null +++ b/spec/controllers/administrateurs/procedure_labels_controller_spec.rb @@ -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 diff --git a/spec/factories/procedure_label.rb b/spec/factories/procedure_label.rb new file mode 100644 index 000000000..fbc907b14 --- /dev/null +++ b/spec/factories/procedure_label.rb @@ -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