Merge pull request #8940 from demarches-simplifiees/routing-new-ux
Nouvelle UX pour le routage
This commit is contained in:
commit
bbedf0e659
59 changed files with 917 additions and 714 deletions
|
@ -42,6 +42,10 @@
|
|||
&.row-reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
&.auto {
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-grow {
|
||||
|
|
|
@ -2,10 +2,11 @@ class Dsfr::SidemenuComponent < ApplicationComponent
|
|||
renders_many :links, "LinkComponent"
|
||||
|
||||
class LinkComponent < ApplicationComponent
|
||||
attr_reader :name, :url
|
||||
def initialize(name:, url:)
|
||||
attr_reader :name, :url, :icon
|
||||
def initialize(name:, url:, icon: nil)
|
||||
@name = name
|
||||
@url = url
|
||||
@icon = icon
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,4 +6,8 @@
|
|||
%ul.fr-sidemenu__list
|
||||
- links.each do |link|
|
||||
%li{ class: "fr-sidemenu__item fr-sidemenu__item#{active?(link.url) ? '--active' : ''}" }
|
||||
= link_to link.name, link.url, class: 'fr-sidemenu__link', 'aria-current': active?(link.url) ? 'page' : nil, target: "_self"
|
||||
= link_to link.url, class: 'fr-sidemenu__link ', 'aria-current': active?(link.url) ? 'page' : nil, target: "_self" do
|
||||
- capture do
|
||||
- if link.icon.present?
|
||||
%span{ class: link.icon }
|
||||
= link.name
|
||||
|
|
6
app/components/procedure/groupes_ajout_component.rb
Normal file
6
app/components/procedure/groupes_ajout_component.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
class Procedure::GroupesAjoutComponent < ApplicationComponent
|
||||
def initialize(procedure:, groupe_instructeurs:)
|
||||
@procedure = procedure
|
||||
@groupe_instructeurs = groupe_instructeurs
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
fr:
|
||||
add_a_group:
|
||||
title: Nouveau groupe
|
||||
button:
|
||||
add_group: Ajouter
|
|
@ -0,0 +1,15 @@
|
|||
- content_for(:title, 'Ajout de groupes')
|
||||
%h1 Ajout de groupes d'instructeurs
|
||||
|
||||
= render partial: 'administrateurs/groupe_instructeurs/import_export',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeurs: @groupe_instructeurs }
|
||||
|
||||
%section
|
||||
= form_for :groupe_instructeur,
|
||||
method: :post do |f|
|
||||
= f.label :label, class: 'fr-label fr-mb-1w' do
|
||||
= t('.add_a_group.title')
|
||||
.flex.justify-between.align-baseline.fr-mb-1w
|
||||
= f.text_field :label, required: true, class: 'fr-input', placeholder: 'Entrer un nom de groupe'
|
||||
= f.button t('.button.add_group'), class: "fr-btn fr-btn fr-btn--secondary fr-btn--icon-right fr-icon-add-line ml-2"
|
26
app/components/procedure/groupes_management_component.rb
Normal file
26
app/components/procedure/groupes_management_component.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Procedure::GroupesManagementComponent < ApplicationComponent
|
||||
def initialize(procedure:, groupe_instructeurs:, query:)
|
||||
@procedure = procedure
|
||||
@groupe_instructeurs = groupe_instructeurs
|
||||
@query = query
|
||||
@total = groupe_instructeurs.total_count
|
||||
end
|
||||
|
||||
def table_header
|
||||
if @query.present?
|
||||
if @groupe_instructeurs.length != @total
|
||||
"#{t('.groupe', count: @groupe_instructeurs.length)} sur #{@total} #{t('.found', count: @total)}"
|
||||
else
|
||||
"#{t('.groupe', count: @groupe_instructeurs.length)} #{t('.found', count: @groupe_instructeurs.length)}"
|
||||
end
|
||||
else
|
||||
if @groupe_instructeurs.length != @total
|
||||
"#{t('.groupe', count: @groupe_instructeurs.length)} sur #{@total}"
|
||||
else
|
||||
t('.groupe', count: @groupe_instructeurs.length)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
fr:
|
||||
groupe:
|
||||
one: "%{count} groupe"
|
||||
other: "%{count} groupes"
|
||||
found:
|
||||
one: "trouvé"
|
||||
other: "trouvés"
|
|
@ -0,0 +1,40 @@
|
|||
- content_for(:title, 'Groupes')
|
||||
%h1 Gestion des groupes
|
||||
|
||||
= render Procedure::GroupesSearchComponent.new(procedure: @procedure, query: @query, to_configure_count: @procedure.groupe_instructeurs.filter(&:routing_to_configure?).count)
|
||||
|
||||
.fr-table.fr-table--no-caption.fr-table--layout-fixed.fr-mt-2w
|
||||
%table
|
||||
%caption= table_header
|
||||
%thead
|
||||
%tr
|
||||
%th{ scope: "col" }
|
||||
.flex
|
||||
.flex.auto= table_header
|
||||
%span.fr-icon.fr-icon-user-line
|
||||
%tbody
|
||||
- @groupe_instructeurs.each do |gi|
|
||||
%tr
|
||||
%td
|
||||
.flex
|
||||
.flex.auto
|
||||
= link_to admin_procedure_groupe_instructeur_path(@procedure, gi), class: 'fr-link' do
|
||||
%span= gi.label
|
||||
- if gi.closed
|
||||
%p.fr-badge.fr-badge--info.fr-badge--sm.fr-ml-1w inactif
|
||||
- elsif gi.routing_to_configure?
|
||||
%p.fr-badge.fr-badge--warning.fr-badge--sm.fr-ml-1w à configurer
|
||||
|
||||
%div{ style: 'width: 25px; text-align: center;' }
|
||||
#{gi.instructeurs.count}
|
||||
%p= gi.routing_rule&.to_s(@procedure.active_revision.types_de_champ)
|
||||
.fr-mt-1w
|
||||
= paginate @groupe_instructeurs
|
||||
|
||||
= form_tag admin_procedure_update_defaut_groupe_instructeur_path,
|
||||
class: 'fr-my-3w',
|
||||
data: { controller: 'autosave' } do
|
||||
= label_tag :defaut_groupe_instructeur_id, 'Et si aucune règle ne correspond, router vers :', class: 'fr-label'
|
||||
= select_tag :defaut_groupe_instructeur_id,
|
||||
options_for_select(@procedure.groupe_instructeurs.pluck(:label, :id), selected: @procedure.defaut_groupe_instructeur.id),
|
||||
class: 'fr-select'
|
5
app/components/procedure/groupes_search_component.rb
Normal file
5
app/components/procedure/groupes_search_component.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class Procedure::GroupesSearchComponent < ApplicationComponent
|
||||
def initialize(procedure:, query:, to_configure_count:)
|
||||
@procedure, @query, @to_configure_count = procedure, query, to_configure_count
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
= form_for admin_procedure_groupe_instructeurs_path(@procedure),
|
||||
method: :get do
|
||||
#header-search.fr-search-bar.fr-mb-2w{ role: "search" }
|
||||
= label_tag :q, 'Rechercher par nom', class: 'fr-label'
|
||||
= text_field_tag :q, @query, class: 'fr-input', type: 'search', autocomplete: 'off', placeholder: 'Rechercher par nom'
|
||||
%button.fr-btn{ title: "Rechercher" } Rechercher
|
||||
|
||||
- if @to_configure_count > 0
|
||||
%span #{ @to_configure_count } à configurer
|
|
@ -0,0 +1,9 @@
|
|||
class Procedure::InstructeursManagementComponent < ApplicationComponent
|
||||
def initialize(procedure:, groupe_instructeur:, instructeurs:, available_instructeur_emails:, disabled_as_super_admin:)
|
||||
@procedure = procedure
|
||||
@groupe_instructeur = groupe_instructeur
|
||||
@instructeurs = instructeurs
|
||||
@available_instructeur_emails = available_instructeur_emails
|
||||
@disabled_as_super_admin = disabled_as_super_admin
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
- content_for(:title, 'Instructeurs')
|
||||
%h1 Gestion des instructeurs
|
||||
|
||||
= render partial: 'administrateurs/groupe_instructeurs/import_export',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeurs: @procedure.groupe_instructeurs }
|
||||
|
||||
= render partial: 'administrateurs/groupe_instructeurs/instructeurs',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeur: @groupe_instructeur,
|
||||
instructeurs: @instructeurs,
|
||||
available_instructeur_emails: @available_instructeur_emails,
|
||||
disabled_as_super_admin: @disabled_as_super_admin }
|
22
app/components/procedure/instructeurs_menu_component.rb
Normal file
22
app/components/procedure/instructeurs_menu_component.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
class Procedure::InstructeursMenuComponent < ApplicationComponent
|
||||
def initialize(procedure:)
|
||||
@procedure = procedure
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def links
|
||||
first_option, first_icon = if @procedure.groupe_instructeurs.one?
|
||||
instructeurs_count = @procedure.groupe_instructeurs.first.instructeurs.count
|
||||
[t('.instructeurs', count: instructeurs_count), 'fr-icon-user-line']
|
||||
else
|
||||
["#{@procedure.groupe_instructeurs.count} groupes", 'fr-icon-group-line']
|
||||
end
|
||||
|
||||
[
|
||||
{ name: first_option, url: admin_procedure_groupe_instructeurs_path(@procedure), icon: "#{first_icon} fr-icon--sm" },
|
||||
({ name: 'Ajout de groupes', url: ajout_admin_procedure_groupe_instructeurs_path(@procedure), icon: 'fr-icon-add-circle-line fr-icon--sm' } if @procedure.routing_enabled?),
|
||||
{ name: 'Options', url: options_admin_procedure_groupe_instructeurs_path(@procedure), icon: 'fr-icon-settings-5-line fr-icon--sm' }
|
||||
].compact
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
fr:
|
||||
instructeurs:
|
||||
one: "%{count} instructeur"
|
||||
other: "%{count} instructeurs"
|
|
@ -0,0 +1,7 @@
|
|||
.container
|
||||
.fr-grid-row
|
||||
.fr-col.fr-col-12.fr-col-md-3
|
||||
= render(Dsfr::SidemenuComponent.new) do |component|
|
||||
- component.with_links(links)
|
||||
.fr-col= content
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class Procedure::InstructeursOptionsComponent < ApplicationComponent
|
||||
def initialize(procedure:, state:)
|
||||
@procedure = procedure
|
||||
@state = state
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
fr:
|
||||
routing_configuration_notice_1:
|
||||
Le routage permet d’acheminer les dossiers vers différents groupes d’instructeurs.
|
||||
routing_configuration_notice_2_html: |
|
||||
<p>Pour le configurer, votre formulaire doit comporter
|
||||
au moins un champ « choix simple ».</p>
|
||||
<p>Ajoutez ce champ dans la page <a href="%{path}">« Configuration des champs »</a>.</p>
|
||||
delete_title: Aucun dossier ne sera supprimé. Les groupes d'instructeurs vont être supprimés. Seuls les instructeurs du groupe « %{defaut_label} » resteront affectés à la procédure.
|
||||
delete_confirmation: |
|
||||
Attention : tous les dossiers vont être déplacés dans le groupe « %{defaut_label} » et seuls les instructeurs présent dans ce groupe resteront affectés à la procédure. Souhaitez-vous continuer ?
|
|
@ -0,0 +1,49 @@
|
|||
- content_for(:title, 'Options')
|
||||
- if @state.nil?
|
||||
%h1 Options concernant l’instruction
|
||||
|
||||
%ul.fr-toggle__list
|
||||
%li
|
||||
= form_for @procedure,
|
||||
method: :patch,
|
||||
url: update_instructeurs_self_management_enabled_admin_procedure_groupe_instructeurs_path(@procedure),
|
||||
data: { controller: 'autosubmit', turbo: 'true' } do |f|
|
||||
|
||||
= render Dsfr::ToggleComponent.new(form: f,
|
||||
target: :instructeurs_self_management_enabled,
|
||||
title: 'Autogestion des instructeurs',
|
||||
hint: "L’autogestion des instructeurs permet aux instructeurs de gérer eux-mêmes la liste des instructeurs de la démarche.#{ ' Lorsque la démarche est routée, l’autogestion est activée d’office et n’est pas désactivable.' if @procedure.routing_enabled? }",
|
||||
disabled: @procedure.routing_enabled?)
|
||||
%p.fr-mt-2w Routage
|
||||
%p.fr-mt-2w= t('.routing_configuration_notice_1')
|
||||
- if @procedure.active_revision.routable_types_de_champ.none?
|
||||
%p.fr-mt-2w= t('.routing_configuration_notice_2_html', path: champs_admin_procedure_path(@procedure))
|
||||
- elsif @procedure.groupe_instructeurs.active.one?
|
||||
= link_to 'Configurer le routage', options_admin_procedure_groupe_instructeurs_path(@procedure, state: :choix), class: 'fr-btn'
|
||||
|
||||
- else
|
||||
= button_to 'Supprimer le routage',
|
||||
destroy_all_groups_but_defaut_admin_procedure_groupe_instructeurs_path,
|
||||
class: 'fr-btn',
|
||||
method: :delete,
|
||||
title: t('.delete_title', defaut_label: @procedure.defaut_groupe_instructeur.label),
|
||||
data: ( @procedure.publiee? ? { disable_with: "Suppression...", confirm: t('.delete_confirmation', defaut_label: @procedure.defaut_groupe_instructeur.label) } : nil)
|
||||
|
||||
- elsif @state == 'choix'
|
||||
= form_for :choice,
|
||||
method: :patch,
|
||||
data: { controller: 'radio-enabled-submit' },
|
||||
url: wizard_admin_procedure_groupe_instructeurs_path(@procedure) do |f|
|
||||
|
||||
%div{ data: { 'action': "click->radio-enabled-submit#click" } }
|
||||
= render Dsfr::RadioButtonListComponent.new(form: f,
|
||||
target: :state,
|
||||
buttons: [ { label: 'À partir d’un champ', value: 'routage_simple', hint: 'crée les groupes en fonction d’un champ du formulaire' } ,
|
||||
{ label: 'Avancé', value: 'routage_custom', hint: 'libre à vous de créer et de configurer les groupes' }]) do
|
||||
%h1 Choix du type de routage
|
||||
|
||||
%ul.fr-btns-group.fr-btns-group--inline-sm
|
||||
%li
|
||||
= link_to 'Retour', options_admin_procedure_groupe_instructeurs_path(@procedure), class: 'fr-btn fr-btn--secondary'
|
||||
%li
|
||||
%button.fr-btn{ disabled: true, data: { 'radio-enabled-submit-target': 'submit' } } Continuer
|
65
app/components/procedure/one_groupe_management_component.rb
Normal file
65
app/components/procedure/one_groupe_management_component.rb
Normal file
|
@ -0,0 +1,65 @@
|
|||
class Procedure::OneGroupeManagementComponent < ApplicationComponent
|
||||
include Logic
|
||||
|
||||
def initialize(revision:, groupe_instructeur:)
|
||||
@revision = revision
|
||||
@groupe_instructeur = groupe_instructeur
|
||||
@procedure_id = revision.procedure_id
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def targeted_champ
|
||||
@groupe_instructeur.routing_rule&.left || empty
|
||||
end
|
||||
|
||||
def value
|
||||
@groupe_instructeur.routing_rule&.right || empty
|
||||
end
|
||||
|
||||
def targeted_champ_tag
|
||||
select_tag(
|
||||
'targeted_champ',
|
||||
options_for_select(targeted_champs_for_select, selected: targeted_champ.to_json),
|
||||
class: 'fr-select'
|
||||
)
|
||||
end
|
||||
|
||||
def targeted_champs_for_select
|
||||
empty_target_for_select + available_targets_for_select
|
||||
end
|
||||
|
||||
def empty_target_for_select
|
||||
[[t('.select'), empty.to_json]]
|
||||
end
|
||||
|
||||
def available_targets_for_select
|
||||
@revision
|
||||
.routable_types_de_champ
|
||||
.map { |tdc| [tdc.libelle, champ_value(tdc.stable_id).to_json] }
|
||||
end
|
||||
|
||||
def value_tag
|
||||
select_tag(
|
||||
'value',
|
||||
options_for_select(
|
||||
values_for_select(targeted_champ),
|
||||
selected: value.to_json
|
||||
),
|
||||
class: 'fr-select'
|
||||
)
|
||||
end
|
||||
|
||||
def values_for_select(targeted_champ)
|
||||
(empty_target_for_select + available_values_for_select(targeted_champ))
|
||||
# add id to help morph render selected option
|
||||
.map { |(libelle, json)| [libelle, json, { id: "option-#{libelle}" }] }
|
||||
end
|
||||
|
||||
def available_values_for_select(targeted_champ)
|
||||
return [] if targeted_champ.is_a?(Logic::Empty)
|
||||
targeted_champ
|
||||
.options(@revision.types_de_champ_public)
|
||||
.map { |tdc| [tdc.first, constant(tdc.first).to_json] }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
fr:
|
||||
select: Sélectionner
|
||||
move_files_confirmation: Réaffecter les dossiers à un autre groupe afin de pouvoir le supprimer
|
||||
move_files:
|
||||
zero: Déplacer les dossiers en brouillon
|
||||
one: Déplacer un dossier
|
||||
other: Déplacer les %{count} dossiers
|
|
@ -0,0 +1,55 @@
|
|||
%div{ id: dom_id(@groupe_instructeur, :routing) }
|
||||
%h2 Paramètres principaux
|
||||
|
||||
= form_for @groupe_instructeur,
|
||||
url: admin_procedure_groupe_instructeur_path(@procedure_id, @groupe_instructeur),
|
||||
method: :patch do |f|
|
||||
= f.label :label, 'Nom du groupe', class: 'fr-label fr-mb-1w'
|
||||
.flex
|
||||
= f.text_field :label, required: true, class: 'fr-input flex auto fr-mr-2w'
|
||||
= f.button 'Renommer', class: 'fr-btn fr-btn--secondary'
|
||||
|
||||
= form_for @groupe_instructeur,
|
||||
url: admin_procedure_groupe_instructeur_update_state_path(@procedure_id, @groupe_instructeur),
|
||||
method: :patch,
|
||||
data: { turbo: true, controller: 'autosubmit' } do |f|
|
||||
.fr-checkbox-group.fr-my-3w
|
||||
= f.check_box :closed, { id: 'closed', "aria-describedby" => "closed-messages", :name => "closed" }
|
||||
%label.fr-label{ :for => "closed" }
|
||||
Groupe inactif
|
||||
%span.fr-hint-text Si cette option est activée, les usagers ne pourront plus sélectionner ce groupe d'instructeurs
|
||||
|
||||
= form_tag admin_procedure_routing_rules_path(@procedure_id),
|
||||
method: :post,
|
||||
data: { controller: 'autosave' },
|
||||
class: 'fr-mb-3w' do
|
||||
|
||||
= hidden_field_tag('groupe_instructeur_id', @groupe_instructeur.id)
|
||||
|
||||
.flex
|
||||
%p.fr-mb-1w.fr-mr-2w Routage
|
||||
- if @groupe_instructeur.routing_to_configure?
|
||||
%p.fr-mb-1w.fr-badge.fr-badge--warning.fr-badge--sm à configurer
|
||||
|
||||
.flex.align-baseline
|
||||
.fr-mr-2w si le champ
|
||||
.target.fr-mr-2w
|
||||
= targeted_champ_tag
|
||||
.operator.fr-mr-2w est égal à
|
||||
.value
|
||||
= value_tag
|
||||
|
||||
%ul.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline.fr-btns-group--icon-right
|
||||
%li
|
||||
- if @groupe_instructeur.can_delete?
|
||||
%p Supprimer le groupe
|
||||
= button_to admin_procedure_groupe_instructeur_path(@procedure_id, @groupe_instructeur),
|
||||
class: 'fr-btn fr-btn--tertiary fr-btn--icon-left fr-icon-delete-line',
|
||||
method: :delete do
|
||||
Supprimer
|
||||
- else
|
||||
= button_to reaffecter_dossiers_admin_procedure_groupe_instructeur_path(@procedure_id, @groupe_instructeur),
|
||||
class: 'fr-btn fr-btn--tertiary fr-icon-folder-2-line',
|
||||
title: t('.move_files_confirmation'),
|
||||
method: :get do
|
||||
= t('.move_files', count: @groupe_instructeur.dossiers.visible_by_administration.size)
|
|
@ -24,6 +24,7 @@ fr:
|
|||
update_type_champ: Le type du champ « %{label} » a été modifié. Il est maintenant de type « %{to} ».
|
||||
update_piece_justificative_template: Le modèle de pièce justificative du champ « %{label} » a été modifié.
|
||||
update_drop_down_options: "Les options de sélection du champ « %{label} » ont été modifiées :"
|
||||
update_drop_down_options_alert: "Le champ « %{label} » est utilisé pour le routage des dossiers. Veuillez mettre à jour la configuration des groupes d'instructeurs après avoir publié les modifications."
|
||||
enable_mandatory: Le champ « %{label} » est maintenant obligatoire.
|
||||
disable_mandatory: Le champ « %{label} » n’est plus obligatoire.
|
||||
enable_drop_down_other: Le champ « %{label} » comporte maintenant un choix « Autre ».
|
||||
|
|
|
@ -74,6 +74,9 @@
|
|||
- if !total_dossiers.zero? && !change.can_rebase?
|
||||
.fr-alert.fr-alert--warning.fr-mt-1v
|
||||
%p= t('.breaking_change', count: total_dossiers)
|
||||
- if removed.present? && change.type_de_champ.used_by_routing_rules?
|
||||
.fr-alert.fr-alert--warning.fr-mt-1v
|
||||
= t(".#{prefix}.update_drop_down_options_alert", label: change.label)
|
||||
- when :drop_down_other
|
||||
- if change.from == false
|
||||
- list.with_item do
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
class Procedure::RoutingRulesComponent < ApplicationComponent
|
||||
include Logic
|
||||
|
||||
def initialize(revision:, groupe_instructeurs:)
|
||||
@revision = revision
|
||||
@groupe_instructeurs = groupe_instructeurs
|
||||
@procedure_id = revision.procedure_id
|
||||
end
|
||||
|
||||
def rows
|
||||
@groupe_instructeurs.active.map do |gi|
|
||||
if gi.routing_rule.present?
|
||||
[gi.routing_rule.left, gi.routing_rule.right, gi]
|
||||
else
|
||||
[empty, empty, gi]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def can_route?
|
||||
available_targets_for_select.present?
|
||||
end
|
||||
|
||||
def targeted_champ_tag(targeted_champ, row_index)
|
||||
select_tag(
|
||||
'targeted_champ',
|
||||
options_for_select(targeted_champs_for_select, selected: targeted_champ.to_json),
|
||||
id: input_id_for('targeted_champ', row_index)
|
||||
)
|
||||
end
|
||||
|
||||
def value_tag(targeted_champ, value, row_index)
|
||||
select_tag(
|
||||
'value',
|
||||
options_for_select(
|
||||
values_for_select(targeted_champ, row_index),
|
||||
selected: value.to_json
|
||||
),
|
||||
id: input_id_for('value', row_index)
|
||||
)
|
||||
end
|
||||
|
||||
def hidden_groupe_instructeur_tag(groupe_instructeur_id)
|
||||
hidden_field_tag(
|
||||
'groupe_instructeur_id',
|
||||
groupe_instructeur_id
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def targeted_champs_for_select
|
||||
empty_target_for_select + available_targets_for_select
|
||||
end
|
||||
|
||||
def empty_target_for_select
|
||||
[[t('.select'), empty.to_json]]
|
||||
end
|
||||
|
||||
def available_targets_for_select
|
||||
@revision.types_de_champ_public
|
||||
.filter { |tdc| [:drop_down_list].include?(tdc.type_champ.to_sym) }
|
||||
.map { |tdc| [tdc.libelle, champ_value(tdc.stable_id).to_json] }
|
||||
end
|
||||
|
||||
def available_values_for_select(targeted_champ)
|
||||
return [] if targeted_champ.is_a?(Logic::Empty)
|
||||
targeted_champ
|
||||
.options(@revision.types_de_champ_public)
|
||||
.map { |tdc| [tdc.first, constant(tdc.first).to_json] }
|
||||
end
|
||||
|
||||
def values_for_select(targeted_champ, row_index)
|
||||
(empty_target_for_select + available_values_for_select(targeted_champ))
|
||||
# add id to help morph render selected option
|
||||
.map { |(libelle, json)| [libelle, json, { id: "#{row_index}-option-#{libelle}" }] }
|
||||
end
|
||||
|
||||
def input_id_for(name, row_index)
|
||||
"#{name}-#{row_index}"
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
fr:
|
||||
select: Sélectionner
|
||||
apply_routing_rules: Règles de routage
|
||||
routing_rules_notice_html: |
|
||||
<p>Ajoutez des règles de routage à partir de champs « choix simple » créés dans le <a href="%{path}">formulaire</a>.</p>
|
||||
<p>Les dossiers seront routées vers le premier groupe affiché dont la règle correspond.</p>
|
||||
routing_rules_warning_html: |
|
||||
<p>Pour appliquer des règles de routage, votre formulaire doit comporter
|
||||
au moins un champ « choix simple ».</p>
|
||||
<p>Ajoutez ce champ dans la page <a href="%{path}">« Configuration des champs »</a>.</p>
|
|
@ -1,40 +0,0 @@
|
|||
.card#routing-rules
|
||||
%h2.card-title= t('.apply_routing_rules')
|
||||
- if can_route?
|
||||
.notice
|
||||
= t('.routing_rules_notice_html', path: champs_admin_procedure_path(@procedure_id))
|
||||
.mt-2.width-100
|
||||
%table.routing-rules-table.mt-2.width-100
|
||||
%thead
|
||||
%tr
|
||||
%th.far-left Router vers
|
||||
%th.if
|
||||
%th.target Champ cible du routage
|
||||
%th.operator
|
||||
%th.value Valeur
|
||||
.mt-2.width-100
|
||||
- rows.each.with_index do |(targeted_champ, value, groupe_instructeur), row_index|
|
||||
= form_tag admin_procedure_routing_rules_path(@procedure_id),
|
||||
method: :post,
|
||||
class: "form width-100 gi-#{groupe_instructeur.id}",
|
||||
data: { controller: 'autosave' } do
|
||||
= hidden_groupe_instructeur_tag(groupe_instructeur.id)
|
||||
%table.routing-rules-table.condition-table.mt-2.width-100
|
||||
%tbody
|
||||
%tr
|
||||
%td.far-left= groupe_instructeur.label
|
||||
%td.if si
|
||||
%td.target= targeted_champ_tag(targeted_champ, row_index)
|
||||
%td.operator est égal à
|
||||
%td.value= value_tag(targeted_champ, value, row_index)
|
||||
|
||||
= form_tag admin_procedure_update_defaut_groupe_instructeur_path(@procedure_id),
|
||||
class: 'form flex align-baseline defaut-groupe',
|
||||
data: { controller: 'autosave' } do
|
||||
= label_tag :defaut_groupe_instructeur_id, 'Et si aucune règle ne correspond, router vers :'
|
||||
= select_tag :defaut_groupe_instructeur_id,
|
||||
options_for_select(@groupe_instructeurs.pluck(:label, :id), selected: @revision.procedure.defaut_groupe_instructeur.id),
|
||||
class: 'width-100'
|
||||
|
||||
- else
|
||||
.notice= t('.routing_rules_warning_html', path: champs_admin_procedure_path(@procedure_id))
|
|
@ -1,6 +1,7 @@
|
|||
module Administrateurs
|
||||
class GroupeInstructeursController < AdministrateurController
|
||||
include ActiveSupport::NumberHelper
|
||||
include Logic
|
||||
|
||||
before_action :ensure_not_super_admin!, only: [:add_instructeur]
|
||||
|
||||
|
@ -19,6 +20,73 @@ module Administrateurs
|
|||
@available_instructeur_emails = available_instructeur_emails
|
||||
end
|
||||
|
||||
def options
|
||||
@procedure = procedure
|
||||
end
|
||||
|
||||
def ajout
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure) if procedure.groupe_instructeurs.one?
|
||||
@procedure = procedure
|
||||
@groupes_instructeurs = paginated_groupe_instructeurs
|
||||
end
|
||||
|
||||
def simple_routing
|
||||
@procedure = procedure
|
||||
end
|
||||
|
||||
def create_simple_routing
|
||||
@procedure = procedure
|
||||
stable_id = params[:create_simple_routing][:stable_id].to_i
|
||||
|
||||
tdc = @procedure.active_revision.routable_types_de_champ.find { |tdc| tdc.stable_id == stable_id }
|
||||
|
||||
tdc_options = tdc.options["drop_down_options"].reject(&:empty?)
|
||||
|
||||
tdc_options.each do |option_label|
|
||||
gi = procedure.groupe_instructeurs.find_by({ label: option_label }) || procedure.groupe_instructeurs
|
||||
.create({ label: option_label, instructeurs: [current_administrateur.instructeur] })
|
||||
gi.update(routing_rule: ds_eq(champ_value(stable_id), constant(gi.label)))
|
||||
end
|
||||
|
||||
defaut = @procedure.defaut_groupe_instructeur
|
||||
|
||||
if !tdc_options.include?(defaut.label)
|
||||
new_defaut = @procedure.reload.groupe_instructeurs_but_defaut.first
|
||||
@procedure.update!(defaut_groupe_instructeur: new_defaut)
|
||||
reaffecter_all_dossiers_to_defaut_groupe
|
||||
defaut.instructeurs.each { new_defaut.add(_1) }
|
||||
defaut.destroy!
|
||||
end
|
||||
|
||||
flash.notice = 'Les groupes instructeurs ont été ajoutés'
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
|
||||
end
|
||||
|
||||
def wizard
|
||||
if params[:choice][:state] == 'routage_custom'
|
||||
new_label = procedure.defaut_groupe_instructeur.label + ' bis'
|
||||
procedure.groupe_instructeurs
|
||||
.create({ label: new_label, instructeurs: [current_administrateur.instructeur] })
|
||||
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
|
||||
elsif params[:choice][:state] == 'routage_simple'
|
||||
redirect_to simple_routing_admin_procedure_groupe_instructeurs_path
|
||||
end
|
||||
end
|
||||
|
||||
def destroy_all_groups_but_defaut
|
||||
reaffecter_all_dossiers_to_defaut_groupe
|
||||
procedure.groupe_instructeurs_but_defaut.each(&:destroy!)
|
||||
procedure.update!(routing_enabled: false, instructeurs_self_management_enabled: false)
|
||||
procedure.defaut_groupe_instructeur.update!(
|
||||
routing_rule: nil,
|
||||
label: GroupeInstructeur::DEFAUT_LABEL,
|
||||
closed: false
|
||||
)
|
||||
flash.notice = 'Tous les groupes instructeurs ont été supprimés'
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
|
||||
end
|
||||
|
||||
def show
|
||||
@procedure = procedure
|
||||
@groupe_instructeur = groupe_instructeur
|
||||
|
@ -48,10 +116,7 @@ module Administrateurs
|
|||
def update
|
||||
@groupe_instructeur = groupe_instructeur
|
||||
|
||||
if closed_params? && @groupe_instructeur.id == procedure.defaut_groupe_instructeur.id
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur),
|
||||
alert: "Il est impossible de désactiver le groupe d’instructeurs par défaut."
|
||||
elsif @groupe_instructeur.update(groupe_instructeur_params)
|
||||
if @groupe_instructeur.update(groupe_instructeur_params)
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur),
|
||||
notice: "Le nom est à présent « #{@groupe_instructeur.label} »."
|
||||
else
|
||||
|
@ -64,6 +129,26 @@ module Administrateurs
|
|||
end
|
||||
end
|
||||
|
||||
def update_state
|
||||
@groupe_instructeur = procedure.groupe_instructeurs.find(params[:groupe_instructeur_id])
|
||||
|
||||
if closed_params? && @groupe_instructeur.id == procedure.defaut_groupe_instructeur.id
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
|
||||
alert: "Il est impossible de désactiver le groupe d’instructeurs par défaut."
|
||||
elsif @groupe_instructeur.update(closed: params[:closed])
|
||||
state_for_notice = @groupe_instructeur.closed ? 'désactivé' : 'activé'
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, @groupe_instructeur),
|
||||
notice: "Le groupe #{@groupe_instructeur.label} est #{state_for_notice}."
|
||||
else
|
||||
@procedure = procedure
|
||||
@instructeurs = paginated_instructeurs
|
||||
@available_instructeur_emails = available_instructeur_emails
|
||||
|
||||
flash.now[:alert] = @groupe_instructeur.errors.full_messages
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@groupe_instructeur = groupe_instructeur
|
||||
|
||||
|
@ -76,7 +161,15 @@ module Administrateurs
|
|||
else
|
||||
@groupe_instructeur.destroy!
|
||||
if procedure.groupe_instructeurs.active.one?
|
||||
procedure.update!(routing_enabled: false)
|
||||
procedure.update!(
|
||||
routing_enabled: false,
|
||||
instructeurs_self_management_enabled: false
|
||||
)
|
||||
procedure.defaut_groupe_instructeur.update!(
|
||||
routing_rule: nil,
|
||||
label: GroupeInstructeur::DEFAUT_LABEL,
|
||||
closed: false
|
||||
)
|
||||
routing_notice = " et le routage a été désactivé"
|
||||
end
|
||||
flash[:notice] = "le groupe « #{@groupe_instructeur.label} » a été supprimé#{routing_notice}."
|
||||
|
@ -112,6 +205,14 @@ module Administrateurs
|
|||
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
|
||||
end
|
||||
|
||||
def reaffecter_all_dossiers_to_defaut_groupe
|
||||
procedure.groupe_instructeurs_but_defaut.each do |gi|
|
||||
gi.dossiers.find_each do |dossier|
|
||||
dossier.assign_to_groupe_instructeur(procedure.defaut_groupe_instructeur, current_administrateur)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def add_instructeur
|
||||
emails = params['emails'].presence || [].to_json
|
||||
emails = JSON.parse(emails).map { EmailSanitizableConcern::EmailSanitizer.sanitize(_1) }
|
||||
|
@ -193,7 +294,7 @@ module Administrateurs
|
|||
def update_instructeurs_self_management_enabled
|
||||
procedure.update!(instructeurs_self_management_enabled_params)
|
||||
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure),
|
||||
redirect_to options_admin_procedure_groupe_instructeurs_path(procedure),
|
||||
notice: "L’autogestion des instructeurs est #{procedure.instructeurs_self_management_enabled? ? "activée" : "désactivée"}."
|
||||
end
|
||||
|
||||
|
@ -264,7 +365,7 @@ module Administrateurs
|
|||
private
|
||||
|
||||
def closed_params?
|
||||
groupe_instructeur_params[:closed] == "1"
|
||||
params[:closed] == "1"
|
||||
end
|
||||
|
||||
def procedure
|
||||
|
@ -287,12 +388,21 @@ module Administrateurs
|
|||
end
|
||||
|
||||
def groupe_instructeur_params
|
||||
params.require(:groupe_instructeur).permit(:label, :closed)
|
||||
params.require(:groupe_instructeur).permit(:label)
|
||||
end
|
||||
|
||||
def paginated_groupe_instructeurs
|
||||
procedure
|
||||
.groupe_instructeurs
|
||||
groupes = if params[:q].present?
|
||||
query = ActiveRecord::Base.sanitize_sql_like(params[:q])
|
||||
|
||||
procedure
|
||||
.groupe_instructeurs
|
||||
.where('unaccent(label) ILIKE unaccent(?)', "%#{query}%")
|
||||
else
|
||||
procedure.groupe_instructeurs
|
||||
end
|
||||
|
||||
groupes
|
||||
.page(params[:page])
|
||||
.per(ITEMS_PER_PAGE)
|
||||
end
|
||||
|
|
|
@ -179,7 +179,6 @@ module Users
|
|||
errors = submit_dossier_and_compute_errors
|
||||
|
||||
if errors.blank?
|
||||
RoutingEngine.compute(@dossier)
|
||||
@dossier.passer_en_construction!
|
||||
@dossier.process_declarative!
|
||||
NotificationMailer.send_en_construction_notification(@dossier).deliver_later
|
||||
|
@ -538,9 +537,7 @@ module Users
|
|||
@dossier.assign_to_groupe_instructeur(defaut_groupe_instructeur)
|
||||
end
|
||||
|
||||
if !@dossier.procedure.feature_enabled?(:routing_rules) && @dossier.groupe_instructeur.nil?
|
||||
errors += format_errors(errors: ["Le champ « #{@dossier.procedure.routing_criteria_name} » doit être rempli"])
|
||||
end
|
||||
RoutingEngine.compute(@dossier)
|
||||
|
||||
errors
|
||||
end
|
||||
|
|
|
@ -26,6 +26,16 @@ module Mutations
|
|||
.build(label: groupe_instructeur.label, closed: groupe_instructeur.closed, instructeurs: [current_administrateur.instructeur].compact)
|
||||
|
||||
if groupe_instructeur.save
|
||||
|
||||
# ugly hack to keep retro compatibility
|
||||
# do not judge
|
||||
if !ENV['OLD_GROUPE_INSTRUCTEURS_CREATE_API_PROCEDURE_ID'].nil? && demarche_number.in?(ENV['OLD_GROUPE_INSTRUCTEURS_CREATE_API_PROCEDURE_ID']&.split(',')&.map(&:to_i))
|
||||
stable_id = procedure.groupe_instructeurs.first.routing_rule.left.stable_id
|
||||
tdc = procedure.published_revision.types_de_champ.find_by(stable_id: stable_ids)
|
||||
tdc.update(options: tdc.options['drop_down_options'].push(groupe_instructeur.label))
|
||||
groupe_instructeur.update(routing_rule: ds_eq(champ_value(stable_id), constant(groupe_instruteur.label)))
|
||||
end
|
||||
|
||||
result = { groupe_instructeur: }
|
||||
|
||||
if emails.present? || ids.present?
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
export class RadioEnabledSubmitController extends Controller {
|
||||
static targets = ['submit'];
|
||||
declare readonly submitTarget: HTMLButtonElement;
|
||||
|
||||
click() {
|
||||
if (
|
||||
this.element.querySelectorAll('input[type="radio"]:checked').length > 0
|
||||
) {
|
||||
this.submitTarget.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -86,10 +86,22 @@ class GroupeInstructeur < ApplicationRecord
|
|||
dossiers.empty? && (procedure.groupe_instructeurs.active.many? || (procedure.groupe_instructeurs.active.one? && closed))
|
||||
end
|
||||
|
||||
def routing_to_configure?
|
||||
rule = routing_rule
|
||||
return true if !(rule.is_a?(Logic::Eq) && rule.left.is_a?(Logic::ChampValue) && rule.right.is_a?(Logic::Constant))
|
||||
!routing_rule_matches_tdc?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def routing_rule_matches_tdc?
|
||||
routing_tdc = procedure.active_revision.types_de_champ.find_by(stable_id: routing_rule.left.stable_id)
|
||||
routing_rule.right.value.in?(routing_tdc.options['drop_down_options'])
|
||||
end
|
||||
|
||||
def toggle_routing
|
||||
procedure.update!(routing_enabled: procedure.groupe_instructeurs.active.many?)
|
||||
procedure.update!(instructeurs_self_management_enabled: true) if procedure.routing_enabled?
|
||||
end
|
||||
|
||||
serialize :routing_rule, LogicSerializer
|
||||
|
|
|
@ -728,16 +728,16 @@ class Procedure < ApplicationRecord
|
|||
revisions.size - 2
|
||||
end
|
||||
|
||||
def instructeurs_self_management?
|
||||
routing_enabled? || instructeurs_self_management_enabled?
|
||||
end
|
||||
|
||||
def defaut_groupe_instructeur_for_new_dossier
|
||||
if !routing_enabled? || feature_enabled?(:procedure_routage_api)
|
||||
defaut_groupe_instructeur
|
||||
end
|
||||
end
|
||||
|
||||
def groupe_instructeurs_but_defaut
|
||||
groupe_instructeurs - [defaut_groupe_instructeur]
|
||||
end
|
||||
|
||||
def can_be_deleted_by_administrateur?
|
||||
brouillon? || dossiers.state_en_instruction.empty?
|
||||
end
|
||||
|
|
|
@ -236,6 +236,10 @@ class ProcedureRevision < ApplicationRecord
|
|||
[coordinate, coordinate&.type_de_champ]
|
||||
end
|
||||
|
||||
def routable_types_de_champ
|
||||
types_de_champ_public.filter { |tdc| [:drop_down_list].include?(tdc.type_champ.to_sym) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_estimated_fill_duration
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
class ProcedureRevisionChange
|
||||
attr_reader :type_de_champ
|
||||
def initialize(type_de_champ)
|
||||
@type_de_champ = type_de_champ
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ module RoutingEngine
|
|||
def self.compute(dossier)
|
||||
return if !dossier.procedure.feature_enabled?(:routing_rules)
|
||||
|
||||
matching_groupe = dossier.procedure.groupe_instructeurs.active.find do |gi|
|
||||
matching_groupe = dossier.procedure.groupe_instructeurs.active.reject(&:routing_to_configure?).find do |gi|
|
||||
gi.routing_rule&.compute(dossier.champs)
|
||||
end
|
||||
matching_groupe ||= dossier.procedure.defaut_groupe_instructeur
|
||||
|
|
|
@ -131,6 +131,7 @@ class TypeDeChamp < ApplicationRecord
|
|||
has_one :procedure, through: :revision
|
||||
|
||||
delegate :estimated_fill_duration, :estimated_read_duration, :tags_for_template, :libelle_for_export, to: :dynamic_type
|
||||
delegate :used_by_routing_rules?, to: :revision_type_de_champ
|
||||
|
||||
class WithIndifferentAccess
|
||||
def self.load(options)
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
- if groupes_instructeurs.many? && !procedure.feature_enabled?(:routing_rules)
|
||||
.card
|
||||
= form_for procedure,
|
||||
url: { action: :update_routing_criteria_name },
|
||||
html: { class: 'form' } do |f|
|
||||
|
||||
= f.label :routing_criteria_name do
|
||||
= t('.routing.title')
|
||||
%p.notice
|
||||
= f.text_field :routing_criteria_name, required: true
|
||||
= f.submit t('.button.rename'), class: 'button primary send'
|
||||
|
||||
.card
|
||||
%h2.card-title= t('.group_management.title')
|
||||
|
||||
= form_for :groupe_instructeur, html: { class: 'form' } do |f|
|
||||
= f.label :label do
|
||||
= t('.add_a_group.title')
|
||||
- if groupes_instructeurs.many?
|
||||
%p.notice
|
||||
= t('.add_a_group.notice', routing_criteria_name: procedure.routing_criteria_name)
|
||||
= f.text_field :label, required: true
|
||||
= f.submit t('.button.add_group'), class: "button primary send"
|
||||
|
||||
- csv_max_size = Administrateurs::GroupeInstructeursController::CSV_MAX_SIZE
|
||||
- if procedure.publiee_or_close?
|
||||
= form_tag import_admin_procedure_groupe_instructeurs_path(procedure), method: :post, multipart: true, class: "mt-4 form" do
|
||||
= label_tag t('.csv_import.title')
|
||||
%p.notice
|
||||
= t('.csv_import.notice_1_html',
|
||||
instructeurs_link: link_to(t('.csv_import.link_text'), t('.csv_import.instructeurs_file_path')),
|
||||
groupes_link: link_to(t('.csv_import.link_text'), t('.csv_import.groupes_file_path')))
|
||||
%p.notice
|
||||
= t('.csv_import.notice_2', csv_max_size: number_to_human_size(csv_max_size))
|
||||
= file_field_tag :csv_file, required: true, accept: 'text/csv', size: "1"
|
||||
= submit_tag t('.csv_import.import_file'), class: 'button primary send', data: { disable_with: "Envoi...", confirm: t('.csv_import.import_file_alert') }
|
||||
- else
|
||||
%p.mt-4.form.font-weight-bold.mb-2.text-lg
|
||||
= t('.csv_import.title')
|
||||
%p.notice
|
||||
= t('.csv_import.import_file_procedure_not_published')
|
||||
- if procedure.groupe_instructeurs.many?
|
||||
%table.table.mt-2
|
||||
%thead
|
||||
%tr
|
||||
// i18n-tasks-use t('.existing_groupe')
|
||||
%th{ colspan: 2 }= t(".existing_groupe", count: groupes_instructeurs.total_count)
|
||||
%th.actions
|
||||
= link_to "Exporter au format CSV", export_groupe_instructeurs_admin_procedure_groupe_instructeurs_path(procedure, format: :csv)
|
||||
%tbody
|
||||
- groupes_instructeurs.each do |group|
|
||||
%tr
|
||||
%td= group.label
|
||||
%td.setup= link_to t('.set_up'), admin_procedure_groupe_instructeur_path(procedure, group)
|
||||
- if group.can_delete?
|
||||
%td.actions
|
||||
= link_to admin_procedure_groupe_instructeur_path(procedure, group), { method: :delete, class: 'button', data: { confirm: t('.group_management.delete_confirmation', group_name: group.label) }} do
|
||||
%span.icon.delete
|
||||
= t('.group_management.delete')
|
||||
- else
|
||||
%td.actions
|
||||
= link_to reaffecter_dossiers_admin_procedure_groupe_instructeur_path(procedure, group), class: 'button', title: t('.group_management.move_files_confirmation') do
|
||||
%span.icon.follow
|
||||
= t('.group_management.move_files', count: group.dossiers.visible_by_administration.size)
|
||||
|
||||
|
||||
= paginate groupes_instructeurs, views_prefix: 'shared'
|
|
@ -0,0 +1,28 @@
|
|||
- key = @procedure.groupe_instructeurs.one? ? 'instructeurs' : 'groupes'
|
||||
%section.fr-accordion.fr-mb-3w
|
||||
%h3.fr-accordion__title
|
||||
%button.fr-accordion__btn{ "aria-controls" => "accordion-106", "aria-expanded" => "false" }
|
||||
= t(".csv_import.#{key}.title")
|
||||
.fr-collapse#accordion-106
|
||||
- csv_max_size = Administrateurs::GroupeInstructeursController::CSV_MAX_SIZE
|
||||
- if procedure.publiee_or_close?
|
||||
%p.notice
|
||||
= t(".csv_import.#{key}.notice_1_html", csv_max_size: number_to_human_size(csv_max_size))
|
||||
%p.notice
|
||||
= t(".csv_import.#{key}.notice_2")
|
||||
= form_tag import_admin_procedure_groupe_instructeurs_path(@procedure), method: :post, multipart: true, class: "mt-4 form flex justify-between align-center" do
|
||||
= file_field_tag :csv_file, required: true, accept: 'text/csv', size: "1"
|
||||
= submit_tag t('.csv_import.import_file'), class: 'fr-btn fr-btn--secondary', data: { disable_with: "Envoi...", confirm: t('.csv_import.import_file_alert') }
|
||||
- else
|
||||
%p.mt-4.form.font-weight-bold.mb-2.text-lg
|
||||
= t(".csv_import.#{key}.title")
|
||||
%p.notice
|
||||
= t('.csv_import.import_file_procedure_not_published')
|
||||
- if groupe_instructeurs.many?
|
||||
.flex.justify-between.align-center.mt-4
|
||||
%div
|
||||
= t(".existing_groupe", count: groupe_instructeurs.total_count)
|
||||
= button_to "Exporter au format CSV",
|
||||
export_groupe_instructeurs_admin_procedure_groupe_instructeurs_path(@procedure, format: :csv),
|
||||
method: :get,
|
||||
class: 'fr-btn fr-btn--secondary'
|
|
@ -1,14 +0,0 @@
|
|||
.card
|
||||
%h2.card-title L’autogestion des instructeurs
|
||||
%p.notice= t('.self_managment_notice_html')
|
||||
|
||||
= form_for procedure,
|
||||
method: :patch,
|
||||
url: update_instructeurs_self_management_enabled_admin_procedure_groupe_instructeurs_path(procedure),
|
||||
data: { controller: 'autosubmit', turbo: 'true' },
|
||||
html: { class: 'form procedure-form__column--form no-background' } do |f|
|
||||
%label.toggle-switch
|
||||
= f.check_box :instructeurs_self_management_enabled, class: 'toggle-switch-checkbox'
|
||||
%span.toggle-switch-control.round
|
||||
%span.toggle-switch-label.on
|
||||
%span.toggle-switch-label.off
|
|
@ -1,4 +0,0 @@
|
|||
.card
|
||||
%h2.card-title= t('.title')
|
||||
|
||||
%p.notice= t('.notice_html')
|
|
@ -0,0 +1,8 @@
|
|||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [[t('.procedures'), admin_procedures_path],
|
||||
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
|
||||
[(@procedure.groupe_instructeurs.many? ? 'Groupes' : 'Instructeurs'), admin_procedure_groupe_instructeurs_path(@procedure)],
|
||||
['Ajout']] }
|
||||
|
||||
= render Procedure::InstructeursMenuComponent.new(procedure: @procedure) do
|
||||
= render Procedure::GroupesAjoutComponent.new(procedure: @procedure, groupe_instructeurs: @groupes_instructeurs)
|
|
@ -1,31 +1,14 @@
|
|||
- if @procedure.routing_enabled?
|
||||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [[t('.procedures'), admin_procedures_path],
|
||||
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
|
||||
[t('.instructors_group')]] }
|
||||
- else
|
||||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [[t('.procedures'), admin_procedures_path],
|
||||
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
|
||||
['Instructeurs']] }
|
||||
|
||||
.container.groupe-instructeur
|
||||
%h1 Gérer les instructeurs et les options d'instruction de « #{@procedure.libelle} »
|
||||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [[t('.procedures'), admin_procedures_path],
|
||||
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
|
||||
[@procedure.groupe_instructeurs.many? ? 'Groupes' : 'Instructeurs']] }
|
||||
|
||||
= render Procedure::InstructeursMenuComponent.new(procedure: @procedure) do
|
||||
- if @procedure.groupe_instructeurs.one?
|
||||
= render partial: 'administrateurs/groupe_instructeurs/instructeurs',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeur: @procedure.defaut_groupe_instructeur,
|
||||
instructeurs: @instructeurs,
|
||||
available_instructeur_emails: @available_instructeur_emails,
|
||||
disabled_as_super_admin: administrateur_as_manager? }
|
||||
- if !@procedure.routing_enabled?
|
||||
= render partial: 'administrateurs/groupe_instructeurs/instructeurs_self_management', locals: { procedure: @procedure }
|
||||
|
||||
= render partial: 'administrateurs/groupe_instructeurs/routing', locals: { procedure: @procedure }
|
||||
|
||||
= render partial: 'administrateurs/groupe_instructeurs/edit', locals: { procedure: @procedure, groupes_instructeurs: @groupes_instructeurs }
|
||||
|
||||
- if @procedure.routing_enabled? && @procedure.feature_enabled?(:routing_rules)
|
||||
= render(Procedure::RoutingRulesComponent.new(revision: @procedure.active_revision,
|
||||
groupe_instructeurs: @procedure.groupe_instructeurs))
|
||||
= render Procedure::InstructeursManagementComponent.new(procedure: @procedure,
|
||||
groupe_instructeur: @procedure.groupe_instructeurs.first,
|
||||
instructeurs: @instructeurs,
|
||||
available_instructeur_emails: @available_instructeur_emails,
|
||||
disabled_as_super_admin: administrateur_as_manager?)
|
||||
- else
|
||||
= render Procedure::GroupesManagementComponent.new(procedure: @procedure, groupe_instructeurs: @groupes_instructeurs, query: params[:q])
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [[t('.procedures'), admin_procedures_path],
|
||||
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
|
||||
[(@procedure.groupe_instructeurs.many? ? 'Groupes' : 'Instructeurs'), admin_procedure_groupe_instructeurs_path(@procedure)],
|
||||
['Options']] }
|
||||
|
||||
= render Procedure::InstructeursMenuComponent.new(procedure: @procedure) do
|
||||
= render Procedure::InstructeursOptionsComponent.new(procedure: @procedure, state: params[:state])
|
|
@ -4,8 +4,9 @@
|
|||
['Groupes d’instructeurs', admin_procedure_groupe_instructeurs_path(@procedure)],
|
||||
[@groupe_instructeur.label]] }
|
||||
|
||||
.container.groupe-instructeur
|
||||
= render partial: 'administrateurs/groups_header'
|
||||
= render Procedure::InstructeursMenuComponent.new(procedure: @procedure) do
|
||||
= render Procedure::OneGroupeManagementComponent.new(revision: @procedure.active_revision, groupe_instructeur: @groupe_instructeur)
|
||||
|
||||
= render partial: 'administrateurs/groupe_instructeurs/instructeurs',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeur: @groupe_instructeur,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [[t('.procedures'), admin_procedures_path],
|
||||
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
|
||||
['Groupes', admin_procedure_groupe_instructeurs_path(@procedure)],
|
||||
['Routage à partir d’un champ']] }
|
||||
|
||||
= render Procedure::InstructeursMenuComponent.new(procedure: @procedure) do
|
||||
- content_for(:title, 'Routage')
|
||||
%h1 Routage à partir d’un champ
|
||||
= form_for :create_simple_routing,
|
||||
method: :post,
|
||||
data: { controller: 'radio-enabled-submit' },
|
||||
url: create_simple_routing_admin_procedure_groupe_instructeurs_path(@procedure) do |f|
|
||||
|
||||
%div{ data: { 'action': "click->radio-enabled-submit#click" } }
|
||||
.notice
|
||||
Sélectionner le champ à partir duquel créer des groupes d'instructeurs
|
||||
- buttons_content = @procedure.active_revision.routable_types_de_champ.map { |tdc| { label: tdc.libelle, value: tdc.stable_id } }
|
||||
= render Dsfr::RadioButtonListComponent.new(form: f,
|
||||
target: :stable_id,
|
||||
buttons: buttons_content)
|
||||
|
||||
%ul.fr-btns-group.fr-btns-group--inline-sm
|
||||
%li
|
||||
= link_to 'Retour', options_admin_procedure_groupe_instructeurs_path(@procedure, state: :choix), class: 'fr-btn fr-btn--secondary'
|
||||
%li
|
||||
%button.fr-btn{ disabled: true, data: { 'radio-enabled-submit-target': 'submit' } } Créer les groupes
|
|
@ -1,2 +1,3 @@
|
|||
= turbo_stream.replace 'routing-rules', render(Procedure::RoutingRulesComponent.new(revision: @procedure.active_revision,
|
||||
groupe_instructeurs: @procedure.groupe_instructeurs))
|
||||
= turbo_stream.replace dom_id(@groupe_instructeur, :routing),
|
||||
render(Procedure::OneGroupeManagementComponent.new(revision: @procedure.active_revision,
|
||||
groupe_instructeur: @groupe_instructeur))
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
|
||||
= link_to t('instructeurs.dossiers.header.banner.statistics'), stats_instructeur_procedure_path(procedure), class: 'header-link'
|
||||
|
||||
- if procedure.instructeurs_self_management?
|
||||
- if procedure.instructeurs_self_management_enabled?
|
||||
|
|
||||
- if can_manage_groupe_instructeurs?(procedure)
|
||||
= link_to t('instructeurs.dossiers.header.banner.instructeurs'), admin_procedure_groupe_instructeurs_path(procedure), class: 'header-link'
|
||||
|
|
|
@ -8,7 +8,6 @@ en:
|
|||
groupe_instructeurs:
|
||||
index:
|
||||
procedures: Procedures
|
||||
instructors_group: Group of instructors
|
||||
add_instructeur:
|
||||
wrong_address:
|
||||
one: "%{emails} is not a valid email address"
|
||||
|
@ -24,48 +23,19 @@ en:
|
|||
assigned_instructeur:
|
||||
one: "%{count} instructor is assigned"
|
||||
other: "%{count} instructors are assigned"
|
||||
edit:
|
||||
routing:
|
||||
title: Label of the groups list
|
||||
group_management:
|
||||
title: Group management
|
||||
delete: delete the group
|
||||
delete_confirmation: Are you sure you want to delete the group "%{group_name}"
|
||||
move_files:
|
||||
zero: move draft files
|
||||
one: move one file
|
||||
other: move the %{count} files
|
||||
move_files_confirmation: Reassign folders to another group so you can delete it
|
||||
add_a_group:
|
||||
title: Add a group
|
||||
notice: This group will be a choice from the list "%{routing_criteria_name}"
|
||||
import_export:
|
||||
csv_import:
|
||||
title: CSV Import
|
||||
notice_1_html: The csv file must have 1 column with instructors emails(%{instructeurs_link}). For routed procedures, the csv file must have 2 columns (Group, Email) and be separated by commas (%{groupes_link}). The import does not overwrite existing groups and instructors.
|
||||
notice_2: The size of the file must be less than %{csv_max_size}.
|
||||
import_file: Import file
|
||||
import_file_procedure_not_published: The import of instructors by CSV file is available once the process has been published
|
||||
import_file_alert: Instructors added to the procedure will receive an email. Are you sure you want to continue ?"
|
||||
link_text: file example
|
||||
instructeurs_file_path: /csv/import-instructeurs-test.csv
|
||||
groupes_file_path: /csv/en/import-groupe-test.csv
|
||||
set_up: set up
|
||||
button:
|
||||
add_group: Add group
|
||||
rename: Rename
|
||||
import_file: Importer le fichier
|
||||
import_file_alert: Tous les instructeurs ajoutés à la procédure vont être notifiés par email. Voulez-vous continuer ?
|
||||
import_file_procedure_not_published: L’import par fichier CSV est disponible une fois la démarche publiée
|
||||
groupes:
|
||||
title: Import / Export en masse
|
||||
notice_1_html: Pour l'import, votre fichier csv doit comporter 2 colonnes (Groupe, Email) et être séparé par des virgules (<a href=/csv/fr/import-groupe-test.csv>exemple de fichier</a>). Le poids du fichier doit être inférieur %{csv_max_size}.
|
||||
notice_2: L’import n’écrase pas les groupes existants. Il permet uniquement d'en ajouter. Pour supprimer un groupe, allez dans la page dédiée et cliquez sur le bouton « Supprimer ».
|
||||
instructeurs:
|
||||
title: Import en masse
|
||||
notice_1_html: Pour l'import, le fichier csv doit comporter 1 seule colonne (Email) avec une adresse email d'instructeur par ligne (<a href=/csv/import-instructeurs-test.csv>exemple de fichier</a>). Le poids du fichier doit être inférieur %{csv_max_size}.
|
||||
notice_2: L’import n’écrase pas les instructeurs existants. Il permet uniquement d'en ajouter. Pour supprimer un instructeur, cliquez sur le bouton « Retirer ».
|
||||
existing_groupe:
|
||||
one: "%{count} group exist"
|
||||
other: "%{count} groups exist"
|
||||
routing:
|
||||
title: Routing
|
||||
notice_html: |
|
||||
Routing is a feature for procedures requiring the sharing of instructions between different groups according to a specific criterion (territory, theme or other).
|
||||
<br><br>
|
||||
This feature makes it possible to route the files to each group, and to no longer need to filter its files among a large quantity of requests. It is therefore particularly suitable for national approaches instructed locally.
|
||||
<br><br>
|
||||
Instructors only see the files that concern them, and therefore do not have access to data outside their scope.
|
||||
<br><br>
|
||||
Routing is activated once there are at least two active instructors groups
|
||||
instructeurs_self_management:
|
||||
self_managment_notice_html: |
|
||||
Instructor Self-Management allows instructors to self-manage the list of Gait Instructors.
|
||||
one: "%{count} groupe existe"
|
||||
other: "%{count} groupes existent"
|
||||
|
|
|
@ -30,6 +30,34 @@ fr:
|
|||
assigned_instructeur:
|
||||
one: "%{count} instructeur est affecté"
|
||||
other: "%{count} instructeurs sont affectés"
|
||||
import_export:
|
||||
csv_import:
|
||||
import_file: Importer le fichier
|
||||
import_file_alert: Tous les instructeurs ajoutés à la procédure vont être notifiés par email. Voulez-vous continuer ?
|
||||
import_file_procedure_not_published: L’import par fichier CSV est disponible une fois la démarche publiée
|
||||
groupes:
|
||||
title: Import / Export en masse
|
||||
notice_1_html: Pour l'import, votre fichier csv doit comporter 2 colonnes (Groupe, Email) et être séparé par des virgules (<a href=/csv/fr/import-groupe-test.csv>exemple de fichier</a>). Le poids du fichier doit être inférieur %{csv_max_size}.
|
||||
notice_2: L’import n’écrase pas les groupes existants. Il permet uniquement d'en ajouter. Pour supprimer un groupe, allez dans la page dédiée et cliquez sur le bouton « Supprimer ».
|
||||
instructeurs:
|
||||
title: Import en masse
|
||||
notice_1_html: Pour l'import, le fichier csv doit comporter 1 seule colonne (Email) avec une adresse email d'instructeur par ligne (<a href=/csv/import-instructeurs-test.csv>exemple de fichier</a>). Le poids du fichier doit être inférieur %{csv_max_size}.
|
||||
notice_2: L’import n’écrase pas les instructeurs existants. Il permet uniquement d'en ajouter. Pour supprimer un instructeur, cliquez sur le bouton « Retirer ».
|
||||
existing_groupe:
|
||||
one: "%{count} groupe existe"
|
||||
other: "%{count} groupes existent"
|
||||
groupe:
|
||||
one: "%{count} groupe"
|
||||
other: "%{count} groupes"
|
||||
found:
|
||||
one: "trouvé"
|
||||
other: "trouvés"
|
||||
ajout:
|
||||
procedures: Démarches
|
||||
options:
|
||||
procedures: Démarches
|
||||
simple_routing:
|
||||
procedures: Démarches
|
||||
edit:
|
||||
routing:
|
||||
title: Libellé de la liste de groupes
|
||||
|
@ -46,14 +74,6 @@ fr:
|
|||
title: Ajouter un nom de groupe
|
||||
notice: Ce groupe sera un choix de la liste "%{routing_criteria_name}"
|
||||
csv_import:
|
||||
title: Importer par CSV
|
||||
notice_1_html: |
|
||||
Pour une procédure standard, le fichier csv doit comporter 1 seule colonne (Email) avec une adresse email d'instructeur par ligne (%{instructeurs_link}).
|
||||
Pour une procédure routée, le fichier doit comporter 2 colonnes (Groupe, Email) et être séparé par des virgules (%{groupes_link}).
|
||||
notice_2: L’import n’écrase pas les instructeurs existants. Il permet uniquement d'en ajouter. Pour supprimer un instructeur, cliquez sur le bouton « retirer ». Le poids du fichier doit être inférieur %{csv_max_size}.
|
||||
import_file: Importer le fichier
|
||||
import_file_procedure_not_published: L’import d’instructeurs par fichier CSV est disponible une fois la démarche publiée
|
||||
import_file_alert: Tous les instructeurs ajoutés à la procédure vont être informés par email. Voulez-vous continuer ?
|
||||
link_text: exemple de fichier
|
||||
instructeurs_file_path: /csv/import-instructeurs-test.csv
|
||||
groupes_file_path: /csv/fr/import-groupe-test.csv
|
||||
|
@ -72,6 +92,3 @@ fr:
|
|||
button:
|
||||
self_managment_toggle: Activer l’autogestion des instructeurs
|
||||
add_group: Ajouter le groupe
|
||||
instructeurs_self_management:
|
||||
self_managment_notice_html: |
|
||||
L’autogestion des instructeurs permet aux instructeurs de gérer eux-mêmes la liste des instructeurs de la démarche.
|
||||
|
|
|
@ -522,6 +522,8 @@ Rails.application.routes.draw do
|
|||
resources :mail_templates, only: [:edit, :update, :show]
|
||||
|
||||
resources :groupe_instructeurs, only: [:index, :show, :create, :update, :destroy] do
|
||||
patch 'update_state' => 'groupe_instructeurs#update_state'
|
||||
|
||||
member do
|
||||
post 'add_instructeur'
|
||||
delete 'remove_instructeur'
|
||||
|
@ -530,6 +532,13 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
collection do
|
||||
get 'options'
|
||||
get 'ajout'
|
||||
post 'ajout' => 'groupe_instructeurs#create'
|
||||
patch 'wizard'
|
||||
get 'simple_routing'
|
||||
post 'create_simple_routing'
|
||||
delete 'destroy_all_groups_but_defaut'
|
||||
patch 'update_routing_criteria_name'
|
||||
patch 'update_instructeurs_self_management_enabled'
|
||||
post 'import'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
describe Procedure::RoutingRulesComponent, type: :component do
|
||||
describe Procedure::OneGroupeManagementComponent, type: :component do
|
||||
include Logic
|
||||
|
||||
describe 'render' do
|
||||
|
@ -9,16 +9,7 @@ describe Procedure::RoutingRulesComponent, type: :component do
|
|||
|
||||
subject do
|
||||
render_inline(described_class.new(revision: procedure.active_revision,
|
||||
groupe_instructeurs: procedure.groupe_instructeurs))
|
||||
end
|
||||
|
||||
context 'when there are no types de champ that can be routed' do
|
||||
before do
|
||||
procedure.publish_revision!
|
||||
procedure.reload
|
||||
subject
|
||||
end
|
||||
it { expect(page).to have_text('Ajoutez ce champ dans la page') }
|
||||
groupe_instructeur: procedure.defaut_groupe_instructeur))
|
||||
end
|
||||
|
||||
context 'when there are types de champ that can be routed' do
|
||||
|
@ -32,7 +23,7 @@ describe Procedure::RoutingRulesComponent, type: :component do
|
|||
procedure.reload
|
||||
subject
|
||||
end
|
||||
it { expect(page).to have_text('Router vers') }
|
||||
it { expect(page).to have_text('à configurer') }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +1,10 @@
|
|||
describe Administrateurs::GroupeInstructeursController, type: :controller do
|
||||
render_views
|
||||
include Logic
|
||||
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, administrateurs: [admin]) }
|
||||
|
||||
let!(:gi_1_1) { procedure.defaut_groupe_instructeur }
|
||||
let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||
|
||||
|
@ -13,13 +15,25 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
|
||||
describe '#index' do
|
||||
context 'of a procedure I own' do
|
||||
before { get :index, params: { procedure_id: procedure.id } }
|
||||
before { get :index, params: }
|
||||
|
||||
context 'when a procedure has multiple groups' do
|
||||
it { expect(response).to have_http_status(:ok) }
|
||||
it { expect(response.body).to include(gi_1_1.label) }
|
||||
it { expect(response.body).to include(gi_1_2.label) }
|
||||
it { expect(response.body).not_to include(gi_2_2.label) }
|
||||
let(:params) { { procedure_id: procedure.id } }
|
||||
|
||||
it do
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include(gi_1_1.label)
|
||||
expect(response.body).to include(gi_1_2.label)
|
||||
expect(response.body).not_to include(gi_2_2.label)
|
||||
end
|
||||
|
||||
context 'when there is a search' do
|
||||
let(:params) { { procedure_id: procedure.id, q: '2' } }
|
||||
|
||||
it do
|
||||
expect(assigns(:groupes_instructeurs)).to match_array([gi_1_2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -46,6 +60,45 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
expect(assigns(:available_instructeur_emails)).to match_array(['instructeur_3@ministere-a.gouv.fr', 'instructeur_4@ministere-b.gouv.fr'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'group without routing rule' do
|
||||
before { get :show, params: { procedure_id: procedure.id, id: gi_1_1.id } }
|
||||
|
||||
it do
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include('à configurer')
|
||||
end
|
||||
end
|
||||
|
||||
context 'group with routing rule matching tdc' do
|
||||
let!(:drop_down_tdc) { create(:type_de_champ_drop_down_list, procedure: procedure, drop_down_options: options) }
|
||||
let(:options) { procedure.groupe_instructeurs.pluck(:label) }
|
||||
|
||||
before do
|
||||
gi_1_1.update(routing_rule: ds_eq(champ_value(drop_down_tdc.stable_id), constant(gi_1_1.label)))
|
||||
get :show, params: { procedure_id: procedure.id, id: gi_1_1.id }
|
||||
end
|
||||
|
||||
it do
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).not_to include('à configurer')
|
||||
end
|
||||
end
|
||||
|
||||
context 'group with routing rule not matching tdc' do
|
||||
let!(:drop_down_tdc) { create(:type_de_champ_drop_down_list, procedure: procedure, drop_down_options: options) }
|
||||
let(:options) { ['parmesan', 'brie', 'morbier'] }
|
||||
|
||||
before do
|
||||
gi_1_1.update(routing_rule: ds_eq(champ_value(drop_down_tdc.stable_id), constant(gi_1_1.label)))
|
||||
get :show, params: { procedure_id: procedure.id, id: gi_1_1.id }
|
||||
end
|
||||
|
||||
it do
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include('à configurer')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create' do
|
||||
|
@ -181,7 +234,6 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
|
||||
describe '#update' do
|
||||
let(:new_name) { 'nouveau nom du groupe' }
|
||||
let(:closed_value) { '0' }
|
||||
let!(:procedure_non_routee) { create(:procedure, :published, :for_individual, administrateurs: [admin]) }
|
||||
let!(:gi_1_1) { procedure_non_routee.defaut_groupe_instructeur }
|
||||
|
||||
|
@ -190,7 +242,7 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
params: {
|
||||
procedure_id: procedure_non_routee.id,
|
||||
id: gi_1_1.id,
|
||||
groupe_instructeur: { label: new_name, closed: closed_value }
|
||||
groupe_instructeur: { label: new_name }
|
||||
}
|
||||
gi_1_1.reload
|
||||
end
|
||||
|
@ -202,18 +254,6 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
expect(flash.notice).to be_present
|
||||
end
|
||||
|
||||
context 'when we try do disable the default groupe instructeur' do
|
||||
let(:closed_value) { '1' }
|
||||
let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||
|
||||
it do
|
||||
expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure_non_routee, gi_1_1)
|
||||
expect(gi_1_1.label).not_to eq(new_name)
|
||||
expect(gi_1_1.closed).to eq(false)
|
||||
expect(flash.alert).to eq('Il est impossible de désactiver le groupe d’instructeurs par défaut.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the name is already taken' do
|
||||
let!(:gi_1_2) { procedure_non_routee.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||
let(:new_name) { gi_1_2.label }
|
||||
|
@ -225,6 +265,45 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#update_state' do
|
||||
let(:closed_value) { '0' }
|
||||
let!(:procedure_non_routee) { create(:procedure, :published, :for_individual, administrateurs: [admin]) }
|
||||
let!(:gi_1_1) { procedure_non_routee.defaut_groupe_instructeur }
|
||||
let!(:gi_1_2) { procedure_non_routee.groupe_instructeurs.create(label: 'groupe instructeur 2') }
|
||||
|
||||
before do
|
||||
patch :update_state,
|
||||
params: {
|
||||
procedure_id: procedure_non_routee.id,
|
||||
groupe_instructeur_id: group.id,
|
||||
closed: closed_value
|
||||
}
|
||||
group.reload
|
||||
end
|
||||
|
||||
context 'when we try do disable the default groupe instructeur' do
|
||||
let(:closed_value) { '1' }
|
||||
let(:group) { gi_1_1 }
|
||||
|
||||
it do
|
||||
expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure_non_routee, gi_1_1)
|
||||
expect(gi_1_1.closed).to eq(false)
|
||||
expect(flash.alert).to eq('Il est impossible de désactiver le groupe d’instructeurs par défaut.')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when we try do disable the second groupe instructeur' do
|
||||
let(:closed_value) { '1' }
|
||||
let(:group) { gi_1_2 }
|
||||
|
||||
it do
|
||||
expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure_non_routee, gi_1_2)
|
||||
expect(gi_1_2.closed).to eq(true)
|
||||
expect(flash.notice).to eq('Le groupe groupe instructeur 2 est désactivé.')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#add_instructeur_procedure_non_routee' do
|
||||
# faire la meme chose sur une procedure non routee
|
||||
let(:procedure_non_routee) { create :procedure }
|
||||
|
@ -624,4 +703,26 @@ describe Administrateurs::GroupeInstructeursController, type: :controller do
|
|||
|
||||
it { expect(procedure.reload.routing_criteria_name).to eq('new name !') }
|
||||
end
|
||||
|
||||
describe '#create_simple_routing' do
|
||||
let!(:procedure3) do
|
||||
create(:procedure,
|
||||
types_de_champ_public: [
|
||||
{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] },
|
||||
{ type: :text, libelle: 'Un champ texte' }
|
||||
],
|
||||
administrateurs: [admin])
|
||||
end
|
||||
|
||||
let!(:drop_down_tdc) { procedure3.draft_revision.types_de_champ.first }
|
||||
|
||||
before { post :create_simple_routing, params: { procedure_id: procedure3.id, create_simple_routing: { stable_id: drop_down_tdc.stable_id } } }
|
||||
|
||||
it do
|
||||
expect(response).to redirect_to(admin_procedure_groupe_instructeurs_path(procedure3))
|
||||
expect(flash.notice).to eq 'Les groupes instructeurs ont été ajoutés'
|
||||
expect(procedure3.groupe_instructeurs.pluck(:label)).to match_array(['Paris', 'Lyon', 'Marseille'])
|
||||
expect(procedure3.reload.defaut_groupe_instructeur.routing_rule).to eq(ds_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon')))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -412,16 +412,6 @@ describe Users::DossiersController, type: :controller do
|
|||
it { expect(flash.alert).to eq(["Le champ « l » doit être rempli, #{anchor_to_first_champ}"]) }
|
||||
end
|
||||
|
||||
context 'when a dossier is invalid' do
|
||||
before do
|
||||
allow_any_instance_of(Dossier).to receive(:groupe_instructeur).and_return(double(nil?: true))
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(response).to render_template(:brouillon) }
|
||||
it { expect(flash.alert).to eq(["Le champ « Votre ville » doit être rempli"]) }
|
||||
end
|
||||
|
||||
context 'when dossier has no champ' do
|
||||
let(:submit_payload) { { id: dossier.id } }
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@ describe RoutingEngine, type: :model do
|
|||
|
||||
describe '.compute' do
|
||||
let(:procedure) do
|
||||
create(:procedure).tap do |p|
|
||||
p.groupe_instructeurs.create(label: 'a second group')
|
||||
p.groupe_instructeurs.create(label: 'a third group')
|
||||
end
|
||||
create(:procedure,
|
||||
types_de_champ_public: [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }]).tap do |p|
|
||||
p.groupe_instructeurs.create(label: 'a second group')
|
||||
p.groupe_instructeurs.create(label: 'a third group')
|
||||
end
|
||||
end
|
||||
|
||||
let(:drop_down_tdc) { procedure.draft_revision.types_de_champ.first }
|
||||
|
||||
let(:dossier) { create(:dossier, procedure:) }
|
||||
let(:defaut_groupe) { procedure.defaut_groupe_instructeur }
|
||||
let(:gi_2) { procedure.groupe_instructeurs.find_by(label: 'a second group') }
|
||||
|
@ -34,13 +37,26 @@ describe RoutingEngine, type: :model do
|
|||
it { is_expected.to eq(defaut_groupe) }
|
||||
end
|
||||
|
||||
context 'with a matching rules' do
|
||||
before { gi_2.update(routing_rule: constant(true)) }
|
||||
context 'with rules not configured yet' do
|
||||
before do
|
||||
procedure.groupe_instructeurs.each do |gi|
|
||||
gi.update(routing_rule: ds_eq(empty, empty))
|
||||
end
|
||||
end
|
||||
|
||||
it { is_expected.to eq(defaut_groupe) }
|
||||
end
|
||||
|
||||
context 'with a matching rule' do
|
||||
before do
|
||||
gi_2.update(routing_rule: ds_eq(champ_value(drop_down_tdc.stable_id), constant('Lyon')))
|
||||
dossier.champs.first.update(value: 'Lyon')
|
||||
end
|
||||
|
||||
it { is_expected.to eq(gi_2) }
|
||||
end
|
||||
|
||||
context 'with a closed gi with a matching rules' do
|
||||
context 'with a closed gi with a matching rule' do
|
||||
before { gi_2.update(routing_rule: constant(true), closed: true) }
|
||||
|
||||
it { is_expected.to eq(defaut_groupe) }
|
||||
|
|
|
@ -17,24 +17,7 @@ describe 'Manage procedure instructeurs', js: true do
|
|||
scenario 'it works' do
|
||||
visit admin_procedure_path(procedure)
|
||||
find('#groupe-instructeurs').click
|
||||
expect(page).to have_css("h1", text: "Gérer les instructeurs et les options d'instruction de « #{procedure.libelle} »")
|
||||
end
|
||||
end
|
||||
|
||||
context 'as admin not from manager' do
|
||||
let(:manager) { false }
|
||||
|
||||
scenario 'can add instructeur' do
|
||||
visit admin_procedure_groupe_instructeurs_path(procedure)
|
||||
expect {
|
||||
fill_in "instructeur_emails", with: create(:instructeur).email
|
||||
click_on "Affecter"
|
||||
}.to change { procedure.instructeurs.count }.by(1)
|
||||
expect {
|
||||
fill_in "groupe_instructeur_label", with: "Bordeaux"
|
||||
click_on "Ajouter le groupe"
|
||||
}.to change { procedure.groupe_instructeurs.count }.by(1)
|
||||
expect(procedure.reload.routing_enabled).to eq true
|
||||
expect(page).to have_css("h1", text: "Gestion des instructeurs")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
describe 'As an administrateur I can manage procedure routing', js: true do
|
||||
include Logic
|
||||
|
||||
let(:administrateur) { procedure.administrateurs.first }
|
||||
let!(:gi_1) { procedure.defaut_groupe_instructeur }
|
||||
let!(:gi_2) { procedure.groupe_instructeurs.create(label: 'a second group') }
|
||||
let!(:gi_3) { procedure.groupe_instructeurs.create(label: 'a third group') }
|
||||
|
||||
let(:procedure) do
|
||||
create(:procedure).tap do |p|
|
||||
p.draft_revision.add_type_de_champ(
|
||||
type_champ: :drop_down_list,
|
||||
libelle: 'Un champ choix simple',
|
||||
options: { "drop_down_other" => "0", "drop_down_options" => ["", "Premier choix", "Deuxième choix", "Troisième choix"] }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
let(:drop_down_tdc) { procedure.draft_revision.types_de_champ.first }
|
||||
|
||||
before do
|
||||
Flipper.enable(:routing_rules, procedure)
|
||||
procedure.publish_revision!
|
||||
login_as administrateur.user, scope: :user
|
||||
end
|
||||
|
||||
it 'routes from a drop_down_list' do
|
||||
visit admin_procedure_groupe_instructeurs_path(procedure)
|
||||
|
||||
within('.condition-table tbody tr:nth-child(1)', match: :first) do
|
||||
expect(page).to have_select('targeted_champ', options: ['Sélectionner', 'Un champ choix simple'])
|
||||
within('.target') { select('Un champ choix simple') }
|
||||
within('.value') { select('Premier choix') }
|
||||
end
|
||||
|
||||
expected_routing_rule = ds_eq(champ_value(drop_down_tdc.stable_id), constant('Premier choix'))
|
||||
wait_until { gi_2.reload.routing_rule == expected_routing_rule }
|
||||
end
|
||||
|
||||
it 'displays groupes instructeurs by alphabetic order' do
|
||||
visit admin_procedure_groupe_instructeurs_path(procedure)
|
||||
|
||||
within('.condition-table tbody tr:nth-child(1)', match: :first) do
|
||||
expect(page).to have_content 'second group'
|
||||
expect(page).not_to have_content 'défaut'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,238 +0,0 @@
|
|||
describe 'The routing', js: true do
|
||||
let(:password) { 'a very complicated password' }
|
||||
let(:procedure) { create(:procedure, :with_type_de_champ, :with_service, :for_individual, :with_zone) }
|
||||
let(:administrateur) { create(:administrateur, procedures: [procedure]) }
|
||||
let(:scientifique_user) { create(:user, password: password) }
|
||||
let(:litteraire_user) { create(:user, password: password) }
|
||||
|
||||
before do
|
||||
procedure.update(routing_enabled: true)
|
||||
procedure.defaut_groupe_instructeur.instructeurs << administrateur.instructeur
|
||||
end
|
||||
|
||||
scenario 'works' do
|
||||
login_as administrateur.user, scope: :user
|
||||
visit admin_procedure_path(procedure.id)
|
||||
find('#groupe-instructeurs').click
|
||||
|
||||
# add littéraire groupe
|
||||
fill_in 'Ajouter un nom de groupe', with: 'littéraire'
|
||||
click_on 'Ajouter le groupe'
|
||||
expect(page).to have_text('Le groupe d’instructeurs « littéraire » a été créé et le routage a été activé.')
|
||||
|
||||
# add victor to littéraire groupe
|
||||
fill_in 'Emails', with: 'victor@inst.com'
|
||||
perform_enqueued_jobs { click_on 'Affecter' }
|
||||
expect(page).to have_text("L’instructeur victor@inst.com a été affecté")
|
||||
|
||||
victor = User.find_by(email: 'victor@inst.com').instructeur
|
||||
|
||||
# add superwoman to littéraire groupe
|
||||
fill_in 'Emails', with: 'superwoman@inst.com'
|
||||
perform_enqueued_jobs { click_on 'Affecter' }
|
||||
expect(page).to have_text("L’instructeur superwoman@inst.com a été affecté")
|
||||
|
||||
superwoman = User.find_by(email: 'superwoman@inst.com').instructeur
|
||||
|
||||
# rename routing criteria to spécialité
|
||||
click_on 'Groupes d’instructeurs'
|
||||
fill_in 'Libellé de la liste de groupes', with: 'spécialité'
|
||||
click_on 'Renommer'
|
||||
expect(page).to have_text('Le libellé est maintenant « spécialité ».')
|
||||
expect(page).to have_field('Libellé de la liste de groupes', with: 'spécialité')
|
||||
|
||||
# add inactive groupe
|
||||
fill_in 'Ajouter un nom de groupe', with: 'non visible car inactif'
|
||||
click_on 'Ajouter le groupe'
|
||||
check "Groupe inactif"
|
||||
click_on 'Modifier'
|
||||
|
||||
# add scientifique groupe
|
||||
click_on 'Groupes d’instructeurs'
|
||||
fill_in 'Ajouter un nom de groupe', with: 'scientifique'
|
||||
click_on 'Ajouter le groupe'
|
||||
expect(page).to have_text('Le groupe d’instructeurs « scientifique » a été créé.')
|
||||
|
||||
# add marie to scientifique groupe
|
||||
fill_in 'Emails', with: 'marie@inst.com'
|
||||
perform_enqueued_jobs { click_on 'Affecter' }
|
||||
expect(page).to have_text("L’instructeur marie@inst.com a été affecté")
|
||||
|
||||
marie = User.find_by(email: 'marie@inst.com').instructeur
|
||||
|
||||
# add superwoman to scientifique groupe
|
||||
fill_in 'Emails', with: 'superwoman@inst.com'
|
||||
perform_enqueued_jobs { click_on 'Affecter' }
|
||||
expect(page).to have_text("L’instructeur superwoman@inst.com a été affecté")
|
||||
|
||||
# publish
|
||||
publish_procedure(procedure)
|
||||
log_out
|
||||
|
||||
# 2 users fill a dossier in each group
|
||||
user_send_dossier(scientifique_user, 'scientifique')
|
||||
user_send_dossier(litteraire_user, 'littéraire')
|
||||
|
||||
# the litteraires instructeurs only manage the litteraires dossiers
|
||||
register_instructeur_and_log_in(victor.email)
|
||||
click_on procedure.libelle
|
||||
expect(page).to have_text(litteraire_user.email)
|
||||
expect(page).not_to have_text(scientifique_user.email)
|
||||
|
||||
# the search only show litteraires dossiers
|
||||
fill_in 'q', with: scientifique_user.email
|
||||
find('.fr-search-bar .fr-btn').click
|
||||
expect(page).to have_text('0 dossier trouvé')
|
||||
|
||||
fill_in 'q', with: litteraire_user.email
|
||||
find('.fr-search-bar .fr-btn').click
|
||||
expect(page).to have_text('1 dossier trouvé')
|
||||
|
||||
## and the result is clickable
|
||||
click_on litteraire_user.email
|
||||
expect(page).to have_current_path(instructeur_dossier_path(procedure, litteraire_user.dossiers.first))
|
||||
|
||||
# follow the dossier
|
||||
click_on 'Suivre le dossier'
|
||||
|
||||
log_out
|
||||
|
||||
# the scientifiques instructeurs only manage the scientifiques dossiers
|
||||
register_instructeur_and_log_in(marie.email)
|
||||
click_on procedure.libelle
|
||||
expect(page).not_to have_text(litteraire_user.email)
|
||||
expect(page).to have_text(scientifique_user.email)
|
||||
|
||||
# follow the dossier
|
||||
click_on scientifique_user.email
|
||||
click_on 'Suivre le dossier'
|
||||
|
||||
log_out
|
||||
|
||||
# litteraire_user change its dossier
|
||||
visit new_user_session_path
|
||||
sign_in_with litteraire_user.email, password
|
||||
|
||||
click_on litteraire_user.dossiers.first.id.to_s
|
||||
click_on 'Modifier mon dossier'
|
||||
|
||||
fill_in litteraire_user.dossiers.first.champs_public.first.libelle, with: 'some value'
|
||||
wait_for_autosave(false)
|
||||
|
||||
click_on 'Déposer les modifications'
|
||||
|
||||
log_out
|
||||
|
||||
# the litteraires instructeurs should have a notification
|
||||
visit new_user_session_path
|
||||
sign_in_with victor.user.email, password
|
||||
|
||||
## on the procedures list
|
||||
expect(page).to have_current_path(instructeur_procedures_path)
|
||||
expect(find('.procedure-stats')).to have_css('span.notifications')
|
||||
|
||||
## on the dossiers list
|
||||
click_on procedure.libelle
|
||||
expect(page).to have_current_path(instructeur_procedure_path(procedure))
|
||||
expect(find('.tabs')).to have_css('span.notifications')
|
||||
|
||||
## on the dossier itself
|
||||
click_on 'suivi'
|
||||
click_on litteraire_user.email
|
||||
expect(page).to have_current_path(instructeur_dossier_path(procedure, litteraire_user.dossiers.first))
|
||||
expect(page).to have_text('Annotations privées')
|
||||
expect(find('.tabs')).to have_css('span.notifications')
|
||||
log_out
|
||||
|
||||
# the scientifiques instructeurs should not have a notification
|
||||
visit new_user_session_path
|
||||
sign_in_with marie.user.email, password
|
||||
|
||||
expect(page).to have_current_path(instructeur_procedures_path)
|
||||
expect(find('.procedure-stats')).not_to have_css('span.notifications')
|
||||
log_out
|
||||
|
||||
# the instructeurs who belong to scientifique AND litteraire groups manage scientifique and litterraire dossiers
|
||||
register_instructeur_and_log_in(superwoman.email)
|
||||
visit instructeur_procedure_path(procedure, params: { statut: 'tous' })
|
||||
expect(page).to have_text(litteraire_user.email)
|
||||
expect(page).to have_text(scientifique_user.email)
|
||||
|
||||
# follow the dossier
|
||||
click_on scientifique_user.email
|
||||
click_on 'Suivre le dossier'
|
||||
|
||||
visit instructeur_procedure_path(procedure, params: { statut: 'tous' })
|
||||
click_on litteraire_user.email
|
||||
click_on 'Suivre le dossier'
|
||||
log_out
|
||||
|
||||
# scientifique_user updates its group
|
||||
user_update_group(scientifique_user, 'littéraire')
|
||||
|
||||
# the instructeurs who belong to scientifique AND litteraire groups should have a notification
|
||||
visit new_user_session_path
|
||||
sign_in_with superwoman.user.email, password
|
||||
|
||||
expect(page).to have_current_path(instructeur_procedures_path)
|
||||
expect(find('.procedure-stats')).to have_css('span.notifications')
|
||||
end
|
||||
|
||||
def publish_procedure(procedure)
|
||||
click_on procedure.libelle
|
||||
find('#publish-procedure-link').click
|
||||
fill_in 'lien_site_web', with: 'http://some.website'
|
||||
click_on 'Publier'
|
||||
|
||||
expect(page).to have_text('Démarche publiée')
|
||||
end
|
||||
|
||||
def user_send_dossier(user, groupe)
|
||||
login_as user, scope: :user
|
||||
visit commencer_path(path: procedure.reload.path)
|
||||
click_on 'Commencer la démarche'
|
||||
|
||||
choose 'Monsieur'
|
||||
fill_in 'individual_nom', with: 'Nom'
|
||||
fill_in 'individual_prenom', with: 'Prenom'
|
||||
click_button('Continuer')
|
||||
|
||||
select(groupe, from: 'dossier_groupe_instructeur_id')
|
||||
wait_for_autosave
|
||||
|
||||
click_on 'Déposer le dossier'
|
||||
expect(page).to have_text('Merci')
|
||||
|
||||
log_out
|
||||
end
|
||||
|
||||
def user_update_group(user, new_group)
|
||||
login_as user, scope: :user
|
||||
visit dossiers_path
|
||||
click_on user.dossiers.first.id.to_s
|
||||
click_on "Modifier mon dossier"
|
||||
expect(page).to have_selector("option", text: "scientifique")
|
||||
expect(page).not_to have_selector("option", text: "Groupe inactif")
|
||||
|
||||
select(new_group, from: 'dossier_groupe_instructeur_id')
|
||||
wait_for_autosave(false)
|
||||
|
||||
expect(page).to have_text(new_group)
|
||||
|
||||
click_on 'Déposer les modifications'
|
||||
|
||||
log_out
|
||||
end
|
||||
|
||||
def register_instructeur_and_log_in(email)
|
||||
confirmation_email = emails_sent_to(email)
|
||||
.find { |m| m.subject == 'Activez votre compte instructeur' }
|
||||
token_params = confirmation_email.body.match(/token=[^"]+/)
|
||||
|
||||
visit "users/activate?#{token_params}"
|
||||
fill_in :user_password, with: password
|
||||
click_button 'Définir le mot de passe'
|
||||
|
||||
expect(page).to have_text('Mot de passe enregistré')
|
||||
end
|
||||
end
|
|
@ -10,28 +10,60 @@ describe 'The routing with rules', js: true do
|
|||
p.draft_revision.add_type_de_champ(
|
||||
type_champ: :drop_down_list,
|
||||
libelle: 'Spécialité',
|
||||
options: { "drop_down_other" => "0", "drop_down_options" => ["", "littéraire", "scientifique"] }
|
||||
options: { "drop_down_other" => "0", "drop_down_options" => ["", "littéraire", "scientifique", "artistique"] }
|
||||
)
|
||||
end
|
||||
end
|
||||
let(:administrateur) { create(:administrateur, procedures: [procedure]) }
|
||||
let(:scientifique_user) { create(:user, password: password) }
|
||||
let(:litteraire_user) { create(:user, password: password) }
|
||||
let(:artistique_user) { create(:user, password: password) }
|
||||
|
||||
before do
|
||||
Flipper.enable(:routing_rules, procedure)
|
||||
procedure.defaut_groupe_instructeur.instructeurs << administrateur.instructeur
|
||||
end
|
||||
|
||||
scenario 'works' do
|
||||
login_as administrateur.user, scope: :user
|
||||
visit admin_procedure_path(procedure.id)
|
||||
find('#groupe-instructeurs').click
|
||||
scenario 'Routage à partir d’un champ' do
|
||||
steps_to_routing_configuration
|
||||
|
||||
# add littéraire groupe
|
||||
fill_in 'Ajouter un nom de groupe', with: 'littéraire'
|
||||
click_on 'Ajouter le groupe'
|
||||
expect(page).to have_text('Le groupe d’instructeurs « littéraire » a été créé et le routage a été activé.')
|
||||
choose('À partir d’un champ', allow_label_click: true)
|
||||
click_on 'Continuer'
|
||||
|
||||
expect(page).to have_text('Routage à partir d’un champ')
|
||||
|
||||
choose('Spécialité', allow_label_click: true)
|
||||
click_on 'Créer les groupes'
|
||||
|
||||
expect(page).to have_text('Gestion des groupes')
|
||||
expect(page).to have_text('3 groupes')
|
||||
expect(page).not_to have_text('À configurer')
|
||||
|
||||
click_on 'littéraire'
|
||||
expect(page).to have_select("targeted_champ", selected: "Spécialité")
|
||||
expect(page).to have_select("value", selected: "littéraire")
|
||||
|
||||
click_on '3 groupes'
|
||||
click_on 'scientifique'
|
||||
|
||||
expect(page).to have_select("targeted_champ", selected: "Spécialité")
|
||||
expect(page).to have_select("value", selected: "scientifique")
|
||||
end
|
||||
|
||||
scenario 'Routage personnalisé' do
|
||||
steps_to_routing_configuration
|
||||
|
||||
choose('Avancé', allow_label_click: true)
|
||||
click_on 'Continuer'
|
||||
|
||||
expect(page).to have_text('Gestion des groupes')
|
||||
|
||||
# update defaut groupe
|
||||
click_on 'défaut'
|
||||
expect(page).to have_text('Paramètres principaux')
|
||||
fill_in 'Nom du groupe', with: 'littéraire'
|
||||
click_on 'Renommer'
|
||||
expect(page).to have_text('Le nom est à présent « littéraire ». ')
|
||||
|
||||
# add victor to littéraire groupe
|
||||
fill_in 'Emails', with: 'victor@inst.com'
|
||||
|
@ -48,17 +80,18 @@ describe 'The routing with rules', js: true do
|
|||
superwoman = User.find_by(email: 'superwoman@inst.com').instructeur
|
||||
|
||||
# add inactive groupe
|
||||
click_on 'Groupes d’instructeurs'
|
||||
fill_in 'Ajouter un nom de groupe', with: 'non visible car inactif'
|
||||
click_on 'Ajouter le groupe'
|
||||
check "Groupe inactif"
|
||||
click_on 'Modifier'
|
||||
click_on 'Ajout de groupes'
|
||||
fill_in 'Nouveau groupe', with: 'non visible car inactif'
|
||||
click_on 'Ajouter'
|
||||
expect(page).to have_text('Le groupe d’instructeurs « non visible car inactif » a été créé. ')
|
||||
check("Groupe inactif", allow_label_click: true)
|
||||
|
||||
# add scientifique groupe
|
||||
click_on 'Groupes d’instructeurs'
|
||||
fill_in 'Ajouter un nom de groupe', with: 'scientifique'
|
||||
click_on 'Ajouter le groupe'
|
||||
expect(page).to have_text('Le groupe d’instructeurs « scientifique » a été créé.')
|
||||
# # add scientifique groupe
|
||||
click_on '3 groupes'
|
||||
click_on 'défaut bis'
|
||||
fill_in 'Nom du groupe', with: 'scientifique'
|
||||
click_on 'Renommer'
|
||||
expect(page).to have_text('Le nom est à présent « scientifique ». ')
|
||||
|
||||
# add marie to scientifique groupe
|
||||
fill_in 'Emails', with: 'marie@inst.com'
|
||||
|
@ -73,30 +106,33 @@ describe 'The routing with rules', js: true do
|
|||
expect(page).to have_text("L’instructeur superwoman@inst.com a été affecté")
|
||||
|
||||
# add routing rules
|
||||
click_on 'Groupes d’instructeurs'
|
||||
within('.target') { select('Spécialité') }
|
||||
within('.value') { select('scientifique') }
|
||||
|
||||
h = procedure.groupe_instructeurs.index_by(&:label).transform_values(&:id)
|
||||
click_on '3 groupes'
|
||||
|
||||
within(".gi-#{h['scientifique']}") do
|
||||
within('.target') { select('Spécialité') }
|
||||
within('.value') { select('scientifique') }
|
||||
end
|
||||
click_on 'littéraire'
|
||||
|
||||
within(".gi-#{h['littéraire']}") do
|
||||
within('.target') { select('Spécialité') }
|
||||
within('.value') { select('littéraire') }
|
||||
end
|
||||
within('.target') { select('Spécialité') }
|
||||
within('.value') { select('littéraire') }
|
||||
|
||||
not_defauts = procedure.groupe_instructeurs.filter { |gi| ['littéraire', 'scientifique'].include?(gi.label) }
|
||||
not_defauts.each { |gi| wait_until { gi.reload.routing_rule.present? } }
|
||||
procedure.groupe_instructeurs.where(closed: false).each { |gi| wait_until { gi.reload.routing_rule.present? } }
|
||||
|
||||
# add a group without routing rules
|
||||
click_on 'Ajout de groupes'
|
||||
fill_in 'Nouveau groupe', with: 'artistique'
|
||||
click_on 'Ajouter'
|
||||
expect(page).to have_text('Le groupe d’instructeurs « artistique » a été créé. ')
|
||||
expect(procedure.groupe_instructeurs.count).to eq(4)
|
||||
|
||||
# publish
|
||||
publish_procedure(procedure)
|
||||
log_out
|
||||
|
||||
# 2 users fill a dossier in each group
|
||||
# 3 users fill a dossier in each group
|
||||
user_send_dossier(scientifique_user, 'scientifique')
|
||||
user_send_dossier(litteraire_user, 'littéraire')
|
||||
user_send_dossier(artistique_user, 'artistique')
|
||||
|
||||
# the litteraires instructeurs only manage the litteraires dossiers
|
||||
register_instructeur_and_log_in(victor.email)
|
||||
|
@ -179,7 +215,7 @@ describe 'The routing with rules', js: true do
|
|||
expect(find('.procedure-stats')).not_to have_css('span.notifications')
|
||||
log_out
|
||||
|
||||
# the instructeurs who belong to scientifique AND litteraire groups manage scientifique and litterraire dossiers
|
||||
# the instructeurs who belong to scientifique AND litteraire groups manage scientifique and litteraire dossiers
|
||||
register_instructeur_and_log_in(superwoman.email)
|
||||
visit instructeur_procedure_path(procedure, params: { statut: 'tous' })
|
||||
expect(page).to have_text(litteraire_user.email)
|
||||
|
@ -263,4 +299,15 @@ describe 'The routing with rules', js: true do
|
|||
|
||||
expect(page).to have_text('Mot de passe enregistré')
|
||||
end
|
||||
|
||||
def steps_to_routing_configuration
|
||||
login_as administrateur.user, scope: :user
|
||||
visit admin_procedure_path(procedure.id)
|
||||
find('#groupe-instructeurs').click
|
||||
|
||||
click_on 'Options'
|
||||
expect(page).to have_text('Options concernant l’instruction')
|
||||
click_on 'Configurer le routage'
|
||||
expect(page).to have_text('Choix du type de routage')
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue