diff --git a/app/controllers/administrateurs/procedures_controller.rb b/app/controllers/administrateurs/procedures_controller.rb index 9b1b2b1b9..4dde5c6c2 100644 --- a/app/controllers/administrateurs/procedures_controller.rb +++ b/app/controllers/administrateurs/procedures_controller.rb @@ -482,7 +482,8 @@ module Administrateurs procedures_result = procedures_result.where('unaccent(libelle) ILIKE unaccent(?)', "%#{filter.libelle}%") if filter.libelle.present? procedures_sql = procedures_result.to_sql - sql = "select id, libelle, published_at, aasm_state, estimated_dossiers_count, template, count(administrateurs_procedures.administrateur_id) as admin_count from administrateurs_procedures inner join procedures on procedures.id = administrateurs_procedures.procedure_id where procedures.id in (#{procedures_sql}) group by procedures.id order by published_at desc" + sql = "select procedures.id, libelle, published_at, aasm_state, estimated_dossiers_count, template, array_agg(distinct latest_labels.name) filter (where latest_labels.name is not null) as latest_zone_labels from administrateurs_procedures inner join procedures on procedures.id = administrateurs_procedures.procedure_id left join procedures_zones ON procedures.id = procedures_zones.procedure_id left join zones ON zones.id = procedures_zones.zone_id left join (select zone_id, name from zone_labels where (zone_id, designated_on) in (select zone_id, max(designated_on) from zone_labels group by zone_id)) as latest_labels on zones.id = latest_labels.zone_id + where procedures.id in (#{procedures_sql}) group by procedures.id order by published_at desc" ActiveRecord::Base.connection.execute(sql) end diff --git a/app/models/procedure_detail.rb b/app/models/procedure_detail.rb index c590787b3..ebe6c3f6d 100644 --- a/app/models/procedure_detail.rb +++ b/app/models/procedure_detail.rb @@ -1,4 +1,4 @@ -ProcedureDetail = Struct.new(:id, :libelle, :published_at, :aasm_state, :estimated_dossiers_count, :admin_count, :template, keyword_init: true) do +ProcedureDetail = Struct.new(:id, :libelle, :published_at, :aasm_state, :estimated_dossiers_count, :admin_count, :template, :latest_zone_labels, keyword_init: true) do include SpreadsheetArchitect def spreadsheet_columns @@ -12,4 +12,11 @@ ProcedureDetail = Struct.new(:id, :libelle, :published_at, :aasm_state, :estimat def administrateurs AdministrateursCounter.new(admin_count) end + + def parsed_latest_zone_labels + # Replace curly braces with square brackets to make it a valid JSON array + JSON.parse(latest_zone_labels.tr('{', '[').tr('}', ']')) + rescue JSON::ParserError + [] + end end diff --git a/app/views/administrateurs/procedures/_detail.html.haml b/app/views/administrateurs/procedures/_detail.html.haml index b2e89c4b7..798bb15a6 100644 --- a/app/views/administrateurs/procedures/_detail.html.haml +++ b/app/views/administrateurs/procedures/_detail.html.haml @@ -5,15 +5,23 @@ - params = show_detail ? {} : { show_detail: true } = button_to detail_admin_procedure_path(procedure["id"]), method: :post, params:, title:, class: [icon, "fr-icon--sm fr-mr-1w fr-mb-1w fr-text-action-high--blue-france fr-btn fr-btn--tertiary-no-outline" ] do = title - %td - if procedure.template - %p.fr-badge.fr-badge--info.fr-badge--sm= "Modèle DS" - %br + %p.fr-badge.fr-badge--info.fr-badge--sm= "Modèle" + %abbr{ title: APPLICATION_NAME }= acronymize(APPLICATION_NAME) = procedure.libelle %td= procedure.id %td= procedure.estimated_dossiers_count - %td= procedure.administrateurs.count + %td + - if procedure.respond_to?(:parsed_latest_zone_labels) + - procedure.parsed_latest_zone_labels.uniq.each do |zone_label| + %span.mb-2= zone_label + .mb-2 + - else + - procedure.zones.uniq.each do |zone| + %span= zone.current_label + .mb-1 + %td= t procedure.aasm_state, scope: 'activerecord.attributes.procedure.aasm_state' %td= l(procedure.published_at, format: :message_date_without_time) if procedure.published_at %td @@ -21,16 +29,10 @@ = link_to('Cloner', admin_procedure_clone_path(procedure.id, from_new_from_existing: true), 'data-method' => :put, class: 'fr-btn fr-btn--tertiary fr-btn--sm') - - - if show_detail %tr.procedure{ id: "procedure_detail_#{procedure.id}" } %td.fr-highlight--beige-gris-galet{ colspan: '8' } .fr-container - .fr-grid-row - .fr-col-6 - - procedure.zones.uniq.each do |zone| - = zone.label_at(procedure.published_or_created_at) - .fr-col-6 - - procedure.administrateurs.uniq.each do |admin| - = admin.email + .fr-col-6 + - procedure.administrateurs.uniq.each do |admin| + = admin.email diff --git a/app/views/administrateurs/procedures/_informations.html.haml b/app/views/administrateurs/procedures/_informations.html.haml index fbc56387d..a3d361a52 100644 --- a/app/views/administrateurs/procedures/_informations.html.haml +++ b/app/views/administrateurs/procedures/_informations.html.haml @@ -120,8 +120,8 @@ %use.fr-artwork-major{ href: image_path("pictograms/buildings/school.svg#artwork-major") } .fr-fieldset__element - = f.label :tags, 'Associez les tags à la démarche', class: 'fr-label' - %p.fr-hint-text Les tags sont des mots ou des expressions que vous attribuez aux démarches pour décrire leur contenu et pour les retrouver. Les tags sont partagés avec la communauté, ce qui vous permet de voir les tags attribués aux démarches créées par les autres administrateurs. + = f.label :tags, 'Associez des thématiques à la démarche', class: 'fr-label' + %p.fr-hint-text Par des mots ou des expressions que vous attribuez aux démarches pour décrire leur contenu et pour les retrouver. Les tags sont partagés avec la communauté, ce qui vous permet de voir les tags attribués aux démarches créées par les autres administrateurs. = hidden_field_tag 'procedure[tags]', JSON.generate(@procedure.tags) = react_component("ComboMultiple", id: "procedure_tags_combo", diff --git a/app/views/administrateurs/procedures/all.html.haml b/app/views/administrateurs/procedures/all.html.haml index 43069b757..224e59fc6 100644 --- a/app/views/administrateurs/procedures/all.html.haml +++ b/app/views/administrateurs/procedures/all.html.haml @@ -57,8 +57,8 @@ %th{ scope: 'col' } %th{ scope: 'col' } Démarche %th{ scope: 'col' } № - %th{ scope: 'col' } Dossiers - %th{ scope: 'col' } Administrateurs + %th{ scope: 'col' } Nombre de dossiers + %th{ scope: 'col' } Zones %th{ scope: 'col' } Statut %th{ scope: 'col' } Date %th{ scope: 'col' } Action diff --git a/app/views/layouts/all.html.haml b/app/views/layouts/all.html.haml index 2b785e13f..44d69dacf 100644 --- a/app/views/layouts/all.html.haml +++ b/app/views/layouts/all.html.haml @@ -1,6 +1,5 @@ - content_for(:main_navigation) do = render 'administrateurs/main_navigation' - - content_for :content do .fr-container %h1.fr-my-4w Toutes les démarches @@ -25,16 +24,16 @@ = link_to all_admin_procedures_path(zone_ids: current_administrateur.zones), { data: { turbo: 'false' } } do %span.fr-icon-arrow-go-back-line Réinitialiser %ul + %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } .fr-mb-1w %button{ 'data-action': 'expand#toggle' } %span.fr-icon-add-line.fr-icon--sm.fr-mr-1w.fr-text-action-high--blue-france{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } - Mes zones - .fr-ml-1w{ 'data-expand-target': 'content' } - = f.collection_check_boxes :zone_ids, @filter.admin_zones, :id, :current_label, include_hidden: false do |b| - .fr-checkbox-group.fr-ml-2w.fr-py-1w - = b.check_box(checked: @filter.zone_filtered?(b.value)) - = b.label(class: 'fr-label') { b.text } + Démarches modèles + .fr-ml-1w.hidden{ 'data-expand-target': 'content' } + .fr-checkbox-group.fr-ml-2w.fr-py-1w + = f.check_box :template, class: 'fr-input' + = f.label :template, 'Modèle DS', class: 'fr-label' %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } .fr-mb-1w %button{ 'data-action': 'expand#toggle' } @@ -45,6 +44,16 @@ .fr-checkbox-group.fr-ml-2w.fr-py-1w = b.check_box(checked: @filter.zone_filtered?(b.value)) = b.label(class: 'fr-label') { b.text } + %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } + .fr-mb-1w + %button{ 'data-action': 'expand#toggle' } + %span.fr-icon-add-line.fr-icon--sm.fr-mr-1w.fr-text-action-high--blue-france{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } + Mes zones + .fr-ml-1w.hidden{ 'data-expand-target': 'content' } + = f.collection_check_boxes :zone_ids, @filter.admin_zones, :id, :current_label, include_hidden: false do |b| + .fr-checkbox-group.fr-ml-2w.fr-py-1w + = b.check_box(checked: @filter.zone_filtered?(b.value)) + = b.label(class: 'fr-label') { b.text } %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } .fr-mb-1w %button{ 'data-action': 'expand#toggle' } @@ -65,6 +74,16 @@ { selected: @filter.service_departement, include_blank: ''}, id: "service_dep_select", class: 'fr-select' + %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } + .fr-mb-1w + %button{ 'data-action': 'expand#toggle' } + %span.fr-icon-add-line.fr-icon--sm.fr-mr-1w.fr-text-action-high--blue-france{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } + Type d'usager + .fr-ml-1w.hidden{ 'data-expand-target': 'content' } + = f.collection_check_boxes :kind_usagers, ['individual', 'personne_morale'], :to_s, :to_s, include_hidden: false do |b| + .fr-checkbox-group.fr-ml-2w.fr-py-1w + = b.check_box(checked: @filter.kind_usager_filtered?(b.value)) + = b.label(class: 'fr-label') { t b.text, scope: 'activerecord.attributes.procedure.kind_usager' } %li.fr-py-2w{ 'data-controller': "expand" } .fr-mb-1w.fr-pl-2w %button{ 'data-action': 'click->expand#toggle' } @@ -86,39 +105,21 @@ = b.check_box(checked: @filter.status_filtered?(b.value)) = b.label(class: 'fr-label') { t b.text, scope: 'activerecord.attributes.procedure.aasm_state' } + %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } .fr-mb-1w %button{ 'data-action': 'expand#toggle' } %span.fr-icon-add-line.fr-icon--sm.fr-mr-1w.fr-text-action-high--blue-france{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } - Type d'usager - .fr-ml-1w.hidden{ 'data-expand-target': 'content' } - = f.collection_check_boxes :kind_usagers, ['individual', 'personne_morale'], :to_s, :to_s, include_hidden: false do |b| - .fr-checkbox-group.fr-ml-2w.fr-py-1w - = b.check_box(checked: @filter.kind_usager_filtered?(b.value)) - = b.label(class: 'fr-label') { t b.text, scope: 'activerecord.attributes.procedure.kind_usager' } - %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } - .fr-mb-1w - %button{ 'data-action': 'expand#toggle' } - %span.fr-icon-add-line.fr-icon--sm.fr-mr-1w.fr-text-action-high--blue-france{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } - Tags + Thématique .fr-ml-1w.hidden{ 'data-expand-target': 'content' } %div - = f.search_field :tags, placeholder: 'Choisissez un tag', list: 'tags_list', class: 'fr-input', data: { no_autosubmit: 'input', turbo_force: :server }, multiple: true + = f.search_field :tags, placeholder: 'Choisissez un thème', list: 'tags_list', class: 'fr-input', data: { no_autosubmit: 'input', turbo_force: :server }, multiple: true %datalist#tags_list - Procedure.tags.each do |tag| %option{ value: tag } - if @filter.tags.present? - @filter.tags.each do |tag| = f.hidden_field :tags, value: tag, multiple: true, id: "tag-#{tag.tr(' ', '_')}" - %li.fr-py-2w.fr-pl-2w{ 'data-controller': "expand" } - .fr-mb-1w - %button{ 'data-action': 'expand#toggle' } - %span.fr-icon-add-line.fr-icon--sm.fr-mr-1w.fr-text-action-high--blue-france{ 'aria-hidden': 'true', 'data-expand-target': 'icon' } - Démarches modèles - .fr-ml-1w.hidden{ 'data-expand-target': 'content' } - .fr-checkbox-group.fr-ml-2w.fr-py-1w - = f.check_box :template, class: 'fr-input' - = f.label :template, 'Modèle DS', class: 'fr-label' .fr-col-9 = yield(:results) diff --git a/spec/controllers/administrateurs/procedures_controller_spec.rb b/spec/controllers/administrateurs/procedures_controller_spec.rb index 72d2484e0..1193b769f 100644 --- a/spec/controllers/administrateurs/procedures_controller_spec.rb +++ b/spec/controllers/administrateurs/procedures_controller_spec.rb @@ -91,6 +91,10 @@ describe Administrateurs::ProceduresController, type: :controller do let!(:draft_procedure) { create(:procedure) } let!(:published_procedure) { create(:procedure_with_dossiers, :published, dossiers_count: 2) } let!(:closed_procedure) { create(:procedure, :closed) } + let!(:procedure_detail_draft) { ProcedureDetail.new(id: draft_procedure.id, latest_zone_labels: '{ "zone1", "zone2" }') } + let!(:procedure_detail_published) { ProcedureDetail.new(id: published_procedure.id, latest_zone_labels: '{ "zone3", "zone4" }') } + let!(:procedure_detail_closed) { ProcedureDetail.new(id: closed_procedure.id, latest_zone_labels: '{ "zone5", "zone6" }') } + subject { get :all } it { expect(subject.status).to eq(200) } @@ -116,6 +120,19 @@ describe Administrateurs::ProceduresController, type: :controller do expect(assigns(:procedures).any? { |p| p.id == draft_procedure.id }).to be_falsey end + context 'with parsed latest zone labels' do + it 'parses the latest zone labels correctly' do + expect(procedure_detail_draft.parsed_latest_zone_labels).to eq(["zone1", "zone2"]) + expect(procedure_detail_published.parsed_latest_zone_labels).to eq(["zone3", "zone4"]) + expect(procedure_detail_closed.parsed_latest_zone_labels).to eq(["zone5", "zone6"]) + end + + it 'returns an empty array for invalid JSON' do + procedure_detail_draft.latest_zone_labels = '{ invalid json }' + expect(procedure_detail_draft.parsed_latest_zone_labels).to eq([]) + end + end + context 'for default admin zones' do let(:zone1) { create(:zone) } let(:zone2) { create(:zone) }