Merge pull request #10990 from demarches-simplifiees/simplify_procedure_presentation_update
Tech: extrait la mise à jour du procedure_presentation dans son propre controller
This commit is contained in:
commit
10e3faec92
27 changed files with 483 additions and 212 deletions
|
@ -3,45 +3,18 @@
|
|||
class Instructeurs::ColumnFilterComponent < ApplicationComponent
|
||||
attr_reader :procedure, :procedure_presentation, :statut, :column
|
||||
|
||||
def initialize(procedure:, procedure_presentation:, statut:, column: nil)
|
||||
@procedure = procedure
|
||||
def initialize(procedure_presentation:, statut:, column: nil)
|
||||
@procedure_presentation = procedure_presentation
|
||||
@procedure = procedure_presentation.procedure
|
||||
@statut = statut
|
||||
@column = column
|
||||
end
|
||||
|
||||
def column_type = column.present? ? column.type : :text
|
||||
|
||||
def html_column_type
|
||||
case column_type
|
||||
when :datetime, :date
|
||||
'date'
|
||||
when :integer, :decimal
|
||||
'number'
|
||||
else
|
||||
'text'
|
||||
end
|
||||
end
|
||||
|
||||
def options_for_select_of_column
|
||||
if column.scope.present?
|
||||
I18n.t(column.scope).map(&:to_a).map(&:reverse)
|
||||
elsif column.table == 'groupe_instructeur'
|
||||
current_instructeur.groupe_instructeurs.filter_map do
|
||||
if _1.procedure_id == procedure.id
|
||||
[_1.label, _1.id]
|
||||
end
|
||||
end
|
||||
else
|
||||
find_type_de_champ(column.column).options_for_select(column)
|
||||
end
|
||||
end
|
||||
|
||||
def filter_react_props
|
||||
{
|
||||
selected_key: column.present? ? column.id : '',
|
||||
items: filterable_columns_options,
|
||||
name: "#{prefix}[id]",
|
||||
name: "filters[][id]",
|
||||
id: 'search-filter',
|
||||
'aria-describedby': 'instructeur-filter-combo-label',
|
||||
form: 'filter-component',
|
||||
|
@ -56,22 +29,9 @@ class Instructeurs::ColumnFilterComponent < ApplicationComponent
|
|||
def current_filter_tags
|
||||
@procedure_presentation.filters_for(@statut).flat_map do
|
||||
[
|
||||
hidden_field_tag("#{prefix}[id]", _1.column.id, id: nil),
|
||||
hidden_field_tag("#{prefix}[filter]", _1.filter, id: nil)
|
||||
hidden_field_tag("filters[][id]", _1.column.id, id: nil),
|
||||
hidden_field_tag("filters[][filter]", _1.filter, id: nil)
|
||||
]
|
||||
end.reduce(&:concat)
|
||||
end
|
||||
|
||||
def prefix = "#{procedure_presentation.filters_name_for(@statut)}[]"
|
||||
|
||||
private
|
||||
|
||||
def find_type_de_champ(column)
|
||||
stable_id = column.to_s.split('->').first
|
||||
TypeDeChamp
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { revision_id: procedure.revisions })
|
||||
.order(created_at: :desc)
|
||||
.find_by(stable_id:)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
= form_tag add_filter_instructeur_procedure_path(procedure), method: :post, class: 'dropdown-form large', id: 'filter-component', data: { turbo: true, controller: 'autosubmit' } do
|
||||
= form_with model: [:instructeur, @procedure_presentation],
|
||||
class: 'dropdown-form large',
|
||||
id: 'filter-component',
|
||||
data: { turbo: true, controller: 'autosubmit' } do
|
||||
= current_filter_tags
|
||||
|
||||
.fr-select-group
|
||||
|
@ -8,28 +11,14 @@
|
|||
|
||||
%input.hidden{
|
||||
type: 'submit',
|
||||
formaction: update_filter_instructeur_procedure_path(procedure),
|
||||
formmethod: 'get',
|
||||
formaction: url_for([:refresh_column_filter, :instructeur, @procedure_presentation]),
|
||||
formnovalidate: 'true',
|
||||
data: { autosubmit_target: 'submitter' }
|
||||
}
|
||||
|
||||
= label_tag :value, t('.value'), for: 'value', class: 'fr-label'
|
||||
- if column_type.in?([:enum, :enums, :boolean])
|
||||
= select_tag :filter,
|
||||
options_for_select(options_for_select_of_column),
|
||||
id: 'value',
|
||||
name: "#{prefix}[filter]",
|
||||
class: 'fr-select',
|
||||
data: { no_autosubmit: true }
|
||||
- else
|
||||
%input#value.fr-input{
|
||||
type: html_column_type,
|
||||
name: "#{prefix}[filter]",
|
||||
maxlength: FilteredColumn::FILTERS_VALUE_MAX_LENGTH,
|
||||
disabled: column.nil? ? true : false,
|
||||
data: { no_autosubmit: true },
|
||||
required: true
|
||||
}
|
||||
= render Instructeurs::ColumnFilterValueComponent.new(column:)
|
||||
|
||||
= hidden_field_tag :statut, statut
|
||||
= submit_tag t('.add_filter'), class: 'fr-btn fr-btn--secondary fr-mt-2w'
|
||||
|
|
71
app/components/instructeurs/column_filter_value_component.rb
Normal file
71
app/components/instructeurs/column_filter_value_component.rb
Normal file
|
@ -0,0 +1,71 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Instructeurs::ColumnFilterValueComponent < ApplicationComponent
|
||||
attr_reader :column
|
||||
|
||||
def initialize(column:)
|
||||
@column = column
|
||||
end
|
||||
|
||||
def column_type = column.present? ? column.type : :text
|
||||
|
||||
def call
|
||||
if column_type.in?([:enum, :enums, :boolean])
|
||||
select_tag :filter,
|
||||
options_for_select(options_for_select_of_column),
|
||||
id: 'value',
|
||||
name: "filters[][filter]",
|
||||
class: 'fr-select',
|
||||
data: { no_autosubmit: true }
|
||||
else
|
||||
tag.input(
|
||||
class: 'fr-input',
|
||||
id: 'value',
|
||||
type:,
|
||||
name: "filters[][filter]",
|
||||
maxlength: FilteredColumn::FILTERS_VALUE_MAX_LENGTH,
|
||||
disabled: column.nil? ? true : false,
|
||||
data: { no_autosubmit: true },
|
||||
required: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def type
|
||||
case column_type
|
||||
when :datetime, :date
|
||||
'date'
|
||||
when :integer, :decimal
|
||||
'number'
|
||||
else
|
||||
'text'
|
||||
end
|
||||
end
|
||||
|
||||
def options_for_select_of_column
|
||||
if column.scope.present?
|
||||
I18n.t(column.scope).map(&:to_a).map(&:reverse)
|
||||
elsif column.table == 'groupe_instructeur'
|
||||
current_instructeur.groupe_instructeurs.filter_map do
|
||||
if _1.procedure_id == procedure_id
|
||||
[_1.label, _1.id]
|
||||
end
|
||||
end
|
||||
else
|
||||
find_type_de_champ(column.column).options_for_select(column)
|
||||
end
|
||||
end
|
||||
|
||||
def find_type_de_champ(column)
|
||||
stable_id = column.to_s.split('->').first
|
||||
TypeDeChamp
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { revision_id: ProcedureRevision.where(procedure_id:) })
|
||||
.order(created_at: :desc)
|
||||
.find_by(stable_id:)
|
||||
end
|
||||
|
||||
def procedure_id = @column.h_id[:procedure_id]
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
= form_tag update_displayed_fields_instructeur_procedure_path(@procedure), method: :patch, class: 'dropdown-form large columns-form' do
|
||||
= form_with model: [:instructeur, @procedure_presentation], class: 'dropdown-form large columns-form' do
|
||||
%react-fragment
|
||||
= render ReactComponent.new "ComboBox/MultiComboBox", items: @displayable_columns_for_select, selected_keys: @displayable_columns_selected, name: 'values[]', 'aria-label': 'Colonne à afficher', value_separator: false
|
||||
= render ReactComponent.new "ComboBox/MultiComboBox", items: @displayable_columns_for_select, selected_keys: @displayable_columns_selected, name: 'displayed_columns[]', 'aria-label': 'Colonne à afficher', value_separator: false
|
||||
|
||||
= submit_tag t('.save'), class: 'fr-btn fr-btn--secondary'
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class Instructeurs::ColumnTableHeaderComponent < ApplicationComponent
|
||||
def initialize(procedure_presentation:)
|
||||
@procedure = procedure_presentation.procedure
|
||||
@procedure_presentation = procedure_presentation
|
||||
@columns = procedure_presentation.displayed_fields_for_headers
|
||||
@sorted_column = procedure_presentation.sorted_column
|
||||
end
|
||||
|
@ -15,11 +15,16 @@ class Instructeurs::ColumnTableHeaderComponent < ApplicationComponent
|
|||
return 'sva-col' if column.column == 'sva_svr_decision_on'
|
||||
end
|
||||
|
||||
def update_sort_path(column)
|
||||
def column_header(column)
|
||||
id = column.id
|
||||
order = opposite_order_for(column)
|
||||
|
||||
update_sort_instructeur_procedure_path(@procedure, sorted_column: { id:, order: })
|
||||
button_to(
|
||||
label_and_arrow(column),
|
||||
[:instructeur, @procedure_presentation],
|
||||
params: { sorted_column: { id: id, order: order } },
|
||||
class: 'fr-text--bold'
|
||||
)
|
||||
end
|
||||
|
||||
def opposite_order_for(column)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
- @columns.each do |column|
|
||||
%th{ aria_sort(column), scope: "col", class: classname(column) }
|
||||
= link_to label_and_arrow(column), update_sort_path(column)
|
||||
= column_header(column)
|
||||
|
|
78
app/components/instructeurs/filter_buttons_component.rb
Normal file
78
app/components/instructeurs/filter_buttons_component.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Instructeurs::FilterButtonsComponent < ApplicationComponent
|
||||
def initialize(filters:, procedure_presentation:, statut:)
|
||||
@filters = filters
|
||||
@procedure_presentation = procedure_presentation
|
||||
@statut = statut
|
||||
end
|
||||
|
||||
def call
|
||||
safe_join(filters_by_family, ' et ')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filters_by_family
|
||||
@filters
|
||||
.group_by { _1.column.id }
|
||||
.values
|
||||
.map { |group| group.map { |f| filter_form(f) } }
|
||||
.map { |group| safe_join(group, ' ou ') }
|
||||
end
|
||||
|
||||
def filter_form(filter)
|
||||
form_with(model: [:instructeur, @procedure_presentation], class: 'inline') do
|
||||
safe_join([
|
||||
hidden_field_tag('filters[]', ''), # to ensure the filters is not empty
|
||||
*other_hidden_fields(filter), # other filters to keep
|
||||
hidden_field_tag('statut', @statut), # collection to set
|
||||
button_tag(button_content(filter), class: 'fr-tag fr-tag--dismiss fr-my-1w')
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
def other_hidden_fields(filter)
|
||||
@filters.reject { _1 == filter }.flat_map do |f|
|
||||
[
|
||||
hidden_field_tag("filters[][id]", f.column.id),
|
||||
hidden_field_tag("filters[][filter]", f.filter)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def button_content(filter)
|
||||
"#{filter.label.truncate(50)} : #{human_value(filter)}"
|
||||
end
|
||||
|
||||
def human_value(filter_column)
|
||||
column, filter = filter_column.column, filter_column.filter
|
||||
|
||||
if column.type_de_champ?
|
||||
find_type_de_champ(column.column).dynamic_type.filter_to_human(filter)
|
||||
elsif column.dossier_state?
|
||||
if filter == 'pending_correction'
|
||||
Dossier.human_attribute_name("pending_correction.for_instructeur")
|
||||
else
|
||||
Dossier.human_attribute_name("state.#{filter}")
|
||||
end
|
||||
elsif column.groupe_instructeur?
|
||||
current_instructeur.groupe_instructeurs
|
||||
.find { _1.id == filter.to_i }&.label || filter
|
||||
elsif column.type == :date
|
||||
helpers.try_parse_format_date(filter)
|
||||
else
|
||||
filter
|
||||
end
|
||||
end
|
||||
|
||||
def find_type_de_champ(column)
|
||||
stable_id = column.to_s.split('->').first
|
||||
|
||||
TypeDeChamp
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { revision_id: @procedure_presentation.procedure.revisions })
|
||||
.order(created_at: :desc)
|
||||
.find_by(stable_id:)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Instructeurs
|
||||
class ProcedurePresentationController < InstructeurController
|
||||
before_action :set_procedure_presentation, only: [:update]
|
||||
|
||||
def update
|
||||
if !@procedure_presentation.update(procedure_presentation_params)
|
||||
# complicated way to display inner error messages
|
||||
flash.alert = @procedure_presentation.errors
|
||||
.flat_map { _1.detail[:value].flat_map { |c| c.errors.full_messages } }
|
||||
end
|
||||
|
||||
redirect_back_or_to([:instructeur, procedure])
|
||||
end
|
||||
|
||||
def refresh_column_filter
|
||||
# According to the html, the selected filters is the last one
|
||||
column = ColumnType.new.cast(params['filters'].last['id'])
|
||||
component = Instructeurs::ColumnFilterValueComponent.new(column:)
|
||||
|
||||
render turbo_stream: turbo_stream.replace('value', component)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def procedure = @procedure_presentation.procedure
|
||||
|
||||
def procedure_presentation_params
|
||||
h = params.permit(displayed_columns: [], sorted_column: [:order, :id], filters: [:id, :filter]).to_h
|
||||
|
||||
if params[:statut].present?
|
||||
filter_name = @procedure_presentation.filters_name_for(params[:statut])
|
||||
h[filter_name] = h.delete("filters") # move filters to the right key, ex: tous_filters
|
||||
end
|
||||
|
||||
# React ComboBox/MultiComboBox return [''] when no value is selected
|
||||
# We need to remove them
|
||||
if h[:displayed_columns].present?
|
||||
h[:displayed_columns] = h[:displayed_columns].reject(&:empty?)
|
||||
end
|
||||
|
||||
h
|
||||
end
|
||||
|
||||
def set_procedure_presentation
|
||||
@procedure_presentation = ProcedurePresentation
|
||||
.includes(:assign_to)
|
||||
.find_by!(id: params[:id], assign_to: { instructeur: current_instructeur })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -132,6 +132,7 @@ module Instructeurs
|
|||
@statut = 'supprime'
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
def update_displayed_fields
|
||||
ids = (params['values'].presence || []).reject(&:empty?)
|
||||
|
||||
|
@ -140,12 +141,14 @@ module Instructeurs
|
|||
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
def update_sort
|
||||
procedure_presentation.update!(sorted_column_params)
|
||||
|
||||
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
def add_filter
|
||||
if !procedure_presentation.update(filter_params)
|
||||
# complicated way to display inner error messages
|
||||
|
@ -156,6 +159,7 @@ module Instructeurs
|
|||
redirect_back(fallback_location: instructeur_procedure_url(procedure))
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
def update_filter
|
||||
@statut = statut
|
||||
@procedure = procedure
|
||||
|
@ -407,10 +411,12 @@ module Instructeurs
|
|||
"exports_#{@procedure.id}_seen_at"
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
def sorted_column_params
|
||||
params.permit(sorted_column: [:order, :id])
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
def filter_params
|
||||
keys = [:tous_filters, :a_suivre_filters, :suivis_filters, :traites_filters, :expirant_filters, :archives_filters, :supprimes_filters]
|
||||
h = keys.index_with { [:id, :filter] }
|
||||
|
|
|
@ -113,6 +113,10 @@ module ApplicationHelper
|
|||
datetime.present? ? I18n.l(datetime, format:) : ''
|
||||
end
|
||||
|
||||
def try_parse_format_date(date)
|
||||
date.then { Date.parse(_1) rescue nil }&.then { I18n.l(_1) }
|
||||
end
|
||||
|
||||
def try_format_mois_effectif(etablissement)
|
||||
if etablissement.entreprise_effectif_mois.present? && etablissement.entreprise_effectif_annee.present?
|
||||
[etablissement.entreprise_effectif_mois, etablissement.entreprise_effectif_annee].join('/')
|
||||
|
|
|
@ -24,7 +24,9 @@ class AssignTo < ApplicationRecord
|
|||
errors = begin
|
||||
procedure_presentation.errors if procedure_presentation&.invalid?
|
||||
rescue ActiveRecord::RecordNotFound => e
|
||||
[e.message]
|
||||
errors = ActiveModel::Errors.new(self)
|
||||
errors.add(:procedure_presentation, e.message)
|
||||
errors
|
||||
end
|
||||
|
||||
if errors.present?
|
||||
|
|
|
@ -38,8 +38,9 @@ class Column
|
|||
end
|
||||
|
||||
def notifications? = [table, column] == ['notifications', 'notifications']
|
||||
|
||||
def dossier_state? = [table, column] == ['self', 'state']
|
||||
def groupe_instructeur? = [table, column] == ['groupe_instructeur', 'id']
|
||||
def type_de_champ? = table == TYPE_DE_CHAMP_TABLE
|
||||
|
||||
def self.find(h_id)
|
||||
begin
|
||||
|
|
|
@ -33,7 +33,7 @@ class FilteredColumn
|
|||
private
|
||||
|
||||
def check_filter_max_length
|
||||
if @filter.present? && @filter.length.to_i > FILTERS_VALUE_MAX_LENGTH
|
||||
if @filter.present? && @filter.length > FILTERS_VALUE_MAX_LENGTH
|
||||
errors.add(
|
||||
:base,
|
||||
"Le filtre « #{label} » est trop long (maximum: #{FILTERS_VALUE_MAX_LENGTH} caractères)"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProcedurePresentation < ApplicationRecord
|
||||
TYPE_DE_CHAMP = 'type_de_champ'
|
||||
|
||||
belongs_to :assign_to, optional: false
|
||||
has_many :exports, dependent: :destroy
|
||||
|
||||
|
@ -42,45 +40,4 @@ class ProcedurePresentation < ApplicationRecord
|
|||
columns.concat(procedure.sva_svr_columns) if procedure.sva_svr_enabled?
|
||||
columns
|
||||
end
|
||||
|
||||
def human_value_for_filter(filtered_column)
|
||||
if filtered_column.column.table == TYPE_DE_CHAMP
|
||||
find_type_de_champ(filtered_column.column.column).dynamic_type.filter_to_human(filtered_column.filter)
|
||||
elsif filtered_column.column.column == 'state'
|
||||
if filtered_column.filter == 'pending_correction'
|
||||
Dossier.human_attribute_name("pending_correction.for_instructeur")
|
||||
else
|
||||
Dossier.human_attribute_name("state.#{filtered_column.filter}")
|
||||
end
|
||||
elsif filtered_column.column.table == 'groupe_instructeur' && filtered_column.column.column == 'id'
|
||||
instructeur.groupe_instructeurs
|
||||
.find { _1.id == filtered_column.filter.to_i }&.label || filtered_column.filter
|
||||
else
|
||||
column = procedure.columns.find { _1.table == filtered_column.column.table && _1.column == filtered_column.column.column }
|
||||
|
||||
if column.type == :date
|
||||
parsed_date = safe_parse_date(filtered_column.filter)
|
||||
|
||||
return parsed_date.present? ? I18n.l(parsed_date) : nil
|
||||
end
|
||||
|
||||
filtered_column.filter
|
||||
end
|
||||
end
|
||||
|
||||
def safe_parse_date(string)
|
||||
Date.parse(string)
|
||||
rescue Date::Error
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_type_de_champ(column)
|
||||
TypeDeChamp
|
||||
.joins(:revision_types_de_champ)
|
||||
.where(revision_types_de_champ: { revision_id: procedure.revisions })
|
||||
.order(created_at: :desc)
|
||||
.find_by(stable_id: column)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
= t('views.instructeurs.dossiers.filters.title')
|
||||
|
||||
- menu.with_form do
|
||||
= render Instructeurs::ColumnFilterComponent.new(procedure:, procedure_presentation:, statut:)
|
||||
= render Instructeurs::ColumnFilterComponent.new(procedure_presentation:, statut:)
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
- if current_filters.count > 0
|
||||
.fr-mb-2w
|
||||
- current_filters.group_by { |filter| filter.column.table }.each_with_index do |(table, filters), i|
|
||||
- if i > 0
|
||||
= " et "
|
||||
- filters.each_with_index do |filter, i|
|
||||
- if i > 0
|
||||
= " ou "
|
||||
= form_tag(add_filter_instructeur_procedure_path(procedure), class: 'inline') do
|
||||
- prefix = procedure_presentation.filters_name_for(statut)
|
||||
= hidden_field_tag "#{prefix}[]", ''
|
||||
- (current_filters - [filter]).each do |f|
|
||||
= hidden_field_tag "#{prefix}[][id]", f.column.id
|
||||
= hidden_field_tag "#{prefix}[][filter]", f.filter
|
||||
|
||||
= button_tag "#{filter.column.label.truncate(50)} : #{procedure_presentation.human_value_for_filter(filter)}",
|
||||
class: 'fr-tag fr-tag--dismiss fr-my-1w'
|
|
@ -70,7 +70,7 @@
|
|||
= render Dossiers::ExportDropdownComponent.new(procedure: @procedure, export_templates: current_instructeur.export_templates_for(@procedure), statut: @statut, count: @dossiers_count, class_btn: 'fr-btn--tertiary', export_url: method(:download_export_instructeur_procedure_path))
|
||||
|
||||
- if @filtered_sorted_paginated_ids.present? || @current_filters.count > 0
|
||||
= render partial: "dossiers_filter_tags", locals: { procedure: @procedure, procedure_presentation: @procedure_presentation, current_filters: @current_filters, statut: @statut }
|
||||
= render Instructeurs::FilterButtonsComponent.new(filters: @current_filters, procedure_presentation: @procedure_presentation, statut: @statut)
|
||||
|
||||
- batch_operation_component = Dossiers::BatchOperationComponent.new(statut: @statut, procedure: @procedure)
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
= turbo_stream.replace 'filter-component' do
|
||||
= render Instructeurs::ColumnFilterComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation, statut: @statut, column: @column)
|
||||
= render Instructeurs::ColumnFilterComponent.new(procedure_presentation: @procedure_presentation, statut: @statut, column: @column)
|
||||
|
|
|
@ -461,6 +461,12 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
|
||||
resources :procedure_presentation, only: [:update] do
|
||||
member do
|
||||
get 'refresh_column_filter'
|
||||
end
|
||||
end
|
||||
|
||||
resources :procedures, only: [:index, :show], param: :procedure_id do
|
||||
member do
|
||||
resources :archives, only: [:index, :create]
|
||||
|
@ -482,11 +488,13 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
|
||||
# TODO: to remove because of new procedure_presentation_controller
|
||||
patch 'update_displayed_fields'
|
||||
get 'update_sort' => 'procedures#update_sort', as: 'update_sort'
|
||||
post 'add_filter'
|
||||
post 'update_filter'
|
||||
get 'remove_filter'
|
||||
|
||||
get 'download_export'
|
||||
post 'download_export'
|
||||
get 'polling_last_export'
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Instructeurs::ColumnFilterComponent, type: :component do
|
||||
let(:component) { described_class.new(procedure:, procedure_presentation:, statut:, column:) }
|
||||
let(:component) { described_class.new(procedure_presentation:, statut:, column:) }
|
||||
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure, instructeurs: [instructeur]) }
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:procedure_id) { procedure.id }
|
||||
let(:procedure_presentation) { nil }
|
||||
let(:procedure_presentation) do
|
||||
groupe_instructeur = procedure.defaut_groupe_instructeur
|
||||
assign_to = create(:assign_to, instructeur:, groupe_instructeur:)
|
||||
assign_to.procedure_presentation_or_default_and_errors.first
|
||||
end
|
||||
|
||||
let(:statut) { nil }
|
||||
let(:column) { nil }
|
||||
|
||||
|
@ -19,33 +24,10 @@ describe Instructeurs::ColumnFilterComponent, type: :component do
|
|||
let(:non_filterable_column) { Column.new(procedure_id:, label: 'depose_since', table: 'self', column: 'depose_since', filterable: false) }
|
||||
let(:mocked_columns) { [filterable_column, non_filterable_column] }
|
||||
|
||||
before { allow(procedure).to receive(:columns).and_return(mocked_columns) }
|
||||
before { allow_any_instance_of(Procedure).to receive(:columns).and_return(mocked_columns) }
|
||||
|
||||
subject { component.filterable_columns_options }
|
||||
|
||||
it { is_expected.to eq([[filterable_column.label, filterable_column.id]]) }
|
||||
end
|
||||
|
||||
describe '.options_for_select_of_column' do
|
||||
subject { component.options_for_select_of_column }
|
||||
|
||||
context "column is groupe_instructeur" do
|
||||
let(:column) { double("Column", scope: nil, table: 'groupe_instructeur') }
|
||||
let!(:gi_2) { instructeur.groupe_instructeurs.create(label: 'gi2', procedure:) }
|
||||
let!(:gi_3) { instructeur.groupe_instructeurs.create(label: 'gi3', procedure: create(:procedure)) }
|
||||
|
||||
it { is_expected.to eq([['défaut', procedure.defaut_groupe_instructeur.id], ['gi2', gi_2.id]]) }
|
||||
end
|
||||
|
||||
context 'when column is dropdown' do
|
||||
let(:types_de_champ_public) { [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }] }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||
let(:drop_down_stable_id) { procedure.active_revision.types_de_champ.first.stable_id }
|
||||
let(:column) { Column.new(procedure_id:, table: 'type_de_champ', scope: nil, column: drop_down_stable_id) }
|
||||
|
||||
it 'find most recent tdc' do
|
||||
is_expected.to eq(['Paris', 'Lyon', 'Marseille'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Instructeurs::ColumnFilterValueComponent, type: :component do
|
||||
let(:component) { described_class.new(column:) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure) { create(:procedure, instructeurs: [instructeur]) }
|
||||
let(:procedure_id) { procedure.id }
|
||||
|
||||
before do
|
||||
allow(component).to receive(:current_instructeur).and_return(instructeur)
|
||||
end
|
||||
|
||||
describe '.options_for_select_of_column' do
|
||||
subject { component.send(:options_for_select_of_column) }
|
||||
|
||||
context "column is groupe_instructeur" do
|
||||
let(:column) { double("Column", scope: nil, table: 'groupe_instructeur', h_id: { procedure_id: }) }
|
||||
let!(:gi_2) { instructeur.groupe_instructeurs.create(label: 'gi2', procedure:) }
|
||||
let!(:gi_3) { instructeur.groupe_instructeurs.create(label: 'gi3', procedure: create(:procedure)) }
|
||||
|
||||
it { is_expected.to eq([['défaut', procedure.defaut_groupe_instructeur.id], ['gi2', gi_2.id]]) }
|
||||
end
|
||||
|
||||
context 'when column is dropdown' do
|
||||
let(:types_de_champ_public) { [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }] }
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
|
||||
let(:drop_down_stable_id) { procedure.active_revision.types_de_champ.first.stable_id }
|
||||
let(:column) { Column.new(procedure_id:, table: 'type_de_champ', scope: nil, column: drop_down_stable_id) }
|
||||
|
||||
it 'find most recent tdc' do
|
||||
is_expected.to eq(['Paris', 'Lyon', 'Marseille'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,88 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Instructeurs::FilterButtonsComponent, type: :component do
|
||||
let(:component) { described_class.new(filters:, procedure_presentation:, statut:) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:assign_to) { create(:assign_to, procedure:, instructeur:) }
|
||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
||||
let(:statut) { 'tous' }
|
||||
let(:filters) { [filter] }
|
||||
|
||||
def to_filter((label, filter)) = FilteredColumn.new(column: procedure.find_column(label: label), filter: filter)
|
||||
|
||||
before { render_inline(component) }
|
||||
|
||||
describe "visible text" do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :text }]) }
|
||||
let(:first_type_de_champ) { procedure.active_revision.types_de_champ_public.first }
|
||||
let(:filter) { to_filter([first_type_de_champ.libelle, "true"]) }
|
||||
|
||||
context 'when type_de_champ text' do
|
||||
it 'should passthrough value' do
|
||||
expect(page).to have_text("true")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when type_de_champ yes_no' do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :yes_no }]) }
|
||||
|
||||
it 'should transform value' do
|
||||
expect(page).to have_text("oui")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filter is state' do
|
||||
let(:filter) { to_filter(['État du dossier', "en_construction"]) }
|
||||
|
||||
it 'should get i18n value' do
|
||||
expect(page).to have_text("En construction")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filter is a date' do
|
||||
let(:filter) { to_filter(['Date de création', "15/06/2023"]) }
|
||||
|
||||
it 'should get formatted value' do
|
||||
expect(page).to have_text("15/06/2023")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are multiple filters' do
|
||||
let(:filters) do
|
||||
[
|
||||
to_filter(['État du dossier', "en_construction"]),
|
||||
to_filter(['État du dossier', "en_instruction"]),
|
||||
to_filter(['Date de création', "15/06/2023"])
|
||||
]
|
||||
end
|
||||
|
||||
it 'should display all filters' do
|
||||
text = "État du dossier : En construction ou État du dossier : En instruction et Date de création : 15/06/2023"
|
||||
expect(page).to have_text(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "hidden inputs" do
|
||||
let(:procedure) { create(:procedure) }
|
||||
|
||||
context 'with 2 filters' do
|
||||
let(:en_construction_filter) { to_filter(['État du dossier', "en_construction"]) }
|
||||
let(:en_instruction_filter) { to_filter(['État du dossier', "en_instruction"]) }
|
||||
let(:column_id) { procedure.find_column(label: 'État du dossier').id }
|
||||
let(:filters) { [en_construction_filter, en_instruction_filter] }
|
||||
|
||||
it 'should have the necessary inputs' do
|
||||
expect(page).to have_field('statut', with: 'tous', type: 'hidden')
|
||||
|
||||
expect(page.all('form').count).to eq(2)
|
||||
|
||||
del_en_construction = page.all('form').first
|
||||
expect(del_en_construction).to have_text('En construction')
|
||||
expect(del_en_construction).to have_field('filters[]', with: '', type: 'hidden')
|
||||
expect(del_en_construction).to have_field('filters[][id]', with: column_id, type: 'hidden')
|
||||
expect(del_en_construction).to have_field('filters[][filter]', with: 'en_instruction', type: 'hidden')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,86 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Instructeurs::ProcedurePresentationController, type: :controller do
|
||||
describe '#update' do
|
||||
subject { patch :update, params: }
|
||||
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:procedure_presentation) do
|
||||
groupe_instructeur = procedure.defaut_groupe_instructeur
|
||||
assign_to = create(:assign_to, instructeur:, groupe_instructeur:)
|
||||
assign_to.procedure_presentation_or_default_and_errors.first
|
||||
end
|
||||
let(:state_column) { procedure.dossier_state_column }
|
||||
|
||||
let(:params) { { id: procedure_presentation.id }.merge(presentation_params) }
|
||||
|
||||
context 'nominal case' do
|
||||
before { sign_in(instructeur.user) }
|
||||
|
||||
let(:presentation_params) do
|
||||
{
|
||||
displayed_columns: [state_column.id],
|
||||
sorted_column: { order: 'asc', id: state_column.id },
|
||||
filters: [{ id: state_column.id, filter: 'en_construction' }],
|
||||
statut: 'tous'
|
||||
}
|
||||
end
|
||||
|
||||
it 'updates the procedure_presentation' do
|
||||
expect(procedure_presentation.displayed_columns).to eq(procedure.default_displayed_columns)
|
||||
expect(procedure_presentation.sorted_column).to eq(procedure.default_sorted_column)
|
||||
expect(procedure_presentation.tous_filters).to eq([])
|
||||
|
||||
subject
|
||||
expect(response).to redirect_to(instructeur_procedure_url(procedure))
|
||||
|
||||
procedure_presentation.reload
|
||||
|
||||
expect(procedure_presentation.displayed_columns).to eq([state_column])
|
||||
|
||||
expect(procedure_presentation.sorted_column.column).to eq(state_column)
|
||||
expect(procedure_presentation.sorted_column.order).to eq('asc')
|
||||
|
||||
filtered_column = FilteredColumn.new(column: state_column, filter: 'en_construction')
|
||||
expect(procedure_presentation.tous_filters).to eq([filtered_column])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a wrong instructeur' do
|
||||
let(:another_instructeur) { create(:instructeur) }
|
||||
before { sign_in(another_instructeur.user) }
|
||||
|
||||
let(:presentation_params) { { displayed_columns: [state_column.id] } }
|
||||
|
||||
it 'does not update the procedure_presentation' do
|
||||
expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an empty string in displayed_columns' do
|
||||
before { sign_in(instructeur.user) }
|
||||
|
||||
let(:presentation_params) { { displayed_columns: [''] } }
|
||||
|
||||
it 'removes the empty string' do
|
||||
subject
|
||||
expect(procedure_presentation.reload.displayed_columns).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an error in filters' do
|
||||
before { sign_in(instructeur.user) }
|
||||
|
||||
let(:presentation_params) do
|
||||
{ filters: [{ id: state_column.id, filter: '' }], statut: 'tous' }
|
||||
end
|
||||
|
||||
it 'does not update the procedure_presentation' do
|
||||
subject
|
||||
|
||||
expect(flash.alert).to include(/ne peut pas être vide/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45,7 +45,7 @@ describe AssignTo, type: :model do
|
|||
it do
|
||||
expect(procedure_presentation_or_default).to be_persisted
|
||||
expect(procedure_presentation_or_default).to be_valid
|
||||
expect(errors).to be_present
|
||||
expect(errors.full_messages).to include(/unable to find procedure 666/)
|
||||
expect(assign_to.procedure_presentation).not_to be(procedure_presentation)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,44 +55,6 @@ describe ProcedurePresentation do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#human_value_for_filter" do
|
||||
let(:filtered_column) { to_filter([first_type_de_champ.libelle, "true"]) }
|
||||
|
||||
subject do
|
||||
procedure_presentation.human_value_for_filter(filtered_column)
|
||||
end
|
||||
|
||||
context 'when type_de_champ text' do
|
||||
it 'should passthrough value' do
|
||||
expect(subject).to eq("true")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when type_de_champ yes_no' do
|
||||
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :yes_no }]) }
|
||||
|
||||
it 'should transform value' do
|
||||
expect(subject).to eq("oui")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filter is state' do
|
||||
let(:filtered_column) { to_filter(['État du dossier', "en_construction"]) }
|
||||
|
||||
it 'should get i18n value' do
|
||||
expect(subject).to eq("En construction")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when filter is a date' do
|
||||
let(:filtered_column) { to_filter(['Date de création', "15/06/2023"]) }
|
||||
|
||||
it 'should get formatted value' do
|
||||
expect(subject).to eq("15/06/2023")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_displayed_fields' do
|
||||
let(:en_construction_column) { procedure.find_column(label: 'Date de passage en construction') }
|
||||
let(:mise_a_jour_column) { procedure.find_column(label: 'Date du dernier évènement') }
|
||||
|
|
|
@ -19,7 +19,7 @@ describe "procedure filters" do
|
|||
|
||||
scenario "should display demandeur by default" do
|
||||
within ".dossiers-table" do
|
||||
expect(page).to have_link("Demandeur")
|
||||
expect(page).to have_button("Demandeur")
|
||||
expect(page).to have_link(new_unfollow_dossier.user.email)
|
||||
end
|
||||
end
|
||||
|
@ -28,7 +28,7 @@ describe "procedure filters" do
|
|||
procedure.update!(sva_svr: SVASVRConfiguration.new(decision: :sva).attributes)
|
||||
visit instructeur_procedure_path(procedure)
|
||||
within ".dossiers-table" do
|
||||
expect(page).to have_link("Date décision SVA")
|
||||
expect(page).to have_button("Date décision SVA")
|
||||
expect(page).to have_link(new_unfollow_dossier.user.email)
|
||||
end
|
||||
end
|
||||
|
@ -46,7 +46,7 @@ describe "procedure filters" do
|
|||
scenario "should add be able to add created_at column", js: true do
|
||||
add_column("Date de création")
|
||||
within ".dossiers-table" do
|
||||
expect(page).to have_link("Date de création")
|
||||
expect(page).to have_button("Date de création")
|
||||
expect(page).to have_link(new_unfollow_dossier.created_at.strftime('%d/%m/%Y'))
|
||||
end
|
||||
end
|
||||
|
@ -54,20 +54,20 @@ describe "procedure filters" do
|
|||
scenario "should add be able to add and remove custom type_de_champ column", js: true do
|
||||
add_column(type_de_champ.libelle)
|
||||
within ".dossiers-table" do
|
||||
expect(page).to have_link(type_de_champ.libelle)
|
||||
expect(page).to have_button(type_de_champ.libelle)
|
||||
expect(page).to have_link(champ.value)
|
||||
end
|
||||
|
||||
remove_column(type_de_champ.libelle)
|
||||
within ".dossiers-table" do
|
||||
expect(page).not_to have_link(type_de_champ.libelle)
|
||||
expect(page).not_to have_button(type_de_champ.libelle)
|
||||
expect(page).not_to have_link(champ.value)
|
||||
end
|
||||
|
||||
# Test removal of all customizable fields
|
||||
remove_column("Demandeur")
|
||||
within ".dossiers-table" do
|
||||
expect(page).not_to have_link("Demandeur")
|
||||
expect(page).not_to have_button("Demandeur")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,12 +22,12 @@ describe "procedure sort", js: true do
|
|||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
|
||||
find("thead .number-col a").click # sort by id asc
|
||||
click_on "Nº dossier" # sort by id asc
|
||||
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
|
||||
find("thead .number-col a").click # reverse order - sort by id desc
|
||||
click_on "Nº dossier" # reverse order - sort by id desc
|
||||
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
|
@ -44,12 +44,14 @@ describe "procedure sort", js: true do
|
|||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
|
||||
find("thead .sva-col a").click # sort by sva date asc
|
||||
click_on "Date décision SVA", exact: true # sort by sva date asc
|
||||
# find("thead .sva-col a").click # sort by sva date asc
|
||||
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
|
||||
find("thead .sva-col a").click # reverse order - sort by sva date desc
|
||||
click_on "Date décision SVA ↑", exact: true # reverse order - sort by sva date desc
|
||||
# find("thead .sva-col a").click # reverse order - sort by sva date desc
|
||||
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
|
@ -74,7 +76,7 @@ describe "procedure sort", js: true do
|
|||
end
|
||||
|
||||
scenario "should be able to sort back by notification filter after any other sort" do
|
||||
find("thead .number-col a").click # sort by id asc
|
||||
click_on "Nº dossier" # sort by id asc
|
||||
|
||||
expect(page).not_to have_checked_field("Remonter les dossiers avec une notification")
|
||||
|
||||
|
|
Loading…
Reference in a new issue