Merge pull request #10976 from demarches-simplifiees/feat-admin-can-modify-labels
ETQ instructeur, je veux pouvoir apposer un label à un dossier (part 2)
This commit is contained in:
commit
c462425db2
53 changed files with 823 additions and 55 deletions
|
@ -37,7 +37,7 @@
|
|||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
.badge {
|
||||
padding: 0 5px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
border-radius: 100px;
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
vertical-align: top;
|
||||
|
||||
&.baseline {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
background-color: $orange;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-group {
|
||||
display: flex;
|
||||
|
||||
.fr-badge {
|
||||
margin-right: $default-spacer;
|
||||
}
|
||||
|
||||
.fr-badge:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
|
@ -262,3 +262,13 @@ button.fr-tag-bug {
|
|||
.fr-badge--lowercase {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
// We don't want badge to split in two lines
|
||||
.fr-tag {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// We remove the line height because it creates unharmonized spaces - most of all in table
|
||||
.fr-tags-group > li {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
width: 450px;
|
||||
}
|
||||
|
||||
.dropdown-label.dropdown-content {
|
||||
min-width: 390px;
|
||||
}
|
||||
|
||||
.print-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
|
|
30
app/assets/stylesheets/tags.scss
Normal file
30
app/assets/stylesheets/tags.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
@import "colors";
|
||||
@import "constants";
|
||||
|
||||
$colors: "green-tilleul-verveine",
|
||||
"green-bourgeon",
|
||||
"green-emeraude",
|
||||
"green-menthe",
|
||||
"blue-ecume",
|
||||
"purple-glycine",
|
||||
"pink-macaron",
|
||||
"yellow-tournesol",
|
||||
"brown-cafe-creme",
|
||||
"beige-gris-galet";
|
||||
|
||||
|
||||
@each $color in $colors {
|
||||
.fr-tag--#{$color},
|
||||
a.fr-tag--#{$color},
|
||||
button.fr-tag--#{$color},
|
||||
input[type=button].fr-tag--#{$color},
|
||||
input[type=image].fr-tag--#{$color},
|
||||
input[type=reset].fr-tag--#{$color},
|
||||
input[type=submit].fr-tag--#{$color} {
|
||||
--idle: transparent;
|
||||
--hover: var(--background-action-low-#{$color}-hover);
|
||||
--active: var(--background-action-low-#{$color}-active);
|
||||
background-color: var(--background-action-low-#{$color});
|
||||
color: var(--text-action-high-#{$color});
|
||||
}
|
||||
}
|
|
@ -53,6 +53,10 @@ class Instructeurs::ColumnFilterValueComponent < ApplicationComponent
|
|||
[_1.label, _1.id]
|
||||
end
|
||||
end
|
||||
elsif column.table == 'dossier_labels'
|
||||
Procedure.find(procedure_id).labels.filter_map do
|
||||
[_1.name, _1.id]
|
||||
end
|
||||
else
|
||||
find_type_de_champ(column.column).options_for_select(column)
|
||||
end
|
||||
|
|
|
@ -59,6 +59,8 @@ class Instructeurs::FilterButtonsComponent < ApplicationComponent
|
|||
elsif column.groupe_instructeur?
|
||||
current_instructeur.groupe_instructeurs
|
||||
.find { _1.id == filter.to_i }&.label || filter
|
||||
elsif column.dossier_labels?
|
||||
Label.find(filter)&.name || filter
|
||||
elsif column.type == :date
|
||||
helpers.try_parse_format_date(filter)
|
||||
else
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
= link_to expert_all_avis_path, class: 'fr-nav__link', aria: aria_current_for(:avis) do
|
||||
= Avis.model_name.human(count: 10)
|
||||
- if helpers.current_expert.avis_summary[:unanswered] > 0
|
||||
%span.badge.warning= helpers.current_expert.avis_summary[:unanswered]
|
||||
%span.fr-badge.fr-badge--new.fr-badge--no-icon= helpers.current_expert.avis_summary[:unanswered]
|
||||
|
||||
= render MainNavigation::AnnouncesLinkComponent.new
|
||||
|
|
7
app/components/procedure/card/labels_component.rb
Normal file
7
app/components/procedure/card/labels_component.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Procedure::Card::LabelsComponent < ApplicationComponent
|
||||
def initialize(procedure:)
|
||||
@procedure = procedure
|
||||
end
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
fr:
|
||||
title: Labels
|
|
@ -0,0 +1,17 @@
|
|||
.fr-col-6.fr-col-md-4.fr-col-lg-3
|
||||
= link_to [:admin, @procedure, :labels], class: 'fr-tile fr-enlarge-link' do
|
||||
.fr-tile__body.flex.column.align-center.justify-between
|
||||
- if @procedure.labels.present?
|
||||
%p.fr-badge.fr-badge--info
|
||||
Configuré
|
||||
%div
|
||||
.line-count.fr-my-1w
|
||||
%p.fr-tag= @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')
|
62
app/controllers/administrateurs/labels_controller.rb
Normal file
62
app/controllers/administrateurs/labels_controller.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Administrateurs
|
||||
class LabelsController < AdministrateurController
|
||||
before_action :retrieve_procedure
|
||||
before_action :retrieve_label, only: [:edit, :update, :destroy]
|
||||
before_action :set_colors_collection, only: [:edit, :new, :create, :update]
|
||||
|
||||
def index
|
||||
@labels = @procedure.labels
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def new
|
||||
@label = Label.new
|
||||
end
|
||||
|
||||
def create
|
||||
@label = @procedure.labels.build(label_params)
|
||||
|
||||
if @label.save
|
||||
flash.notice = 'Le label a bien été créé'
|
||||
redirect_to [:admin, @procedure, :labels]
|
||||
else
|
||||
flash.alert = @label.errors.full_messages
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @label.update(label_params)
|
||||
flash.notice = 'Le label a bien été modifié'
|
||||
redirect_to [:admin, @procedure, :labels]
|
||||
else
|
||||
flash.alert = @label.errors.full_messages
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@label.destroy!
|
||||
flash.notice = 'Le label a bien été supprimé'
|
||||
redirect_to [:admin, @procedure, :labels]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def label_params
|
||||
params.require(:label).permit(:name, :color)
|
||||
end
|
||||
|
||||
def retrieve_label
|
||||
@label = @procedure.labels.find(params[:id])
|
||||
end
|
||||
|
||||
def set_colors_collection
|
||||
@colors_collection = Label.colors.keys
|
||||
end
|
||||
end
|
||||
end
|
|
@ -108,6 +108,7 @@ module Administrateurs
|
|||
flash.now.alert = @procedure.errors.full_messages
|
||||
render 'new'
|
||||
else
|
||||
@procedure.create_generic_labels
|
||||
flash.notice = 'Démarche enregistrée.'
|
||||
current_administrateur.instructeur.assign_to_procedure(@procedure)
|
||||
|
||||
|
|
|
@ -63,6 +63,19 @@ module Instructeurs
|
|||
end
|
||||
end
|
||||
|
||||
def dossier_labels
|
||||
labels = params[:label_id]&.map(&:to_i) || []
|
||||
|
||||
@dossier = dossier
|
||||
labels.each { |params_label| DossierLabel.find_or_create_by(dossier_id: @dossier.id, label_id: params_label) }
|
||||
|
||||
all_labels = DossierLabel.where(dossier_id: @dossier.id).pluck(:label_id)
|
||||
|
||||
(all_labels - labels).each { DossierLabel.find_by(dossier_id: @dossier.id, label_id: _1).destroy }
|
||||
|
||||
render :change_state
|
||||
end
|
||||
|
||||
def messagerie
|
||||
@commentaire = Commentaire.new
|
||||
@messagerie_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.messagerie_seen_at
|
||||
|
|
|
@ -118,6 +118,21 @@ module DossierHelper
|
|||
tag.span(Dossier.human_attribute_name("pending_correction.resolved"), class: ['fr-badge fr-badge--sm fr-badge--success super', html_class], role: 'status')
|
||||
end
|
||||
|
||||
def tags_label(tags)
|
||||
if tags.count > 1
|
||||
tag.ul(class: 'fr-tags-group') do
|
||||
safe_join(tags.map { |t| tag.li(tag_label(t[1], t[2])) })
|
||||
end
|
||||
else
|
||||
tag = tags.first
|
||||
tag_label(tag[1], tag[2])
|
||||
end
|
||||
end
|
||||
|
||||
def tag_label(name, color)
|
||||
tag.span(name, class: "fr-tag fr-tag--sm fr-tag--#{Label.class_name(color)}")
|
||||
end
|
||||
|
||||
def demandeur_dossier(dossier)
|
||||
if dossier.procedure.for_individual? && dossier.for_tiers?
|
||||
return t('shared.dossiers.beneficiaire', mandataire: dossier.mandataire_full_name, beneficiaire: "#{dossier&.individual&.prenom} #{dossier&.individual&.nom}")
|
||||
|
|
|
@ -40,6 +40,7 @@ class Column
|
|||
def notifications? = [table, column] == ['notifications', 'notifications']
|
||||
def dossier_state? = [table, column] == ['self', 'state']
|
||||
def groupe_instructeur? = [table, column] == ['groupe_instructeur', 'id']
|
||||
def dossier_labels? = [table, column] == ['dossier_labels', 'label_id']
|
||||
def type_de_champ? = table == TYPE_DE_CHAMP_TABLE
|
||||
|
||||
def self.find(h_id)
|
||||
|
|
|
@ -91,6 +91,8 @@ module ColumnsConcern
|
|||
|
||||
def user_france_connected_column = Columns::DossierColumn.new(procedure_id: id, table: 'self', column: 'user_from_france_connect?', filterable: false, displayable: false)
|
||||
|
||||
def dossier_labels_column = Columns::DossierColumn.new(procedure_id: id, table: 'dossier_labels', column: 'label_id', type: :enum)
|
||||
|
||||
def procedure_chorus_columns
|
||||
['domaine_fonctionnel', 'referentiel_prog', 'centre_de_cout']
|
||||
.map { |column| Columns::DossierColumn.new(procedure_id: id, table: 'procedure', column:, displayable: false, filterable: false) }
|
||||
|
@ -127,7 +129,8 @@ module ColumnsConcern
|
|||
followers_instructeurs_email_column,
|
||||
groupe_instructeurs_id_column,
|
||||
Columns::DossierColumn.new(procedure_id: id, table: 'avis', column: 'question_answer', filterable: false),
|
||||
user_france_connected_column
|
||||
user_france_connected_column,
|
||||
dossier_labels_column
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -132,6 +132,8 @@ class Dossier < ApplicationRecord
|
|||
|
||||
belongs_to :transfer, class_name: 'DossierTransfer', foreign_key: 'dossier_transfer_id', optional: true, inverse_of: :dossiers
|
||||
has_many :transfer_logs, class_name: 'DossierTransferLog', dependent: :destroy
|
||||
has_many :dossier_labels, dependent: :destroy
|
||||
has_many :labels, through: :dossier_labels
|
||||
|
||||
after_destroy_commit :log_destroy
|
||||
|
||||
|
|
6
app/models/dossier_label.rb
Normal file
6
app/models/dossier_label.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DossierLabel < ApplicationRecord
|
||||
belongs_to :dossier
|
||||
belongs_to :label
|
||||
end
|
35
app/models/label.rb
Normal file
35
app/models/label.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Label < ApplicationRecord
|
||||
belongs_to :procedure
|
||||
has_many :dossier_labels, dependent: :destroy
|
||||
|
||||
NAME_MAX_LENGTH = 30
|
||||
GENERIC_LABELS = [
|
||||
{ name: 'À examiner', color: 'purple_glycine' },
|
||||
{ name: 'À relancer', color: 'green_tilleul_verveine' },
|
||||
{ name: 'Complet', color: 'green_emeraude' },
|
||||
{ name: 'À signer', color: 'blue_ecume' },
|
||||
{ name: 'Urgent', color: 'pink_macaron' }
|
||||
]
|
||||
|
||||
enum color: {
|
||||
green_tilleul_verveine: "green-tilleul-verveine",
|
||||
green_bourgeon: "green-bourgeon",
|
||||
green_emeraude: "green-emeraude",
|
||||
green_menthe: "green-menthe",
|
||||
blue_ecume: "blue-ecume",
|
||||
purple_glycine: "purple-glycine",
|
||||
pink_macaron: "pink-macaron",
|
||||
yellow_tournesol: "yellow-tournesol",
|
||||
brown_cafe_creme: "brown-cafe-creme",
|
||||
beige_gris_galet: "beige-gris-galet"
|
||||
}
|
||||
|
||||
validates :name, :color, presence: true
|
||||
validates :name, length: { maximum: NAME_MAX_LENGTH }
|
||||
|
||||
def self.class_name(color)
|
||||
Label.colors.fetch(color.underscore)
|
||||
end
|
||||
end
|
|
@ -60,6 +60,7 @@ class Procedure < ApplicationRecord
|
|||
has_and_belongs_to_many :procedure_tags
|
||||
|
||||
has_many :bulk_messages, dependent: :destroy
|
||||
has_many :labels, dependent: :destroy
|
||||
|
||||
def active_dossier_submitted_message
|
||||
published_dossier_submitted_message || draft_dossier_submitted_message
|
||||
|
@ -528,6 +529,7 @@ class Procedure < ApplicationRecord
|
|||
procedure.closing_notification_en_cours = false
|
||||
procedure.template = false
|
||||
procedure.monavis_embed = nil
|
||||
procedure.labels = labels.map(&:dup)
|
||||
|
||||
if !procedure.valid?
|
||||
procedure.errors.attribute_names.each do |attribute|
|
||||
|
@ -935,6 +937,12 @@ class Procedure < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def create_generic_labels
|
||||
Label::GENERIC_LABELS.each do |label|
|
||||
Label.create(name: label[:name], color: label[:color], procedure_id: self.id)
|
||||
end
|
||||
end
|
||||
|
||||
def stable_ids_used_by_routing_rules
|
||||
@stable_ids_used_by_routing_rules ||= groupe_instructeurs.flat_map { _1.routing_rule&.sources }.compact
|
||||
end
|
||||
|
|
|
@ -56,6 +56,11 @@ class DossierFilterService
|
|||
.order("#{sanitized_column(table, column)} #{order}")
|
||||
.pluck(:id)
|
||||
.uniq
|
||||
when 'dossier_labels'
|
||||
dossiers.includes(table)
|
||||
.order("#{self.class.sanitized_column(table, column)} #{order}")
|
||||
.pluck(:id)
|
||||
.uniq
|
||||
when 'self', 'user', 'individual', 'etablissement', 'groupe_instructeur'
|
||||
(table == 'self' ? dossiers : dossiers.includes(table))
|
||||
.order("#{sanitized_column(table, column)} #{order}")
|
||||
|
@ -122,6 +127,11 @@ class DossierFilterService
|
|||
dossiers
|
||||
.includes(table)
|
||||
.filter_ilike(table, column, values) # ilike or where column == 'value' are both valid, we opted for ilike
|
||||
when 'dossier_labels'
|
||||
assert_supported_column(table, column)
|
||||
dossiers
|
||||
.joins(:dossier_labels)
|
||||
.where(dossier_labels: { label_id: values })
|
||||
when 'groupe_instructeur'
|
||||
assert_supported_column(table, column)
|
||||
|
||||
|
|
|
@ -123,6 +123,18 @@ class DossierProjectionService
|
|||
|
||||
fields[0][:id_value_h] = id_value_h
|
||||
|
||||
when 'dossier_labels'
|
||||
columns = fields.map { _1[COLUMN].to_sym }
|
||||
|
||||
id_value_h =
|
||||
DossierLabel
|
||||
.includes(:label)
|
||||
.where(dossier_id: dossiers_ids)
|
||||
.pluck('dossier_id, labels.name, labels.color')
|
||||
.group_by { |dossier_id, _| dossier_id }
|
||||
|
||||
fields[0][:id_value_h] = id_value_h.transform_values { |v| { value: v, type: :label } }
|
||||
|
||||
when 'procedure'
|
||||
Dossier
|
||||
.joins(:procedure)
|
||||
|
|
25
app/tasks/maintenance/backfill_labels_for_procedures_task.rb
Normal file
25
app/tasks/maintenance/backfill_labels_for_procedures_task.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Maintenance
|
||||
class BackfillLabelsForProceduresTask < MaintenanceTasks::Task
|
||||
# Cette tâche permet de créer un jeu de labels génériques pour les anciennes procédures
|
||||
# Plus d'informations sur l'implémentation des labels ici : https://github.com/demarches-simplifiees/demarches-simplifiees.fr/issues/9787
|
||||
# 2024-10-15
|
||||
|
||||
include RunnableOnDeployConcern
|
||||
|
||||
run_on_first_deploy
|
||||
|
||||
def collection
|
||||
Procedure
|
||||
.includes(:labels)
|
||||
.where(labels: { id: nil })
|
||||
end
|
||||
|
||||
def process(procedure)
|
||||
Label::GENERIC_LABELS.each do |label|
|
||||
Label.create(name: label[:name], color: label[:color], procedure_id: procedure.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
15
app/views/administrateurs/labels/_form.html.haml
Normal file
15
app/views/administrateurs/labels/_form.html.haml
Normal file
|
@ -0,0 +1,15 @@
|
|||
= form_with model: label, url: [:admin, @procedure, @label], local: true do |f|
|
||||
= render Dsfr::InputComponent.new(form: f, attribute: :name, input_type: :text_field, opts: { maxlength: Label::NAME_MAX_LENGTH})
|
||||
|
||||
%fieldset.fr-fieldset
|
||||
%legend.fr-fieldset__legend.fr-fieldset__legend--regular
|
||||
= t('activerecord.attributes.label.color')
|
||||
= asterisk
|
||||
|
||||
- @colors_collection.each do |color|
|
||||
.fr-fieldset__element.fr-fieldset__element--inline
|
||||
.fr-radio-group
|
||||
= f.radio_button :color, color, checked: (label.color == color)
|
||||
= f.label :color, t("activerecord.attributes.label/color.#{color}"), value: color, class: "fr-label fr-tag fr-tag--sm fr-tag--#{Label.class_name(color)}"
|
||||
|
||||
= render Procedure::FixedFooterComponent.new(procedure: @procedure, form: f)
|
20
app/views/administrateurs/labels/edit.html.haml
Normal file
20
app/views/administrateurs/labels/edit.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
- content_for :title, "Modifier le label"
|
||||
|
||||
= 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, :labels]],
|
||||
['Modifier le label']] }
|
||||
|
||||
|
||||
.fr-container
|
||||
.fr-mb-3w
|
||||
= link_to "Liste de tous les labels",
|
||||
[:admin, @procedure, :labels],
|
||||
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 }
|
43
app/views/administrateurs/labels/index.html.haml
Normal file
43
app/views/administrateurs/labels/index.html.haml
Normal file
|
@ -0,0 +1,43 @@
|
|||
- content_for :title, "Labels"
|
||||
|
||||
= 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, :label],
|
||||
class: "fr-btn fr-btn--primary fr-btn--icon-left fr-icon-add-circle-line mb-3"
|
||||
|
||||
- if @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, label],
|
||||
class: 'fr-btn fr-btn--sm fr-btn--secondary fr-btn--icon-left fr-icon-pencil-line'
|
||||
|
||||
= link_to 'Supprimer',
|
||||
[:admin, @procedure, label],
|
||||
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)
|
20
app/views/administrateurs/labels/new.html.haml
Normal file
20
app/views/administrateurs/labels/new.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
- content_for :title, "Nouveau label"
|
||||
|
||||
= 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, :labels]],
|
||||
['Nouveau label']] }
|
||||
|
||||
|
||||
.fr-container
|
||||
.fr-mb-3w
|
||||
= link_to "Liste de tous les labels",
|
||||
[:admin, @procedure, :labels],
|
||||
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 }
|
|
@ -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)
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#header-top.fr-container
|
||||
.flex.fr-mb-3w
|
||||
.flex
|
||||
%div
|
||||
%h1.fr-h3.fr-mb-1w
|
||||
= "Dossier nº #{dossier.id}"
|
||||
|
||||
= link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link"
|
||||
.fr-mt-2w.badge-group
|
||||
.fr-mt-2w.fr-badge-group
|
||||
= procedure_badge(dossier.procedure)
|
||||
|
||||
= status_badge(dossier.state)
|
||||
|
@ -16,7 +16,6 @@
|
|||
= render Instructeurs::SVASVRDecisionBadgeComponent.new(projection_or_dossier: dossier, procedure: dossier.procedure, with_label: true)
|
||||
|
||||
|
||||
|
||||
.header-actions.fr-ml-auto
|
||||
= render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: }
|
||||
= render partial: 'instructeurs/dossiers/print_and_export_actions', locals: { dossier: }
|
||||
|
@ -26,3 +25,30 @@
|
|||
- if dossier.user_deleted?
|
||||
%p.fr-mb-1w
|
||||
%small L’usager a supprimé son compte. Vous pouvez archiver puis supprimer le dossier.
|
||||
|
||||
- if dossier.procedure.labels.present?
|
||||
.fr-mb-3w
|
||||
- if dossier.labels.present?
|
||||
- dossier.labels.each do |label|
|
||||
= tag_label(label.name, label.color)
|
||||
|
||||
= render Dropdown::MenuComponent.new(wrapper: :span, button_options: { class: ['fr-btn--sm fr-btn--tertiary-no-outline fr-pl-1v']}, menu_options: { class: ['dropdown-label left-aligned'] }) do |menu|
|
||||
- if dossier.labels.empty?
|
||||
- menu.with_button_inner_html do
|
||||
Ajouter un label
|
||||
|
||||
- menu.with_form do
|
||||
= form_with(url: dossier_labels_instructeur_dossier_path(dossier_id: dossier.id, procedure_id: dossier.procedure.id), method: :post, class: 'fr-p-3w', data: { controller: 'autosubmit', turbo: 'true' }) do |f|
|
||||
%fieldset.fr-fieldset.fr-mt-2w.fr-mb-0
|
||||
= f.collection_check_boxes :label_id, dossier.procedure.labels, :id, :name, include_hidden: false do |b|
|
||||
.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--#{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
|
||||
|
|
|
@ -132,12 +132,12 @@
|
|||
%td
|
||||
- if p.hidden_by_administration_at.present?
|
||||
%span.cell-link
|
||||
= column
|
||||
= column.is_a?(Hash) ? tags_label(column[:value]) : column
|
||||
- if p.hidden_by_user_at.present?
|
||||
= "- #{t("views.instructeurs.dossiers.deleted_reason.#{p.hidden_by_reason}")}"
|
||||
- else
|
||||
%a.cell-link{ href: path }
|
||||
= column
|
||||
= column.is_a?(Hash) ? tags_label(column[:value]) : column
|
||||
= "- #{t("views.instructeurs.dossiers.deleted_reason.#{p.hidden_by_reason}")}" if p.hidden_by_user_at.present?
|
||||
|
||||
%td.status-col
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
= dsfr_icon('fr-icon-user-add-fill', :sm, :mr)
|
||||
- if invites.present?
|
||||
= t('views.invites.dropdown.view_invited_people')
|
||||
%span.badge= invites.size
|
||||
%span.fr-badge.fr-ml-1v= invites.size
|
||||
- else
|
||||
- if dossier.read_only?
|
||||
= t('views.invites.dropdown.invite_to_view')
|
||||
|
|
|
@ -153,11 +153,6 @@
|
|||
%span.label.refused .label.refused
|
||||
%span.label.without-continuation .label.without-continuation
|
||||
|
||||
%h1 Badges
|
||||
|
||||
%span.badge 1
|
||||
%span.badge.warning 1
|
||||
|
||||
%h1 Cards
|
||||
|
||||
.card
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
%span.notifications{ 'aria-label': 'notifications' }
|
||||
= link_to(url, 'aria-selected': active ? true : nil, class: 'fr-tabs__tab', role: 'tab' ) do
|
||||
- if badge.present?
|
||||
%span.badge.fr-mr-1w= badge
|
||||
%span.fr-badge.fr-badge--blue-ecume.fr-mr-1w= badge
|
||||
= label
|
||||
|
|
17
config/locales/models/label/fr.yml
Normal file
17
config/locales/models/label/fr.yml
Normal file
|
@ -0,0 +1,17 @@
|
|||
fr:
|
||||
activerecord:
|
||||
attributes:
|
||||
label:
|
||||
color: Couleur
|
||||
name: Nom
|
||||
label/color: &color
|
||||
green_tilleul_verveine: 'tilleul'
|
||||
green_bourgeon: 'bourgeon'
|
||||
green_emeraude: 'émeraude'
|
||||
green_menthe: 'menthe'
|
||||
blue_ecume: 'écume'
|
||||
purple_glycine: 'glycine'
|
||||
pink_macaron: 'macaron'
|
||||
yellow_tournesol: 'tournesol'
|
||||
brown_cafe_creme: 'café'
|
||||
beige_gris_galet: 'galet'
|
|
@ -81,3 +81,5 @@ en:
|
|||
association_date_creation: 'Association date de création'
|
||||
association_date_declaration: 'Association date de déclaration'
|
||||
association_date_publication: 'Association date de publication'
|
||||
dossier_labels:
|
||||
label_id: Labels
|
||||
|
|
|
@ -85,3 +85,5 @@ fr:
|
|||
association_date_creation: 'Association date de création'
|
||||
association_date_declaration: 'Association date de déclaration'
|
||||
association_date_publication: 'Association date de publication'
|
||||
dossier_labels:
|
||||
label_id: Labels
|
||||
|
|
|
@ -512,6 +512,7 @@ Rails.application.routes.draw do
|
|||
resources :commentaires, only: [:destroy]
|
||||
post 'repousser-expiration' => 'dossiers#extend_conservation'
|
||||
post 'repousser-expiration-and-restore' => 'dossiers#extend_conservation_and_restore'
|
||||
post 'dossier_labels' => 'dossiers#dossier_labels'
|
||||
get 'geo_data'
|
||||
get 'apercu_attestation'
|
||||
get 'bilans_bdf'
|
||||
|
@ -707,6 +708,8 @@ Rails.application.routes.draw do
|
|||
get 'preview', on: :member
|
||||
end
|
||||
|
||||
resources :labels, controller: 'labels'
|
||||
|
||||
resource :attestation_template, only: [:show, :edit, :update, :create] do
|
||||
get 'preview', on: :member
|
||||
end
|
||||
|
|
12
db/migrate/20240924151336_create_labels.rb
Normal file
12
db/migrate/20240924151336_create_labels.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateLabels < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :labels do |t|
|
||||
t.string :name
|
||||
t.string :color
|
||||
t.references :procedure, null: false, foreign_key: true
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
12
db/migrate/20240925133719_create_dossier_labels.rb
Normal file
12
db/migrate/20240925133719_create_dossier_labels.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateDossierLabels < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
create_table :dossier_labels do |t|
|
||||
t.references :dossier, null: false, foreign_key: true
|
||||
t.references :label, null: false, foreign_key: true
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
21
db/schema.rb
21
db/schema.rb
|
@ -416,6 +416,15 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_14_084333) do
|
|||
t.index ["resolved_at"], name: "index_dossier_corrections_on_resolved_at", where: "((resolved_at IS NULL) OR (resolved_at IS NOT NULL))"
|
||||
end
|
||||
|
||||
create_table "dossier_labels", force: :cascade do |t|
|
||||
t.datetime "created_at", null: false
|
||||
t.bigint "dossier_id", null: false
|
||||
t.bigint "label_id", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["dossier_id"], name: "index_dossier_labels_on_dossier_id"
|
||||
t.index ["label_id"], name: "index_dossier_labels_on_label_id"
|
||||
end
|
||||
|
||||
create_table "dossier_operation_logs", force: :cascade do |t|
|
||||
t.boolean "automatic_operation", default: false, null: false
|
||||
t.bigint "bill_signature_id"
|
||||
|
@ -820,6 +829,15 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_14_084333) do
|
|||
t.index ["email", "dossier_id"], name: "index_invites_on_email_and_dossier_id", unique: true
|
||||
end
|
||||
|
||||
create_table "labels", force: :cascade do |t|
|
||||
t.string "color"
|
||||
t.datetime "created_at", null: false
|
||||
t.string "name"
|
||||
t.bigint "procedure_id", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["procedure_id"], name: "index_labels_on_procedure_id"
|
||||
end
|
||||
|
||||
create_table "maintenance_tasks_runs", force: :cascade do |t|
|
||||
t.text "arguments"
|
||||
t.text "backtrace"
|
||||
|
@ -1280,6 +1298,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_14_084333) do
|
|||
add_foreign_key "dossier_batch_operations", "dossiers"
|
||||
add_foreign_key "dossier_corrections", "commentaires"
|
||||
add_foreign_key "dossier_corrections", "dossiers"
|
||||
add_foreign_key "dossier_labels", "dossiers"
|
||||
add_foreign_key "dossier_labels", "labels"
|
||||
add_foreign_key "dossier_operation_logs", "bill_signatures"
|
||||
add_foreign_key "dossier_transfer_logs", "dossiers"
|
||||
add_foreign_key "dossiers", "batch_operations"
|
||||
|
@ -1300,6 +1320,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_14_084333) do
|
|||
add_foreign_key "groupe_instructeurs", "procedures"
|
||||
add_foreign_key "initiated_mails", "procedures"
|
||||
add_foreign_key "instructeurs", "users"
|
||||
add_foreign_key "labels", "procedures"
|
||||
add_foreign_key "merge_logs", "users"
|
||||
add_foreign_key "procedure_presentations", "assign_tos"
|
||||
add_foreign_key "procedure_revision_types_de_champ", "procedure_revision_types_de_champ", column: "parent_id"
|
||||
|
|
|
@ -68,7 +68,7 @@ describe MainNavigation::InstructeurExpertNavigationComponent, type: :component
|
|||
it 'renders a link to expert all avis with current page class' do
|
||||
expect(subject).to have_link('Avis', href: component.helpers.expert_all_avis_path)
|
||||
expect(subject).to have_selector('a[aria-current="true"]', text: 'Avis')
|
||||
expect(subject).not_to have_selector('span.badge')
|
||||
expect(subject).not_to have_selector('span.fr-badge')
|
||||
end
|
||||
|
||||
it 'does not have Démarches link' do
|
||||
|
@ -79,7 +79,7 @@ describe MainNavigation::InstructeurExpertNavigationComponent, type: :component
|
|||
let(:unanswered) { 2 }
|
||||
|
||||
it 'renders an unanswered avis badge for the expert' do
|
||||
expect(subject).to have_selector('span.badge.warning', text: '2')
|
||||
expect(subject).to have_selector('span.fr-badge', text: '2')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
168
spec/controllers/administrateurs/labels_controller_spec.rb
Normal file
168
spec/controllers/administrateurs/labels_controller_spec.rb
Normal file
|
@ -0,0 +1,168 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Administrateurs::LabelsController, 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(:label, procedure:) }
|
||||
let!(:label_2) { create(:label, procedure:) }
|
||||
let!(:label_3) { create(: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
|
||||
{
|
||||
label: {
|
||||
name: 'Nouveau label',
|
||||
color: 'green-bourgeon'
|
||||
},
|
||||
procedure_id: procedure.id
|
||||
}
|
||||
end
|
||||
|
||||
it { expect { subject }.to change { Label.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(Label.last.name).to eq('Nouveau label')
|
||||
expect(Label.last.color).to eq('green_bourgeon')
|
||||
expect(procedure.labels.last).to eq(Label.last)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when submitting an invalid label' do
|
||||
let(:params) { { label: { name: 'Nouveau label' }, procedure_id: procedure.id } }
|
||||
|
||||
it { expect { subject }.not_to change { Label.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
|
||||
{
|
||||
label: {
|
||||
name: 'Nouveau label',
|
||||
color: 'green-bourgeon'
|
||||
},
|
||||
procedure_id: procedure_2.id
|
||||
}
|
||||
end
|
||||
|
||||
it { expect { subject }.not_to change { Label.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(:label, procedure:) }
|
||||
let(:label_params) { { name: 'Nouveau nom' } }
|
||||
let(:params) { { id: label.id, 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_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, 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(: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_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
|
|
@ -513,6 +513,11 @@ describe Administrateurs::ProceduresController, type: :controller do
|
|||
expect(response).to redirect_to(champs_admin_procedure_path(Procedure.last))
|
||||
expect(flash[:notice]).to be_present
|
||||
end
|
||||
|
||||
it "create generic labels" do
|
||||
expect(subject.labels.size).to eq(5)
|
||||
expect(subject.labels.first.name).to eq('À examiner')
|
||||
end
|
||||
end
|
||||
|
||||
describe "procedure is saved with custom retention period" do
|
||||
|
@ -657,7 +662,7 @@ describe Administrateurs::ProceduresController, type: :controller do
|
|||
end
|
||||
|
||||
describe 'PUT #clone' do
|
||||
let(:procedure) { create(:procedure, :with_notice, :with_deliberation, administrateur: admin) }
|
||||
let(:procedure) { create(:procedure, :with_notice, :with_deliberation, :with_labels, administrateur: admin) }
|
||||
let(:params) { { procedure_id: procedure.id } }
|
||||
|
||||
subject { put :clone, params: params }
|
||||
|
@ -679,6 +684,10 @@ describe Administrateurs::ProceduresController, type: :controller do
|
|||
expect(Procedure.last.cloned_from_library).to be_falsey
|
||||
expect(Procedure.last.notice.attached?).to be_truthy
|
||||
expect(Procedure.last.deliberation.attached?).to be_truthy
|
||||
expect(Procedure.last.labels.present?).to be_truthy
|
||||
expect(Procedure.last.labels.first.procedure_id).to eq(Procedure.last.id)
|
||||
expect(procedure.labels.first.procedure_id).to eq(procedure.id)
|
||||
|
||||
expect(flash[:notice]).to have_content 'Démarche clonée. Pensez à vérifier la présentation et choisir le service à laquelle cette démarche est associée.'
|
||||
end
|
||||
|
||||
|
@ -700,6 +709,7 @@ describe Administrateurs::ProceduresController, type: :controller do
|
|||
|
||||
it 'creates a new procedure and redirect to it' do
|
||||
expect(response).to redirect_to admin_procedure_path(id: Procedure.last.id)
|
||||
expect(Procedure.last.labels.present?).to be_truthy
|
||||
expect(flash[:notice]).to have_content 'Démarche clonée. Pensez à vérifier la présentation et choisir le service à laquelle cette démarche est associée.'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1518,4 +1518,38 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
expect([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp, Commentaire]).to include(*assigns(:gallery_attachments).map { _1.record.class })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'dossier_labels' do
|
||||
let(:procedure) { create(:procedure, :with_labels, instructeurs: [instructeur]) }
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure:) }
|
||||
context 'it create dossier labels' do
|
||||
subject { post :dossier_labels, params: { procedure_id: procedure.id, dossier_id: dossier.id, label_id: [Label.first.id] }, format: :turbo_stream }
|
||||
it 'works' do
|
||||
subject
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.dossier_labels.count).to eq(1)
|
||||
expect(subject.body).to include('fr-tag--purple-glycine')
|
||||
expect(subject.body).not_to include('Ajouter un label')
|
||||
end
|
||||
end
|
||||
|
||||
context 'it remove dossier labels' do
|
||||
before do
|
||||
DossierLabel.create(dossier_id: dossier.id, label_id: dossier.procedure.labels.first.id)
|
||||
end
|
||||
|
||||
subject { post :dossier_labels, params: { procedure_id: procedure.id, dossier_id: dossier.id, label_id: [] }, format: :turbo_stream }
|
||||
|
||||
it 'works' do
|
||||
expect(dossier.dossier_labels.count).to eq(1)
|
||||
|
||||
subject
|
||||
dossier.reload
|
||||
|
||||
expect(dossier.dossier_labels.count).to eq(0)
|
||||
expect(subject.body).to include('Ajouter un label')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -637,6 +637,38 @@ describe Instructeurs::ProceduresController, type: :controller do
|
|||
it { expect(assigns(:last_export)).to eq(nil) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'dossier labels' do
|
||||
let(:procedure) { create(:procedure, :with_labels, instructeurs: [instructeur]) }
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure:, groupe_instructeur: gi_2) }
|
||||
let!(:dossier_2) { create(:dossier, :en_construction, procedure:, groupe_instructeur: gi_2) }
|
||||
let(:statut) { 'tous' }
|
||||
let(:label_id) { procedure.find_column(label: 'Labels') }
|
||||
let!(:procedure_presentation) do
|
||||
ProcedurePresentation.create!(assign_to: AssignTo.first)
|
||||
end
|
||||
render_views
|
||||
|
||||
before do
|
||||
DossierLabel.create(dossier_id: dossier.id, label_id: dossier.procedure.labels.first.id)
|
||||
DossierLabel.create(dossier_id: dossier.id, label_id: dossier.procedure.labels.second.id)
|
||||
DossierLabel.create(dossier_id: dossier_2.id, label_id: dossier.procedure.labels.last.id)
|
||||
|
||||
procedure_presentation.update(displayed_columns: [
|
||||
label_id.id
|
||||
])
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'displays correctly labels in instructeur table' do
|
||||
expect(response.body).to include("Labels")
|
||||
expect(response.body).to have_selector('ul.fr-tags-group li span.fr-tag', text: 'À examiner')
|
||||
expect(response.body).to have_selector('ul.fr-tags-group li span.fr-tag', text: 'À relancer')
|
||||
expect(response.body).not_to have_selector('ul li span.fr-tag', text: 'Urgent')
|
||||
expect(response.body).to have_selector('span.fr-tag', text: 'Urgent')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
9
spec/factories/label.rb
Normal file
9
spec/factories/label.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :label do
|
||||
name { 'Un label' }
|
||||
color { 'green-bourgeon' }
|
||||
association :procedure
|
||||
end
|
||||
end
|
|
@ -291,6 +291,12 @@ FactoryBot.define do
|
|||
trait :accuse_lecture do
|
||||
accuse_lecture { true }
|
||||
end
|
||||
|
||||
trait :with_labels do
|
||||
after(:create) do |procedure, _evaluator|
|
||||
procedure.create_generic_labels
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ describe ColumnsConcern do
|
|||
{ label: 'Groupe instructeur', table: 'groupe_instructeur', column: 'id', displayable: true, type: :enum, scope: '', value_column: :value, filterable: true },
|
||||
{ label: 'Avis oui/non', table: 'avis', column: 'question_answer', displayable: true, type: :text, scope: '', value_column: :value, filterable: false },
|
||||
{ label: 'France connecté ?', table: 'self', column: 'user_from_france_connect?', displayable: false, type: :text, scope: '', value_column: :value, filterable: false },
|
||||
{ label: "Labels", table: "dossier_labels", column: "label_id", displayable: true, scope: '', value_column: :value, filterable: true },
|
||||
{ label: 'SIREN', table: 'etablissement', column: 'entreprise_siren', displayable: true, type: :text, scope: '', value_column: :value, filterable: true },
|
||||
{ label: 'Forme juridique', table: 'etablissement', column: 'entreprise_forme_juridique', displayable: true, type: :text, scope: '', value_column: :value, filterable: true },
|
||||
{ label: 'Nom commercial', table: 'etablissement', column: 'entreprise_nom_commercial', displayable: true, type: :text, scope: '', value_column: :value, filterable: true },
|
||||
|
|
|
@ -72,7 +72,7 @@ describe 'Inviting an expert:', js: true do
|
|||
expect(page).to have_text('1 avis à donner')
|
||||
expect(page).to have_text('0 avis donnés')
|
||||
|
||||
expect(page).to have_selector('.badge', text: 1)
|
||||
expect(page).to have_selector('.fr-badge', text: 1)
|
||||
expect(page).to have_selector('.notifications')
|
||||
|
||||
click_on '1 avis à donner'
|
||||
|
@ -93,7 +93,7 @@ describe 'Inviting an expert:', js: true do
|
|||
expect(page).to have_text('0 avis à donner')
|
||||
expect(page).to have_text('1 avis donné')
|
||||
|
||||
expect(page).not_to have_selector('.badge', text: 1)
|
||||
expect(page).not_to have_selector('.fr-badge', text: 1)
|
||||
expect(page).not_to have_selector('.notifications')
|
||||
end
|
||||
|
||||
|
|
|
@ -272,6 +272,39 @@ describe 'Instructing a dossier:', js: true do
|
|||
after { DownloadHelpers.clear_downloads }
|
||||
end
|
||||
|
||||
context 'An instructeur can add labels' do
|
||||
let(:procedure) { create(:procedure, :with_labels, :published, instructeurs: [instructeur]) }
|
||||
|
||||
scenario 'An instructeur can add and remove labels to a dossier' do
|
||||
log_in(instructeur.email, password)
|
||||
|
||||
visit instructeur_dossier_path(procedure, dossier)
|
||||
click_on 'Ajouter un label'
|
||||
|
||||
check 'À relancer', allow_label_click: true
|
||||
expect(page).to have_css('.fr-tag', text: "À relancer", count: 2)
|
||||
expect(dossier.dossier_labels.count).to eq(1)
|
||||
|
||||
expect(page).not_to have_text('Ajouter un label')
|
||||
find('span.dropdown button.dropdown-button').click
|
||||
|
||||
expect(page).to have_checked_field('À relancer')
|
||||
check 'Complet', allow_label_click: true
|
||||
|
||||
expect(page).to have_css('.fr-tag', text: "Complet", count: 2)
|
||||
expect(dossier.dossier_labels.count).to eq(2)
|
||||
|
||||
find('span.dropdown button.dropdown-button').click
|
||||
uncheck 'À relancer', allow_label_click: true
|
||||
|
||||
expect(page).to have_unchecked_field('À relancer')
|
||||
expect(page).to have_checked_field('Complet')
|
||||
expect(page).to have_css('.fr-tag', text: "À relancer", count: 1)
|
||||
expect(page).to have_css('.fr-tag', text: "Complet", count: 2)
|
||||
expect(dossier.dossier_labels.count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
def log_in(email, password, check_email: true)
|
||||
visit new_user_session_path
|
||||
expect(page).to have_current_path(new_user_session_path)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
describe "procedure filters" do
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:, instructeurs: [instructeur]) }
|
||||
let(:procedure) { create(:procedure, :published, :with_labels, types_de_champ_public:, instructeurs: [instructeur]) }
|
||||
let(:types_de_champ_public) { [{ type: :text }] }
|
||||
let!(:type_de_champ) { procedure.active_revision.types_de_champ_public.first }
|
||||
let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
|
||||
|
@ -94,6 +94,7 @@ describe "procedure filters" do
|
|||
expect(page).to have_link(new_unfollow_dossier_2.user.email)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with dropdown' do
|
||||
let(:types_de_champ_public) { [{ type: :drop_down_list }] }
|
||||
|
||||
|
@ -171,6 +172,15 @@ describe "procedure filters" do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'dossier labels' do
|
||||
scenario "should be able to filter by dossier labels", js: true do
|
||||
DossierLabel.create!(dossier_id: new_unfollow_dossier.id, label_id: procedure.labels.first.id)
|
||||
add_filter('Labels', procedure.labels.first.name, type: :enum)
|
||||
expect(page).to have_link(new_unfollow_dossier.id.to_s)
|
||||
expect(page).not_to have_link(new_unfollow_dossier_2.id.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
scenario "should be able to add and remove two filters for the same field", js: true do
|
||||
add_filter(type_de_champ.libelle, champ.value)
|
||||
add_filter(type_de_champ.libelle, champ_2.value)
|
||||
|
|
|
@ -217,4 +217,44 @@ describe 'instructeurs/dossiers/show', type: :view do
|
|||
expect(subject).to have_selector('a.fr-sidemenu__link', text: 'l1')
|
||||
end
|
||||
end
|
||||
|
||||
describe "Dossier labels" do
|
||||
let(:procedure) { create(:procedure, :with_labels) }
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure:) }
|
||||
|
||||
context "Procedure without labels" do
|
||||
let(:procedure_without_labels) { create(:procedure) }
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure: procedure_without_labels) }
|
||||
it 'does not display button to add label or dropdown' do
|
||||
expect(subject).not_to have_text("Ajouter un label")
|
||||
expect(subject).not_to have_text("À examiner")
|
||||
end
|
||||
end
|
||||
|
||||
context "Dossier without labels" do
|
||||
it 'displays button with text to add label' do
|
||||
expect(subject).to have_text("Ajouter un label")
|
||||
expect(subject).to have_selector("button.dropdown-button")
|
||||
expect(subject).to have_text("À examiner", count: 1)
|
||||
within('.dropdown') do
|
||||
expect(subject).to have_text("À examiner", count: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "Dossier with labels" do
|
||||
before do
|
||||
DossierLabel.create(dossier_id: dossier.id, label_id: dossier.procedure.labels.first.id)
|
||||
end
|
||||
|
||||
it 'displays labels and button without text to add label' do
|
||||
expect(subject).not_to have_text("Ajouter un label")
|
||||
expect(subject).to have_selector("button.dropdown-button")
|
||||
expect(subject).to have_text("À examiner", count: 2)
|
||||
within('.dropdown') do
|
||||
expect(subject).to have_text("À examiner", count: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue