diff --git a/app/components/types_de_champ_editor/header_sections_summary_component.rb b/app/components/types_de_champ_editor/header_sections_summary_component.rb new file mode 100644 index 000000000..7f3db3a75 --- /dev/null +++ b/app/components/types_de_champ_editor/header_sections_summary_component.rb @@ -0,0 +1,16 @@ +class TypesDeChampEditor::HeaderSectionsSummaryComponent < ApplicationComponent + def initialize(procedure:, is_private:) + @procedure = procedure + @is_private = is_private + end + + def header_sections + @procedure.draft_revision + .send(@is_private ? :revision_types_de_champ_private : :revision_types_de_champ_public) + .filter { _1.type_de_champ.header_section? } + end + + def href(header_section) # used by type de champ editor to anchor elements + "##{dom_id(header_section, :type_de_champ_editor)}" + end +end diff --git a/app/views/administrateurs/procedures/_champs_summary.html.haml b/app/components/types_de_champ_editor/header_sections_summary_component/header_sections_summary_component.html.haml similarity index 72% rename from app/views/administrateurs/procedures/_champs_summary.html.haml rename to app/components/types_de_champ_editor/header_sections_summary_component/header_sections_summary_component.html.haml index 8c662691b..61b3036bc 100644 --- a/app/views/administrateurs/procedures/_champs_summary.html.haml +++ b/app/components/types_de_champ_editor/header_sections_summary_component/header_sections_summary_component.html.haml @@ -6,6 +6,6 @@ %li.fr-sidemenu__item - level = header.type_de_champ.header_section_level_value.to_i - if level == 1 - %a.fr-sidemenu__link{ href: "##{dom_id(header, :type_de_champ_editor)}" }= header.libelle + %a.fr-sidemenu__link{ href: href(header) }= header.libelle - else - %a.fr-sidemenu__link{ class: level >= 3 ? 'custom-link-grey': '', href: "##{dom_id(header, :type_de_champ_editor)}" }= "-- #{header.libelle}" + %a.fr-sidemenu__link{ class: level >= 3 ? 'custom-link-grey': '', href: href(header) }= "-- #{header.libelle}" diff --git a/app/components/viewable_champ/header_sections_summary_component.rb b/app/components/viewable_champ/header_sections_summary_component.rb new file mode 100644 index 000000000..9b84e69dd --- /dev/null +++ b/app/components/viewable_champ/header_sections_summary_component.rb @@ -0,0 +1,17 @@ +class ViewableChamp::HeaderSectionsSummaryComponent < ApplicationComponent + def initialize(dossier:, is_private:) + @dossier = dossier + @is_private = is_private + end + + def header_sections + @dossier.revision + .types_de_champ_for(scope: @is_private ? :private : :public) + .filter(&:header_section?) + .map { @dossier.project_champ(_1, nil) } # row_id not needed, do not link to repetiion header_sections + end + + def href(header_section) # used by viewable champs to anchor elements + "##{header_section.input_group_id}" + end +end diff --git a/app/components/viewable_champ/header_sections_summary_component/header_sections_summary_component.html.haml b/app/components/viewable_champ/header_sections_summary_component/header_sections_summary_component.html.haml new file mode 100644 index 000000000..d5578eff4 --- /dev/null +++ b/app/components/viewable_champ/header_sections_summary_component/header_sections_summary_component.html.haml @@ -0,0 +1,11 @@ +#summary + - if header_sections.present? + %nav.fr-sidemenu.sticky.fr-hidden.fr-unhidden-md{ "aria-labelledby" => "fr-summary-title", role: "navigation" } + %ul.fr-sidemenu__list + - header_sections.each do |header_section| + %li.fr-sidemenu__item + - level = header_section.header_section_level_value.to_i + - if level == 1 + %a.fr-sidemenu__link{ href: href(header_section) }= header_section.libelle + - else + %a.fr-sidemenu__link{ class: level >= 3 ? 'custom-link-grey': '', href: href(header_section) }= "-- #{header_section.libelle}" diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 11f422718..950f7a70d 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -1002,14 +1002,6 @@ class Procedure < ApplicationRecord lien_dpo.present? && lien_dpo.match?(/@/) end - def draft_revision_header_sections_public - draft_revision.revision_types_de_champ_public.filter { _1.type_de_champ.header_section? } - end - - def draft_revision_header_sections_private - draft_revision.revision_types_de_champ_private.filter { _1.type_de_champ.header_section? } - end - def dossier_for_preview(user) # Try to use a preview or a dossier filled by current user dossiers.where(for_procedure_preview: true).or(dossiers.not_brouillon) diff --git a/app/views/administrateurs/procedures/annotations.html.haml b/app/views/administrateurs/procedures/annotations.html.haml index efd7199d7..579e9cac1 100644 --- a/app/views/administrateurs/procedures/annotations.html.haml +++ b/app/views/administrateurs/procedures/annotations.html.haml @@ -11,7 +11,7 @@ = render NestedForms::FormOwnerComponent.new .fr-grid-row - = render partial: 'champs_summary', locals: { header_sections: @procedure.draft_revision_header_sections_private } + = render TypesDeChampEditor::HeaderSectionsSummaryComponent.new(procedure: @procedure, is_private: true) .fr-col = render TypesDeChampEditor::EditorComponent.new(revision: @procedure.draft_revision, is_annotation: true) diff --git a/app/views/administrateurs/procedures/champs.html.haml b/app/views/administrateurs/procedures/champs.html.haml index 1c2da20dd..5f0c1fa5c 100644 --- a/app/views/administrateurs/procedures/champs.html.haml +++ b/app/views/administrateurs/procedures/champs.html.haml @@ -11,7 +11,7 @@ = render NestedForms::FormOwnerComponent.new .fr-grid-row - = render partial: 'champs_summary', locals: { header_sections: @procedure.draft_revision_header_sections_public } + = render TypesDeChampEditor::HeaderSectionsSummaryComponent.new(procedure: @procedure, is_private: false) .fr-col = render TypesDeChampEditor::EditorComponent.new(revision: @procedure.draft_revision, is_annotation: false) diff --git a/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml b/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml index 8aa818989..2cde00e10 100644 --- a/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml +++ b/app/views/administrateurs/types_de_champ/_insert.turbo_stream.haml @@ -14,7 +14,7 @@ = turbo_stream.replace 'errors-summary', render(Procedure::ErrorsSummary.new(procedure: @procedure, validation_context: @coordinate&.private? ? :types_de_champ_private_editor : :types_de_champ_public_editor)) -= turbo_stream.replace 'summary', render(partial: 'administrateurs/procedures/champs_summary', locals: { header_sections: @coordinate&.private? ? @procedure.draft_revision_header_sections_private : @procedure.draft_revision_header_sections_public}) += turbo_stream.replace 'summary', render(TypesDeChampEditor::HeaderSectionsSummaryComponent.new(procedure: @procedure, is_private: @coordinate&.private?)) - unless flash.alert = turbo_stream.show 'autosave-notice' diff --git a/app/views/instructeurs/dossiers/annotations_privees.html.haml b/app/views/instructeurs/dossiers/annotations_privees.html.haml index 3212cac3c..2d5a9a711 100644 --- a/app/views/instructeurs/dossiers/annotations_privees.html.haml +++ b/app/views/instructeurs/dossiers/annotations_privees.html.haml @@ -3,4 +3,8 @@ = render partial: "header", locals: { dossier: @dossier } #dossier-annotations-privees - = render partial: "shared/dossiers/edit_annotations", locals: { dossier: @dossier, seen_at: @annotations_privees_seen_at } + .fr-container + .fr-grid-row.fr-grid-row--center + .fr-col-md-3= render ViewableChamp::HeaderSectionsSummaryComponent.new(dossier: @dossier, is_private: true) + .fr-col-md-9 + = render partial: "shared/dossiers/edit_annotations", locals: { dossier: @dossier, seen_at: @annotations_privees_seen_at } diff --git a/app/views/instructeurs/dossiers/show.html.haml b/app/views/instructeurs/dossiers/show.html.haml index cbb8f7833..5e733aaf7 100644 --- a/app/views/instructeurs/dossiers/show.html.haml +++ b/app/views/instructeurs/dossiers/show.html.haml @@ -14,4 +14,8 @@ %p Les informations sur l'entreprise arriveront d’ici quelques heures. -= render partial: "shared/dossiers/demande", locals: { dossier: @dossier, demande_seen_at: @demande_seen_at, profile: 'instructeur' } +.fr-container + .fr-grid-row.fr-grid-row--center + .fr-col-md-3= render ViewableChamp::HeaderSectionsSummaryComponent.new(dossier: @dossier, is_private: false) + .fr-col-md-9 + = render partial: "shared/dossiers/demande", locals: { dossier: @dossier, demande_seen_at: @demande_seen_at, profile: 'instructeur' } diff --git a/app/views/shared/dossiers/_demande.html.haml b/app/views/shared/dossiers/_demande.html.haml index 3cdd95d2e..6bb7f5fc1 100644 --- a/app/views/shared/dossiers/_demande.html.haml +++ b/app/views/shared/dossiers/_demande.html.haml @@ -2,64 +2,63 @@ - content_for(:notice_info) do = render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.user.france_connect_informations.first } -.fr-container.counter-start-header-section.dossier-show{ class: class_names('gallery': feature_enabled?(:gallery_demande), 'gallery-demande': feature_enabled?(:gallery_demande), "dossier-show-instructeur" => profile =="instructeur"), "data-controller": "lightbox" } - .fr-grid-row.fr-grid-row--center - .fr-col-12.fr-col-xl-8 - - if profile == 'instructeur' && dossier.termine_and_accuse_lecture? - = render Dsfr::CalloutComponent.new(title: nil) do |c| - - c.with_html_body do - = t('views.shared.dossiers.demande.accuse_lecture') - - if dossier.accuse_lecture_agreement_at.present? - = t('views.shared.dossiers.demande.accuse_lecture_with_agreement', agreement: l(dossier.accuse_lecture_agreement_at, format: :long)) - - else - = t('views.shared.dossiers.demande.accuse_lecture_without_agreement') +.counter-start-header-section.dossier-show{ class: class_names('gallery': feature_enabled?(:gallery_demande), 'gallery-demande': feature_enabled?(:gallery_demande), "dossier-show-instructeur" => profile =="instructeur"), "data-controller": "lightbox" } - %h2.fr-h6.fr-background-alt--grey.fr-mb-0 - .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.en_construction') + - if profile == 'instructeur' && dossier.termine_and_accuse_lecture? + = render Dsfr::CalloutComponent.new(title: nil) do |c| + - c.with_html_body do + = t('views.shared.dossiers.demande.accuse_lecture') + - if dossier.accuse_lecture_agreement_at.present? + = t('views.shared.dossiers.demande.accuse_lecture_with_agreement', agreement: l(dossier.accuse_lecture_agreement_at, format: :long)) + - else + = t('views.shared.dossiers.demande.accuse_lecture_without_agreement') - - if dossier.depose_at.present? - = render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier, profile: profile } + %h2.fr-h6.fr-background-alt--grey.fr-mb-0 + .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.en_construction') + + - if dossier.depose_at.present? + = render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier, profile: profile } - - if dossier.for_tiers? - %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex - .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.mandataire_identity') + - if dossier.for_tiers? + %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex + .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.mandataire_identity') - - if dossier.individual.present? && profile == 'usager' && !dossier.read_only? - = link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline' + - if dossier.individual.present? && profile == 'usager' && !dossier.read_only? + = link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline' - = render partial: "shared/dossiers/mandataire_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display), dossier: dossier } + = render partial: "shared/dossiers/mandataire_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display), dossier: dossier } - .tab-title - %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex - .flex-grow.fr-py-3v.fr-px-2w - = t('views.shared.dossiers.demande.requester_identity') + .tab-title + %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex + .flex-grow.fr-py-3v.fr-px-2w + = t('views.shared.dossiers.demande.requester_identity') - - if dossier.identity_updated_at.present? && demande_seen_at&.<(dossier.identity_updated_at) - %span.fr-badge.fr-badge--new.fr-badge--sm - = t('views.shared.dossiers.demande.requester_identity_updated_at', date: try_format_datetime(dossier.identity_updated_at)) + - if dossier.identity_updated_at.present? && demande_seen_at&.<(dossier.identity_updated_at) + %span.fr-badge.fr-badge--new.fr-badge--sm + = t('views.shared.dossiers.demande.requester_identity_updated_at', date: try_format_datetime(dossier.identity_updated_at)) - - if dossier.etablissement.present? && profile == 'usager' && !dossier.read_only? - = link_to t('views.shared.dossiers.demande.edit_siret'), siret_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline' + - if dossier.etablissement.present? && profile == 'usager' && !dossier.read_only? + = link_to t('views.shared.dossiers.demande.edit_siret'), siret_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline' - - if dossier.individual.present? && profile == 'usager' && !dossier.read_only? - = link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline' + - if dossier.individual.present? && profile == 'usager' && !dossier.read_only? + = link_to t('views.shared.dossiers.demande.edit_identity'), identite_dossier_path(dossier), class: 'fr-py-3v fr-btn fr-btn--tertiary-no-outline' - = render partial: "shared/dossiers/user_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display), for_tiers: dossier.for_tiers?, beneficiaire_mail: dossier.for_tiers? ? dossier.individual.email : ""} + = render partial: "shared/dossiers/user_infos", locals: { user_deleted: dossier.user_deleted?, email: dossier.user_email_for(:display), for_tiers: dossier.for_tiers?, beneficiaire_mail: dossier.for_tiers? ? dossier.individual.email : ""} - - if dossier.individual.present? - = render partial: "shared/dossiers/identite_individual", locals: { dossier: dossier } + - if dossier.individual.present? + = render partial: "shared/dossiers/identite_individual", locals: { dossier: dossier } - - if dossier.etablissement.present? - .fr-mt-1w.fr-mb-4w.fr-px-2w - = render partial: "shared/dossiers/identite_entreprise", locals: { etablissement: dossier.etablissement, profile: profile } + - if dossier.etablissement.present? + .fr-mt-1w.fr-mb-4w.fr-px-2w + = render partial: "shared/dossiers/identite_entreprise", locals: { etablissement: dossier.etablissement, profile: profile } - %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex - .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.form') + %h2.fr-h6.fr-background-alt--grey.fr-mb-0.flex + .flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.form') - - types_de_champ = dossier.revision.types_de_champ_public - - if types_de_champ.any? || dossier.procedure.routing_enabled? - = render ViewableChamp::SectionComponent.new(dossier:, types_de_champ:, demande_seen_at:, profile:) + - types_de_champ = dossier.revision.types_de_champ_public + - if types_de_champ.any? || dossier.procedure.routing_enabled? + = render ViewableChamp::SectionComponent.new(dossier:, types_de_champ:, demande_seen_at:, profile:) diff --git a/app/views/users/dossiers/demande.html.haml b/app/views/users/dossiers/demande.html.haml index 346b2cfa1..543b3c867 100644 --- a/app/views/users/dossiers/demande.html.haml +++ b/app/views/users/dossiers/demande.html.haml @@ -11,13 +11,15 @@ .fr-grid-row.fr-grid-row--center .fr-col-xl-10 = render Dossiers::EnConstructionNotSubmittedComponent.new(dossier: @dossier, user: current_user) - - = render partial: 'shared/dossiers/demande', locals: { dossier: @dossier, demande_seen_at: nil, profile: 'usager' } + .fr-container + .fr-grid-row.fr-grid-row--center + .fr-col-md-9 + = render partial: 'shared/dossiers/demande', locals: { dossier: @dossier, demande_seen_at: nil, profile: 'usager' } - if !@dossier.read_only? .fr-container.fr-mt-2w - .fr-grid-row + .fr-grid-row.fr-grid-row--center .fr-col-xl-8.fr-col-offset-xl-2 %p= link_to t('views.users.dossiers.demande.edit_dossier'), modifier_dossier_path(@dossier), class: 'fr-btn fr-btn-sm', title: t('views.users.dossiers.demande.edit_dossier_title') diff --git a/spec/views/instructeur/dossiers/annotations_privee.html.haml_spec.rb b/spec/views/instructeur/dossiers/annotations_privee.html.haml_spec.rb new file mode 100644 index 000000000..fbc4f84b3 --- /dev/null +++ b/spec/views/instructeur/dossiers/annotations_privee.html.haml_spec.rb @@ -0,0 +1,26 @@ +describe 'instructeurs/dossiers/annotations_privees', type: :view do + let(:current_instructeur) { create(:instructeur) } + let(:dossier) { create(:dossier, :en_construction) } + + before do + sign_in(current_instructeur.user) + allow(view).to receive(:current_instructeur).and_return(current_instructeur) + assign(:dossier, dossier) + end + + subject { render } + + describe 'when header_sections are present' do + let(:procedure) { create(:procedure, types_de_champ_private:) } + let(:types_de_champ_private) do + [ + { type: :header_section, level: 1, libelle: 'l1' } + ] + end + let(:dossier) { create(:dossier, :en_construction, procedure:) } + + it 'displays a link to header_section' do + expect(subject).to have_selector('a.fr-sidemenu__link', text: 'l1') + end + end +end diff --git a/spec/views/instructeur/dossiers/show.html.haml_spec.rb b/spec/views/instructeur/dossiers/show.html.haml_spec.rb index ffbcb4e85..190362823 100644 --- a/spec/views/instructeur/dossiers/show.html.haml_spec.rb +++ b/spec/views/instructeur/dossiers/show.html.haml_spec.rb @@ -201,4 +201,18 @@ describe 'instructeurs/dossiers/show', type: :view do end end end + + describe 'when header_sections are present' do + let(:procedure) { create(:procedure, types_de_champ_public:) } + let(:types_de_champ_public) do + [ + { type: :header_section, level: 1, libelle: 'l1' } + ] + end + let(:dossier) { create(:dossier, :en_construction, procedure:) } + + it 'displays a link to header_section' do + expect(subject).to have_selector('a.fr-sidemenu__link', text: 'l1') + end + end end