Merge pull request #10117 from tchak/refactor-readable-champ-project

refactor(champs): if champ not found - build it
This commit is contained in:
Paul Chavard 2024-03-18 05:28:46 +00:00 committed by GitHub
commit 659f15fcd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 129 additions and 120 deletions

View file

@ -3,15 +3,17 @@ class Dossiers::ChampsRowsShowComponent < ApplicationComponent
attr_reader :seen_at attr_reader :seen_at
def initialize(champs:, profile:, seen_at:) def initialize(champs:, profile:, seen_at:)
@champs = champs @champs, @profile, @seen_at = champs, profile, seen_at
@seen_at = seen_at
@profile = profile
end end
def updated_after_deposer?(champ) private
return false if champ.dossier.depose_at.blank?
champ.updated_at > champ.dossier.depose_at def updated_at_after_deposer(champ)
return if champ.dossier.depose_at.blank?
if champ.updated_at > champ.dossier.depose_at
champ.updated_at
end
end end
def number_with_html_delimiter(num) def number_with_html_delimiter(num)

View file

@ -1,14 +1,13 @@
- each_champ do |champ| - each_champ do |champ|
- if champ.repetition? - if champ.repetition?
- types_de_champ = champ.dossier.revision.children_of(champ.type_de_champ) - types_de_champ = champ.dossier.revision.children_of(champ.type_de_champ)
- champs_by_stable_id_with_row = champ.dossier.champs_by_stable_id_with_row - champ.row_ids.each.with_index(1) do |row_id, row_number|
- champ.row_ids.each.with_index do |row_id, i|
.fr-background-alt--grey.fr-p-2w.fr-my-3w.fr-ml-2w.champ-repetition .fr-background-alt--grey.fr-p-2w.fr-my-3w.fr-ml-2w.champ-repetition
%p.font-weight-bold= "#{champ.libelle} #{i + 1} :" %p.font-weight-bold= "#{champ.libelle} #{row_number} :"
= render ViewableChamp::SectionComponent.new(types_de_champ:, champs_by_stable_id_with_row:, row_id:, demande_seen_at: seen_at, profile:) = render ViewableChamp::SectionComponent.new(dossier: champ.dossier, types_de_champ:, row_id:, demande_seen_at: seen_at, profile:)
- else - else
= render Dossiers::RowShowComponent.new(label: champ.libelle, seen_at:, profile:, content_class: champ.type_champ, updated_at: updated_after_deposer?(champ) ? champ.updated_at : nil) do |c| = render Dossiers::RowShowComponent.new(label: champ.libelle, seen_at:, profile:, content_class: champ.type_champ, updated_at: updated_at_after_deposer(champ)) do |c|
- if champ.blank? - if champ.blank?
- c.with_blank do - c.with_blank do
= t(blank_key(champ)) = t(blank_key(champ))

View file

@ -5,9 +5,9 @@
.repetition{ id: dom_id(@champ, :rows) } .repetition{ id: dom_id(@champ, :rows) }
- @champ.row_ids.each do |row_id| - @champ.row_ids.each.with_index(1) do |row_id, row_number|
= render EditableChamp::RepetitionRowComponent.new(form: @form, champ: @champ, row_id:, seen_at: @seen_at) = render EditableChamp::RepetitionRowComponent.new(form: @form, dossier: @champ.dossier, type_de_champ: @champ.type_de_champ, row_id:, row_number:, seen_at: @seen_at)
.actions .actions
= render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@champ.id), http_method: :create, opt: { class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-circle-line fr-mb-3w", title: t(".add_title", libelle: @champ.libelle), id: dom_id(@champ, :create_repetition)}) do = render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@champ.dossier, @champ.stable_id), http_method: :create, opt: { class: "fr-btn fr-btn--secondary fr-btn--icon-left fr-icon-add-circle-line fr-mb-3w", title: t(".add_title", libelle: @champ.libelle), id: dom_id(@champ, :create_repetition)}) do
= t(".add", libelle: @champ.libelle) = t(".add", libelle: @champ.libelle)

View file

@ -1,10 +1,7 @@
class EditableChamp::RepetitionRowComponent < ApplicationComponent class EditableChamp::RepetitionRowComponent < ApplicationComponent
def initialize(form:, champ:, row_id:, seen_at: nil) def initialize(form:, dossier:, type_de_champ:, row_id:, row_number:, seen_at: nil)
@form, @champ, @row_id, @seen_at = form, champ, row_id, seen_at @form, @dossier, @type_de_champ, @row_id, @row_number, @seen_at = form, dossier, type_de_champ, row_id, row_number, seen_at
@types_de_champ = dossier.revision.children_of(type_de_champ)
@types_de_champ = champ.dossier.revision.children_of(champ.type_de_champ)
@champs_by_stable_id_with_row = champ.dossier.champs_by_stable_id_with_row
@row_number = champ.row_ids.find_index(row_id)
end end
attr_reader :row_id, :row_number attr_reader :row_id, :row_number
@ -12,6 +9,6 @@ class EditableChamp::RepetitionRowComponent < ApplicationComponent
private private
def section_component def section_component
EditableChamp::SectionComponent.new(types_de_champ: @types_de_champ, champs_by_stable_id_with_row: @champs_by_stable_id_with_row, row_id:) EditableChamp::SectionComponent.new(dossier: @dossier, types_de_champ: @types_de_champ, row_id:)
end end
end end

View file

@ -1,11 +1,11 @@
.row{ id: "safe-row-selector-#{row_id}" } .row{ id: "safe-row-selector-#{row_id}" }
- if @types_de_champ.size > 1 - if @types_de_champ.size > 1
%fieldset %fieldset
%legend.block-id= "#{@champ.libelle} " %legend.block-id= "#{@type_de_champ.libelle} "
= render section_component = render section_component
- else - else
= render section_component = render section_component
.flex.row-reverse .flex.row-reverse
= render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@champ.id, row_id:), http_method: :delete, opt: { class: "fr-btn fr-btn--sm fr-btn--tertiary fr-text-action-high--red-marianne", title: t(".delete_title", row_number:)}) do = render NestedForms::OwnedButtonComponent.new(formaction: champs_repetition_path(@dossier, @type_de_champ.stable_id, row_id:), http_method: :delete, opt: { class: "fr-btn fr-btn--sm fr-btn--tertiary fr-text-action-high--red-marianne", title: t(".delete_title", row_number:)}) do
= t(".delete") = t(".delete")

View file

@ -2,9 +2,9 @@ class EditableChamp::SectionComponent < ApplicationComponent
include ApplicationHelper include ApplicationHelper
include TreeableConcern include TreeableConcern
def initialize(nodes: nil, types_de_champ: nil, row_id: nil, champs_by_stable_id_with_row:) def initialize(dossier:, nodes: nil, types_de_champ: nil, row_id: nil)
nodes ||= to_tree(types_de_champ:) nodes ||= to_tree(types_de_champ:)
@champs_by_stable_id_with_row = champs_by_stable_id_with_row @dossier = dossier
@row_id = row_id @row_id = row_id
@nodes = to_fieldset(nodes:) @nodes = to_fieldset(nodes:)
end end
@ -15,7 +15,7 @@ class EditableChamp::SectionComponent < ApplicationComponent
def header_section def header_section
node = @nodes.first node = @nodes.first
champ_for_type_de_champ(node) if node.is_a?(TypeDeChamp) && node.header_section? @dossier.project_champ(node, @row_id) if node.is_a?(TypeDeChamp) && node.header_section?
end end
def splitted_tail def splitted_tail
@ -38,21 +38,17 @@ class EditableChamp::SectionComponent < ApplicationComponent
when EditableChamp::SectionComponent when EditableChamp::SectionComponent
[node, nil] [node, nil]
else else
[nil, champ_for_type_de_champ(node)] [nil, @dossier.project_champ(node, @row_id)]
end end
end end
private private
def to_fieldset(nodes:) def to_fieldset(nodes:)
nodes.map { _1.is_a?(Array) ? EditableChamp::SectionComponent.new(nodes: _1, row_id: @row_id, champs_by_stable_id_with_row: @champs_by_stable_id_with_row) : _1 } nodes.map { _1.is_a?(Array) ? EditableChamp::SectionComponent.new(dossier: @dossier, nodes: _1, row_id: @row_id) : _1 }
end end
def first_champ_is_an_header_section? def first_champ_is_an_header_section?
header_section.present? header_section.present?
end end
def champ_for_type_de_champ(type_de_champ)
@champs_by_stable_id_with_row[[@row_id, type_de_champ.stable_id].compact]
end
end end

View file

@ -2,25 +2,25 @@ class ViewableChamp::SectionComponent < ApplicationComponent
include ApplicationHelper include ApplicationHelper
include TreeableConcern include TreeableConcern
def initialize(nodes: nil, types_de_champ: nil, row_id: nil, demande_seen_at:, profile:, champs_by_stable_id_with_row:) def initialize(dossier:, nodes: nil, types_de_champ: nil, row_id: nil, demande_seen_at:, profile:)
@demande_seen_at, @profile, @row_id, @champs_by_stable_id_with_row = demande_seen_at, profile, row_id, champs_by_stable_id_with_row @dossier, @demande_seen_at, @profile, @row_id = dossier, demande_seen_at, profile, row_id
nodes ||= to_tree(types_de_champ:) nodes ||= to_tree(types_de_champ:)
@nodes = to_sections(nodes:) @nodes = to_sections(nodes:)
end end
private
def section_id def section_id
@section_id ||= header_section ? dom_id(header_section, :content) : SecureRandom.uuid @section_id ||= header_section ? dom_id(header_section, :content) : SecureRandom.uuid
end end
def header_section def header_section
maybe_header_section = @nodes.first node = @nodes.first
if maybe_header_section.is_a?(TypeDeChamp) && maybe_header_section.header_section? @dossier.project_champ(node, @row_id) if node.is_a?(TypeDeChamp) && node.header_section?
champ_for_type_de_champ(maybe_header_section)
end
end end
def champs def champs
tail.filter_map { _1.is_a?(TypeDeChamp) ? champ_for_type_de_champ(_1) : nil } tail.filter_map { _1.is_a?(TypeDeChamp) ? @dossier.project_champ(_1, @row_id) : nil }
end end
def sections def sections
@ -28,14 +28,14 @@ class ViewableChamp::SectionComponent < ApplicationComponent
end end
def tail def tail
return @nodes if header_section.blank? return @nodes if header_section.nil?
_, *rest_of_champ = @nodes _, *rest_of_champ = @nodes
rest_of_champ rest_of_champ
end end
def reset_tag_for_depth def reset_tag_for_depth
return if !header_section return if header_section.nil?
"reset-h#{header_section.level + 1}" "reset-h#{header_section.level + 1}"
end end
@ -49,10 +49,6 @@ class ViewableChamp::SectionComponent < ApplicationComponent
private private
def to_sections(nodes:) def to_sections(nodes:)
nodes.map { _1.is_a?(Array) ? ViewableChamp::SectionComponent.new(nodes: _1, demande_seen_at: @demande_seen_at, profile: @profile, champs_by_stable_id_with_row: @champs_by_stable_id_with_row, row_id: @row_id) : _1 } nodes.map { _1.is_a?(Array) ? ViewableChamp::SectionComponent.new(dossier: @dossier, nodes: _1, demande_seen_at: @demande_seen_at, profile: @profile, row_id: @row_id) : _1 }
end
def champ_for_type_de_champ(type_de_champ)
@champs_by_stable_id_with_row[[@row_id, type_de_champ.stable_id].compact]
end end
end end

View file

@ -1,14 +1,14 @@
= tag.div(class: class_names(reset_tag_for_depth => true, "fr-my-2w" => !first_level?), 'data-controller': 'expand') do = tag.div(class: class_names(reset_tag_for_depth => true, "fr-my-2w" => !first_level?), 'data-controller': 'expand') do
- if header_section - if header_section
%div{ class: class_names(flex: true, "top-bordered" => first_level?) } %div{ class: class_names(flex: true, "top-bordered" => first_level?) }
= render EditableChamp::HeaderSectionComponent.new(champ: header_section, html_class: {' fr-m-0 fr-text--md fr-px-4v flex-grow' => true, "fr-text-action-high--blue-france" => header_section.level == 1, 'fr-py-2w' => first_level?, 'fr-py-1v' => !first_level?}) = render EditableChamp::HeaderSectionComponent.new(champ: header_section, html_class: { 'fr-m-0 fr-text--md fr-px-4v flex-grow': true, "fr-text-action-high--blue-france fr-py-2w": first_level?, 'fr-py-1v': !first_level? })
- if ![champs, sections].map(&:empty?).all? && first_level? - if ![champs, sections].map(&:empty?).all? && first_level?
%button{ type: "button", aria: { controls: section_id, "expanded": "true", label: t('.toggle_section', section: header_section.libelle) }, href: section_id, 'data-action': 'click->expand#toggle', class: "fr-btn fr-btn--tertiary-no-outline" } %button{ type: "button", aria: { controls: section_id, "expanded": "true", label: t('.toggle_section', section: header_section.libelle) }, href: section_id, 'data-action': 'click->expand#toggle', class: "fr-btn fr-btn--tertiary-no-outline" }
%i.fr-icon-arrow-up-s-line{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } %i.fr-icon-arrow-up-s-line{ 'aria-hidden': 'true', 'data-expand-target': 'icon' }
%div{ id: section_id, 'data-expand-target': 'content' } %div{ id: section_id, 'data-expand-target': 'content' }
- if !champs.empty? - if !champs.empty?
= render Dossiers::ChampsRowsShowComponent.new(champs: champs, seen_at: @demande_seen_at, profile: @profile) = render Dossiers::ChampsRowsShowComponent.new(champs:, seen_at: @demande_seen_at, profile: @profile)
- sections.each do |section| - sections.each do |section|
= render section = render section

View file

@ -2,16 +2,29 @@ class Champs::RepetitionController < ApplicationController
before_action :authenticate_logged_user! before_action :authenticate_logged_user!
def add def add
@champ = policy_scope(Champ).includes(:champs).find(params[:champ_id]) @champ = find_champ
row = @champ.add_row(@champ.dossier.revision) row = @champ.add_row(@champ.dossier.revision)
@first_champ_id = row.map(&:focusable_input_id).compact.first @first_champ_id = row.map(&:focusable_input_id).compact.first
@row_id = row.first&.row_id @row_id = row.first&.row_id
@row_number = @champ.row_ids.find_index(@row_id) + 1
end end
def remove def remove
@champ = policy_scope(Champ).includes(:champs).find(params[:champ_id]) @champ = find_champ
@champ.champs.where(row_id: params[:row_id]).destroy_all @champ.champs.where(row_id: params[:row_id]).destroy_all
@champ.reload @champ.reload
@row_id = params[:row_id] @row_id = params[:row_id]
end end
private
def find_champ
if params[:champ_id].present?
policy_scope(Champ).includes(:champs).find(params[:champ_id])
else
policy_scope(Champ)
.includes(:champs, :type_de_champ)
.find_by!(dossier_id: params[:dossier_id], type_de_champ: { stable_id: params[:stable_id] })
end
end
end end

View file

@ -160,7 +160,7 @@ class Champ < ApplicationRecord
end end
def input_group_id def input_group_id
"champ-#{html_id}" html_id
end end
def input_id def input_id
@ -258,7 +258,7 @@ class Champ < ApplicationRecord
end end
def html_id def html_id
"champ-#{stable_id}-#{id}" row_id.present? ? "champ-#{stable_id}-#{row_id}" : "champ-#{stable_id}"
end end
def needs_dossier_id? def needs_dossier_id?

View file

@ -1,14 +1,4 @@
class Champs::HeaderSectionChamp < Champ class Champs::HeaderSectionChamp < Champ
def level
if parent.present?
header_section_level_value.to_i + parent.current_section_level(dossier.revision)
elsif header_section_level_value
header_section_level_value.to_i
else
0
end
end
def search_terms def search_terms
# The user cannot enter any information here so it doesnt make much sense to search # The user cannot enter any information here so it doesnt make much sense to search
end end
@ -16,4 +6,8 @@ class Champs::HeaderSectionChamp < Champ
def libelle_with_section_index? def libelle_with_section_index?
libelle =~ /^\d/ libelle =~ /^\d/
end end
def level
type_de_champ.level_for_revision(dossier.revision)
end
end end

View file

@ -42,7 +42,7 @@ class Champs::RepetitionChamp < Champ
def rows_for_export def rows_for_export
champs = dossier.champs_by_stable_id_with_row champs = dossier.champs_by_stable_id_with_row
row_ids.each.with_index(1).map do |row_id, index| row_ids.map.with_index(1) do |row_id, index|
Champs::RepetitionChamp::Row.new(index: index, row_id:, dossier_id: dossier_id.to_s, champs:) Champs::RepetitionChamp::Row.new(index: index, row_id:, dossier_id: dossier_id.to_s, champs:)
end end
end end

View file

@ -1410,6 +1410,16 @@ class Dossier < ApplicationRecord
revision.revision_types_de_champ_private.present? revision.revision_types_de_champ_private.present?
end end
def project_champ(type_de_champ, row_id)
stable_id_with_row = [row_id, type_de_champ.stable_id].compact
champ = champs.find { _1.stable_id_with_row == stable_id_with_row }
if champ.nil?
type_de_champ.build_champ(dossier: self, row_id:)
else
champ
end
end
private private
def create_missing_traitemets def create_missing_traitemets

View file

@ -145,7 +145,7 @@ class TypeDeChamp < ApplicationRecord
has_one :revision, through: :revision_type_de_champ has_one :revision, through: :revision_type_de_champ
has_one :procedure, through: :revision has_one :procedure, through: :revision
delegate :estimated_fill_duration, :estimated_read_duration, :tags_for_template, :libelle_for_export, to: :dynamic_type delegate :estimated_fill_duration, :estimated_read_duration, :tags_for_template, :libelle_for_export, :primary_options, :secondary_options, to: :dynamic_type
delegate :used_by_routing_rules?, to: :revision_type_de_champ delegate :used_by_routing_rules?, to: :revision_type_de_champ
class WithIndifferentAccess class WithIndifferentAccess
@ -167,8 +167,6 @@ class TypeDeChamp < ApplicationRecord
attr_reader :dynamic_type attr_reader :dynamic_type
delegate :primary_options, :secondary_options, to: :dynamic_type
scope :public_only, -> { where(private: false) } scope :public_only, -> { where(private: false) }
scope :private_only, -> { where(private: true) } scope :private_only, -> { where(private: true) }
scope :repetition, -> { where(type_champ: type_champs.fetch(:repetition)) } scope :repetition, -> { where(type_champ: type_champs.fetch(:repetition)) }
@ -526,6 +524,17 @@ class TypeDeChamp < ApplicationRecord
previous_section_level(tdcs.take(tdcs.find_index(self))) previous_section_level(tdcs.take(tdcs.find_index(self)))
end end
def level_for_revision(revision)
rtdc = revision.revision_types_de_champ.find { |rtdc| rtdc.stable_id == stable_id }
if rtdc.child?
header_section_level_value.to_i + rtdc.parent.type_de_champ.current_section_level(revision)
elsif header_section_level_value
header_section_level_value.to_i
else
0
end
end
def self.filter_hash_type(type_champ) def self.filter_hash_type(type_champ)
if is_choice_type_from(type_champ) if is_choice_type_from(type_champ)
:enum :enum

View file

@ -1,5 +1,5 @@
- if @row_id.present? - if @row_id.present?
= fields_for @champ.input_name, @champ do |form| = fields_for @champ.input_name, @champ do |form|
= turbo_stream.append dom_id(@champ, :rows), render(EditableChamp::RepetitionRowComponent.new(form: form, champ: @champ, row_id: @row_id)) = turbo_stream.append dom_id(@champ, :rows), render(EditableChamp::RepetitionRowComponent.new(form: form, dossier: @champ.dossier, type_de_champ: @champ.type_de_champ, row_id: @row_id, row_number: @row_number))
- if @first_champ_id - if @first_champ_id
= turbo_stream.focus(@first_champ_id) = turbo_stream.focus(@first_champ_id)

View file

@ -15,13 +15,13 @@
- types_de_champ_public = @dossier.revision.types_de_champ_public - types_de_champ_public = @dossier.revision.types_de_champ_public
- if types_de_champ_public.any? || @dossier.procedure.routing_enabled? - if types_de_champ_public.any? || @dossier.procedure.routing_enabled?
= render partial: "shared/dossiers/champs", locals: { types_de_champ: types_de_champ_public, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' } = render ViewableChamp::SectionComponent.new(dossier: @dossier, types_de_champ: types_de_champ_public, demande_seen_at: nil, profile: 'instructeur')
%h2 Annotations privées %h2 Annotations privées
- types_de_champ_private = @dossier.revision.types_de_champ_private - types_de_champ_private = @dossier.revision.types_de_champ_private
- if types_de_champ_private.any? - if types_de_champ_private.any?
= render partial: "shared/dossiers/champs", locals: { types_de_champ: types_de_champ_private, dossier: @dossier, demande_seen_at: nil, profile: 'instructeur' } = render ViewableChamp::SectionComponent.new(dossier: @dossier, types_de_champ: types_de_champ_private, demande_seen_at: nil, profile: 'instructeur')
- else - else
Aucune annotation privée Aucune annotation privée

View file

@ -33,7 +33,7 @@
%form.form %form.form
= form_for @dossier, url: '', html: { class: 'form' } do |f| = form_for @dossier, url: '', html: { class: 'form' } do |f|
= render EditableChamp::SectionComponent.new(types_de_champ: @dossier.revision.types_de_champ_public, champs_by_stable_id_with_row: @dossier.champs_by_stable_id_with_row) = render EditableChamp::SectionComponent.new(dossier: @dossier, types_de_champ: @dossier.revision.types_de_champ_public)
.editable-champ.editable-champ-text .editable-champ.editable-champ-text
%label Mot de passe %label Mot de passe

View file

@ -1 +0,0 @@
= render ViewableChamp::SectionComponent.new(types_de_champ:, champs_by_stable_id_with_row: dossier.champs_by_stable_id_with_row, demande_seen_at:, profile:)

View file

@ -53,4 +53,4 @@
- types_de_champ = dossier.revision.types_de_champ_public - types_de_champ = dossier.revision.types_de_champ_public
- if types_de_champ.any? || dossier.procedure.routing_enabled? - if types_de_champ.any? || dossier.procedure.routing_enabled?
= render partial: "shared/dossiers/champs", locals: { types_de_champ:, dossier:, demande_seen_at:, profile: } = render ViewableChamp::SectionComponent.new(dossier:, types_de_champ:, demande_seen_at:, profile:)

View file

@ -21,7 +21,7 @@
= render Procedure::NoticeComponent.new(procedure: dossier.procedure) = render Procedure::NoticeComponent.new(procedure: dossier.procedure)
= render EditableChamp::SectionComponent.new(types_de_champ: dossier_for_editing.revision.types_de_champ_public, champs_by_stable_id_with_row: dossier_for_editing.champs_by_stable_id_with_row) = render EditableChamp::SectionComponent.new(dossier: dossier_for_editing, types_de_champ: dossier_for_editing.revision.types_de_champ_public)
= render Dossiers::PendingCorrectionCheckboxComponent.new(dossier: dossier) = render Dossiers::PendingCorrectionCheckboxComponent.new(dossier: dossier)

View file

@ -3,7 +3,7 @@
%section.counter-start-header-section %section.counter-start-header-section
= render NestedForms::FormOwnerComponent.new = render NestedForms::FormOwnerComponent.new
= form_for dossier, url: annotations_instructeur_dossier_path(dossier.procedure, dossier), html: { class: 'form', multipart: true } do |f| = form_for dossier, url: annotations_instructeur_dossier_path(dossier.procedure, dossier), html: { class: 'form', multipart: true } do |f|
= render EditableChamp::SectionComponent.new(types_de_champ: dossier.revision.types_de_champ_private, champs_by_stable_id_with_row: dossier.champs_by_stable_id_with_row) = render EditableChamp::SectionComponent.new(dossier:, types_de_champ: dossier.revision.types_de_champ_private)
= render Dossiers::EditFooterComponent.new(dossier: dossier, annotation: true) = render Dossiers::EditFooterComponent.new(dossier: dossier, annotation: true)
- else - else

View file

@ -191,10 +191,13 @@ Rails.application.routes.draw do
end end
namespace :champs do namespace :champs do
post ':dossier_id/:stable_id/repetition', to: 'repetition#add', as: :repetition
delete ':dossier_id/:stable_id/repetition', to: 'repetition#remove'
post ':champ_id/repetition', to: 'repetition#add'
delete ':champ_id/repetition', to: 'repetition#remove'
get ':champ_id/siret', to: 'siret#show', as: :siret get ':champ_id/siret', to: 'siret#show', as: :siret
get ':champ_id/rna', to: 'rna#show', as: :rna get ':champ_id/rna', to: 'rna#show', as: :rna
post ':champ_id/repetition', to: 'repetition#add', as: :repetition
delete ':champ_id/repetition', to: 'repetition#remove'
delete ':champ_id/options', to: 'options#remove', as: :options delete ':champ_id/options', to: 'options#remove', as: :options
get ':champ_id/carte/features', to: 'carte#index', as: :carte_features get ':champ_id/carte/features', to: 'carte#index', as: :carte_features

View file

@ -1,12 +1,14 @@
describe EditableChamp::SectionComponent, type: :component do describe EditableChamp::SectionComponent, type: :component do
include TreeableConcern include TreeableConcern
let(:types_de_champ) { champs.map(&:type_de_champ) } let(:procedure) { create(:procedure, types_de_champ_public:) }
let(:champs_by_stable_id_with_row) { champs.index_by(&:stable_id_with_row) } let(:types_de_champ_public) { [] }
let(:component) { described_class.new(types_de_champ:, champs_by_stable_id_with_row:) } let(:dossier) { create(:dossier, :with_populated_champs, procedure:) }
let(:types_de_champ) { dossier.revision.types_de_champ_public }
let(:component) { described_class.new(types_de_champ:, dossier:) }
before { render_inline(component).to_html } before { render_inline(component).to_html }
context 'list of champs without an header_section' do context 'list of champs without an header_section' do
let(:champs) { [build(:champ_text), build(:champ_textarea)] } let(:types_de_champ_public) { [{ type: :text }, { type: :textarea }] }
it 'render in a fieldset' do it 'render in a fieldset' do
expect(page).to have_selector("fieldset", count: 1) expect(page).to have_selector("fieldset", count: 1)
@ -19,7 +21,7 @@ describe EditableChamp::SectionComponent, type: :component do
end end
context 'list of champs with an header_section' do context 'list of champs with an header_section' do
let(:champs) { [build(:champ_header_section_level_1), build(:champ_text), build(:champ_textarea)] } let(:types_de_champ_public) { [{ type: :header_section, level: 1 }, { type: :text }, { type: :textarea }] }
it 'renders fieldset' do it 'renders fieldset' do
expect(page).to have_selector("fieldset") expect(page).to have_selector("fieldset")
@ -33,7 +35,7 @@ describe EditableChamp::SectionComponent, type: :component do
end end
context 'list of champs without section and an header_section having champs' do context 'list of champs without section and an header_section having champs' do
let(:champs) { [build(:champ_text), build(:champ_header_section_level_1), build(:champ_text)] } let(:types_de_champ_public) { [{ type: :text }, { type: :header_section, level: 1 }, { type: :text }] }
it 'renders fieldset' do it 'renders fieldset' do
expect(page).to have_selector("fieldset", count: 2) expect(page).to have_selector("fieldset", count: 2)
@ -47,7 +49,7 @@ describe EditableChamp::SectionComponent, type: :component do
end end
context 'list of header_section without champs' do context 'list of header_section without champs' do
let(:champs) { [build(:champ_header_section_level_1), build(:champ_header_section_level_2), build(:champ_header_section_level_3)] } let(:types_de_champ_public) { [{ type: :header_section, level: 1 }, { type: :header_section, level: 2 }, { type: :header_section, level: 3 }] }
it 'render header within fieldset' do it 'render header within fieldset' do
expect(page).to have_selector("fieldset > legend", count: 3) expect(page).to have_selector("fieldset > legend", count: 3)
@ -58,7 +60,7 @@ describe EditableChamp::SectionComponent, type: :component do
end end
context 'header_section followed by explication and another fieldset' do context 'header_section followed by explication and another fieldset' do
let(:champs) { [build(:champ_header_section_level_1), build(:champ_explication), build(:champ_header_section_level_1), build(:champ_text)] } let(:types_de_champ_public) { [{ type: :header_section, level: 1 }, { type: :explication }, { type: :header_section, level: 1 }, { type: :text }] }
it 'render fieldset, header_section, also render explication' do it 'render fieldset, header_section, also render explication' do
expect(page).to have_selector("h2", count: 2) expect(page).to have_selector("h2", count: 2)
@ -69,7 +71,7 @@ describe EditableChamp::SectionComponent, type: :component do
end end
context 'nested fieldsset' do context 'nested fieldsset' do
let(:champs) { [build(:champ_header_section_level_1), build(:champ_text), build(:champ_header_section_level_2), build(:champ_textarea)] } let(:types_de_champ_public) { [{ type: :header_section, level: 1 }, { type: :text }, { type: :header_section, level: 2 }, { type: :textarea }] }
it 'render nested fieldsets' do it 'render nested fieldsets' do
expect(page).to have_selector("fieldset") expect(page).to have_selector("fieldset")
@ -85,22 +87,19 @@ describe EditableChamp::SectionComponent, type: :component do
end end
context 'with repetition' do context 'with repetition' do
let(:procedure) do let(:types_de_champ_public) do
create(:procedure, types_de_champ_public: [ [
{ type: :header_section, header_section_level: 1 }, { type: :header_section, level: 1 },
{ {
type: :repetition, type: :repetition,
libelle: 'repetition', libelle: 'repetition',
children: [ children: [
{ type: :header_section, header_section_level: 1, libelle: 'child_1' }, { type: :header_section, level: 1, libelle: 'child_1' },
{ type: :text, libelle: 'child_2' } { type: :text, libelle: 'child_2' }
] ]
} }
]) ]
end end
let(:dossier) { create(:dossier, :with_populated_champs, procedure: procedure) }
let(:champs) { dossier.champs_public }
let(:champs_by_stable_id_with_row) { dossier.champs_by_stable_id_with_row }
it 'render nested fieldsets, increase heading level for repetition header_section' do it 'render nested fieldsets, increase heading level for repetition header_section' do
expect(page).to have_selector("fieldset") expect(page).to have_selector("fieldset")
@ -137,24 +136,24 @@ describe EditableChamp::SectionComponent, type: :component do
end end
end end
let(:champs) { let(:types_de_champ_public) {
[ [
build(:champ_header_section_level_1), { type: :header_section, level: 1 },
build(:champ_header_section_level_2), { type: :header_section, level: 2 },
build(:champ_header_section_level_3), { type: :header_section, level: 3 },
build(:champ_integer_number), { type: :integer_number },
build(:champ_header_section_level_3), { type: :header_section, level: 3 },
build(:champ_yes_no), { type: :yes_no },
build(:champ_header_section_level_2), { type: :header_section, level: 2 },
build(:champ_header_section_level_3), { type: :header_section, level: 3 },
build(:champ_integer_number), { type: :integer_number },
build(:champ_header_section_level_1), { type: :header_section, level: 1 },
build(:champ_text), { type: :text },
build(:champ_header_section_level_2), { type: :header_section, level: 2 },
build(:champ_text) { type: :text }
] ]
} }

View file

@ -319,6 +319,10 @@ def build_types_de_champ(types_de_champ, revision:, scope: :public, parent: nil)
type_de_champ_attributes[:editable_options] = layers.index_with { '1' } type_de_champ_attributes[:editable_options] = layers.index_with { '1' }
end end
if type == :header_section
type_de_champ_attributes[:header_section_level] = type_de_champ_attributes.delete(:level)
end
type_de_champ = if scope == :private type_de_champ = if scope == :private
build(:"type_de_champ_#{type}", :private, no_coordinate: true, **type_de_champ_attributes) build(:"type_de_champ_#{type}", :private, no_coordinate: true, **type_de_champ_attributes)
else else

View file

@ -46,18 +46,6 @@ describe Dossier, type: :model do
end end
end end
describe 'with_champs' do
let(:procedure) { create(:procedure) }
let!(:tdc_1) { create(:type_de_champ, libelle: 'l1', position: 1, procedure: procedure) }
let!(:tdc_3) { create(:type_de_champ, libelle: 'l3', position: 3, procedure: procedure) }
let!(:tdc_2) { create(:type_de_champ, libelle: 'l2', position: 2, procedure: procedure) }
let(:dossier) { create(:dossier, procedure: procedure) }
it do
expect(Dossier.with_champs.find(dossier.id).champs_public.map(&:libelle)).to match(['l1', 'l2', 'l3'])
end
end
describe 'brouillon_close_to_expiration' do describe 'brouillon_close_to_expiration' do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) } let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, :en_construction, procedure: procedure) } let!(:young_dossier) { create(:dossier, :en_construction, procedure: procedure) }

View file

@ -15,7 +15,7 @@ describe 'shared/dossiers/champs', type: :view do
end end
end end
subject { render 'shared/dossiers/champs', types_de_champ:, dossier:, demande_seen_at:, profile: } subject { render ViewableChamp::SectionComponent.new(types_de_champ:, dossier:, demande_seen_at:, profile:) }
context "there are some champs" do context "there are some champs" do
let(:types_de_champ_public) { [{ type: :checkbox }, { type: :header_section }, { type: :explication }, { type: :dossier_link }, { type: :textarea }, { type: :rna }] } let(:types_de_champ_public) { [{ type: :checkbox }, { type: :header_section }, { type: :explication }, { type: :dossier_link }, { type: :textarea }, { type: :rna }] }