From 5ff59236127ab50999ea52a3cd4ff470a474214f Mon Sep 17 00:00:00 2001 From: Lisa Durand Date: Wed, 26 Jul 2023 17:51:04 +0200 Subject: [PATCH] make dossiers search compatible with filter by procedure --- .../user_procedure_filter_component.html.haml | 1 + app/controllers/users/dossiers_controller.rb | 21 +++++--- app/services/dossier_search_service.rb | 8 +-- .../users/dossiers/_dossiers_list.html.haml | 9 +++- app/views/users/dossiers/index.html.haml | 14 +++-- config/locales/en.yml | 4 ++ config/locales/fr.yml | 5 ++ spec/system/users/list_dossiers_spec.rb | 53 ++++++++++++------- 8 files changed, 79 insertions(+), 36 deletions(-) diff --git a/app/components/dossiers/user_procedure_filter_component/user_procedure_filter_component.html.haml b/app/components/dossiers/user_procedure_filter_component/user_procedure_filter_component.html.haml index bca9ee4aa..570ec76ff 100644 --- a/app/components/dossiers/user_procedure_filter_component/user_procedure_filter_component.html.haml +++ b/app/components/dossiers/user_procedure_filter_component/user_procedure_filter_component.html.haml @@ -1,4 +1,5 @@ = form_with(url: dossiers_path, method: :get, data: { controller: 'autosubmit' } ) do |f| + = f.hidden_field :q, value: params[:q], id: nil = f.label :procedure_id, t('.procedure.label'), class: 'sr-only' .fr-input-group = f.select :procedure_id, options_for_select(@procedures_for_select, params[:procedure_id]), { prompt: t('.procedures.prompt') }, class: 'fr-select' diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 91419ad23..2c4948b58 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -33,21 +33,28 @@ module Users .distinct(:procedure_id) .order(:libelle) .pluck(:libelle, :id) - @procedure_id = params[:procedure_id] + @procedure_id = params[:procedure_id] if @procedure_id.present? ordered_dossiers = ordered_dossiers.where(procedures: { id: @procedure_id }) deleted_dossiers = deleted_dossiers.where(procedures: { id: @procedure_id }) end - dossiers_visibles = ordered_dossiers.visible_by_user + @search_terms = params[:q] + if @search_terms.present? + dossiers_filter_by_search = DossierSearchService.matching_dossiers_for_user(@search_terms, current_user).page + ordered_dossiers = ordered_dossiers.merge(dossiers_filter_by_search) + deleted_dossiers = nil + end - @user_dossiers = current_user.dossiers.state_not_termine.merge(dossiers_visibles) - @dossiers_traites = current_user.dossiers.state_termine.merge(dossiers_visibles) - @dossiers_invites = current_user.dossiers_invites.merge(dossiers_visibles) + @dossiers_visibles = ordered_dossiers.visible_by_user + + @user_dossiers = current_user.dossiers.state_not_termine.merge(@dossiers_visibles) + @dossiers_traites = current_user.dossiers.state_termine.merge(@dossiers_visibles) + @dossiers_invites = current_user.dossiers_invites.merge(@dossiers_visibles) @dossiers_supprimes_recemment = current_user.dossiers.hidden_by_user.merge(ordered_dossiers) - @dossier_transferes = dossiers_visibles.where(dossier_transfer_id: DossierTransfer.for_email(current_user.email).ids) - @dossiers_close_to_expiration = current_user.dossiers.close_to_expiration.merge(dossiers_visibles) + @dossier_transferes = @dossiers_visibles.where(dossier_transfer_id: DossierTransfer.for_email(current_user.email).ids) + @dossiers_close_to_expiration = current_user.dossiers.close_to_expiration.merge(@dossiers_visibles) @dossiers_supprimes_definitivement = deleted_dossiers @statut = statut(@user_dossiers, @dossiers_traites, @dossiers_invites, @dossiers_supprimes_recemment, @dossiers_supprimes_definitivement, @dossier_transferes, @dossiers_close_to_expiration, params[:statut]) diff --git a/app/services/dossier_search_service.rb b/app/services/dossier_search_service.rb index 9259908a7..7afdad5f6 100644 --- a/app/services/dossier_search_service.rb +++ b/app/services/dossier_search_service.rb @@ -10,7 +10,7 @@ class DossierSearchService def self.matching_dossiers_for_user(search_terms, user) dossier_by_exact_id_for_user(search_terms, user) - .presence || dossier_by_full_text_for_user(search_terms, Dossier.where(id: user.dossiers.ids + user.dossiers_invites.ids)) + .presence || dossier_by_full_text_for_user(search_terms, Dossier.includes(:procedure).where(id: user.dossiers.ids + user.dossiers_invites.ids)) end private @@ -38,7 +38,7 @@ class DossierSearchService def self.dossier_by_full_text_for_user(search_terms, dossiers) ts_vector = "to_tsvector('french', search_terms)" - ts_query = "to_tsquery('french', #{Dossier.connection.quote(to_tsquery(search_terms))})" + ts_query = "to_tsquery('french', #{Dossier.includes(:procedure).connection.quote(to_tsquery(search_terms))})" dossiers .visible_by_user @@ -49,9 +49,9 @@ class DossierSearchService def self.dossier_by_exact_id_for_user(search_terms, user) id = search_terms.to_i if id != 0 && id_compatible?(id) # Sometimes user is searching dossiers with a big number (ex: SIRET), ActiveRecord can't deal with them and throws ActiveModel::RangeError. id_compatible? prevents this. - Dossier.where(id: user.dossiers.visible_by_user.where(id: id) + user.dossiers_invites.visible_by_user.where(id: id)).distinct + Dossier.includes(:procedure).where(id: user.dossiers.visible_by_user.where(id: id) + user.dossiers_invites.visible_by_user.where(id: id)).distinct else - Dossier.none + Dossier.includes(:procedure).none end end diff --git a/app/views/users/dossiers/_dossiers_list.html.haml b/app/views/users/dossiers/_dossiers_list.html.haml index 057ea81d6..e2f759b32 100644 --- a/app/views/users/dossiers/_dossiers_list.html.haml +++ b/app/views/users/dossiers/_dossiers_list.html.haml @@ -94,7 +94,7 @@ - else - - if filter.filter_params.present? + - if filter.present? .blank-tab %h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title') %p.empty-text-details @@ -102,6 +102,13 @@ %br = link_to t('views.users.dossiers.dossiers_list.no_result_reset_filter'), dossiers_path(statut: statut), class: 'fr-btn fr-btn--sm fr-mt-2w' + - elsif search + .blank-tab + %h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title') + %p.empty-text-details + = t('views.users.dossiers.dossiers_list.no_result_text_with_search') + %br + = link_to t('views.users.dossiers.dossiers_list.no_result_reset_search'), dossiers_path(), class: 'fr-btn fr-btn--sm fr-mt-2w' - else .blank-tab %h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title') diff --git a/app/views/users/dossiers/index.html.haml b/app/views/users/dossiers/index.html.haml index 6b5789af4..f9b90694c 100644 --- a/app/views/users/dossiers/index.html.haml +++ b/app/views/users/dossiers/index.html.haml @@ -14,7 +14,8 @@ - if current_user.dossiers.count > 2 || current_user.dossiers_invites.count > 2 .fr-col #search-2.fr-search-bar - = form_tag recherche_dossiers_path, method: :get, :role => "search", class: "flex width-100 fr-mb-5w" do + = form_tag dossiers_path, method: :get, :role => "search", class: "flex width-100 fr-mb-5w" do + = hidden_field_tag :procedure_id, params[:procedure_id] = label_tag "q", t('views.users.dossiers.search.search_file'), class: 'fr-label' = text_field_tag "q", "#{@search_terms if @search_terms.present?}", placeholder: t('views.users.dossiers.search.search_file'), class: "fr-input" %button.fr-btn.fr-btn--sm @@ -71,7 +72,7 @@ .fr-container .fr-grid-row.fr-grid-row--center .fr-col-xl-10 - - if @statut == "en-cours" + - if @statut == "en-cours" && @search_terms.blank? - if @first_brouillon_recently_updated.present? = render Dsfr::CalloutComponent.new(title: t('users.dossiers.header.callout.first_brouillon_recently_updated_title'), heading_level: 'h2') do |c| - c.with_body do @@ -80,8 +81,11 @@ = link_to t('users.dossiers.header.callout.first_brouillon_recently_updated_button'), url_for_dossier(@first_brouillon_recently_updated), class: 'fr-btn' - if @search_terms.present? - %h2.page-title Résultat de la recherche pour « #{@search_terms} » - = render partial: "dossiers_list", locals: { dossiers: @dossiers } + %h2.page-title + = t('views.users.dossiers.search.result_term_title', search_terms: @search_terms) + - if @procedure_id.present? + = t('views.users.dossiers.search.result_procedure_title', procedure_libelle: @procedures_for_select.rassoc(@procedure_id.to_i).first) + = render partial: "dossiers_list", locals: { dossiers: @dossiers_visibles, filter: nil, search: true } - else = render Dossiers::UserFilterComponent.new(statut: @statut, filter: @filter, procedure_id: @procedure_id ) @@ -90,4 +94,4 @@ -# /!\ in this context, @dossiers is a collection of DeletedDossier not Dossier = render partial: "deleted_dossiers_list", locals: { deleted_dossiers: @dossiers } - else - = render partial: "dossiers_list", locals: { dossiers: @dossiers, filter: @filter, statut: @statut } + = render partial: "dossiers_list", locals: { dossiers: @dossiers, filter: @filter, statut: @statut, search: false } diff --git a/config/locales/en.yml b/config/locales/en.yml index 1e2c09f69..021047ced 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -456,6 +456,8 @@ en: search: search_file: Search a file (File number, keywords) simple: Search + result_term_title: Search result for « %{search_terms} » + result_procedure_title: and procedure « %{procedure_libelle} » secondary_menu: Secondary menu index: dossiers: "My files" @@ -463,6 +465,8 @@ en: n_dossier: "File n." no_result_title: No files no_result_text_html: "To fill a procedure, contact your administration asking for the procedure link.
It should look like %{app_base}/commencer/xxx." + no_result_text_with_search: found with search terms + no_result_reset_search: Reset search no_result_text_with_filter: found with selected filters no_result_reset_filter: Reset filters procedure_closed: This procedure has been closed, you will not be able to submit a file again from the procedure link, contact your administration for more information. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 17f86bfc0..e1ebac1a2 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -459,6 +459,9 @@ fr: search: search_file: Rechercher un dossier (N° de dossier, mots-clés) simple: Rechercher + result: Résultat de la recherche pour « %{search_terms} » + result_term_title: Résultat de la recherche pour « %{search_terms} » + result_procedure_title: et pour la procédure « %{procedure_libelle} » secondary_menu: Menu secondaire index: dossiers: "Mes dossiers" @@ -466,6 +469,8 @@ fr: n_dossier: "dossier Nº " no_result_title: Aucun dossier no_result_text_html: "Pour remplir une démarche, contactez votre administration en lui demandant le lien de la démarche.
Celui ci doit ressembler à %{app_base}/commencer/xxx." + no_result_text_with_search: ne correspond aux termes recherchés + no_result_reset_search: Réinitialiser la recherche no_result_text_with_filter: ne correspond aux filtres sélectionnés no_result_reset_filter: Réinitialiser les filtres procedure_closed: Cette démarche a été clôturée, vous ne pourrez pas redéposer de dossier à partir du lien de la démarche, contactez votre administration pour plus d’information. diff --git a/spec/system/users/list_dossiers_spec.rb b/spec/system/users/list_dossiers_spec.rb index e83a19831..68b28a23b 100644 --- a/spec/system/users/list_dossiers_spec.rb +++ b/spec/system/users/list_dossiers_spec.rb @@ -190,7 +190,7 @@ describe 'user access to the list of their dossiers', js: true, retry: 3 do end end - describe "recherche" do + describe "user search bar" do context "when the dossier does not exist" do before do page.find_by_id('q').set(10000000) @@ -199,7 +199,9 @@ describe 'user access to the list of their dossiers', js: true, retry: 3 do it "shows an error message on the dossiers page" do expect(current_path).to eq(dossiers_path) - expect(page).to have_content("Vous n’avez pas de dossiers contenant « 10000000 ».") + expect(page).to have_content("Résultat de la recherche pour « 10000000 »") + expect(page).to have_content("Aucun dossier") + expect(page).to have_content("ne correspond aux termes recherchés") end end @@ -213,7 +215,10 @@ describe 'user access to the list of their dossiers', js: true, retry: 3 do it "shows an error message on the dossiers page" do expect(current_path).to eq(dossiers_path) - expect(page).to have_content("Vous n’avez pas de dossiers contenant « #{dossier_other_user.id} ».") + expect(page).to have_content("Résultat de la recherche pour « #{dossier_other_user.id} »") + expect(page).to have_content("Aucun dossier") + expect(page).to have_content("ne correspond aux termes recherchés") + expect(page).to have_content("Réinitialiser la recherche") end end @@ -223,8 +228,11 @@ describe 'user access to the list of their dossiers', js: true, retry: 3 do find('.fr-search-bar .fr-btn').click end - it "redirects to the dossier page" do - expect(current_path).to eq(dossier_path(dossier_en_construction)) + it "appears in the result list" do + expect(current_path).to eq(dossiers_path) + expect(page).to have_content("Résultat de la recherche pour « #{dossier_en_construction.id} »") + expect(page).not_to have_css('.tabs') + expect(page).to have_content(dossier_en_construction.id) end end @@ -233,25 +241,32 @@ describe 'user access to the list of their dossiers', js: true, retry: 3 do page.find_by_id('q').set(dossier_en_construction.champs_public.first.value) end - context 'when it only matches one dossier' do - before do - find('.fr-search-bar .fr-btn').click - end - it "redirects to the dossier page" do - expect(current_path).to eq(dossier_path(dossier_en_construction)) - end - end - - context 'when it matches multiple dossier' do + context 'when it matches multiple dossiers' do let!(:dossier_with_champs) { create(:dossier, :with_populated_champs, :en_construction, user: user) } before do find('.fr-search-bar .fr-btn').click end - it "redirects to the search results" do - expect(current_path).to eq(recherche_dossiers_path) - expect(page).to have_content(dossier_en_construction.id) - expect(page).to have_content(dossier_with_champs.id) + it "appears in the result list" do + expect(current_path).to eq(dossiers_path) + expect(page).to have_link(dossier_en_construction.procedure.libelle) + expect(page).to have_link(dossier_with_champs.procedure.libelle) + expect(page).to have_text("2 sur 2 dossiers") + end + + it "can be filtered by procedure and display the result - one item" do + select dossier_en_construction.procedure.libelle, from: 'procedure_id' + expect(page).to have_link(dossier_en_construction.procedure.libelle) + expect(page).not_to have_link(dossier_with_champs.procedure.libelle) + expect(page).to have_text("1 dossier") + end + + it "can be filtered by procedure and display the result - no item" do + select dossier_brouillon.procedure.libelle, from: 'procedure_id' + expect(page).not_to have_link(dossier_en_construction.id) + expect(page).not_to have_link(dossier_with_champs.id) + expect(page).to have_content("Résultat de la recherche pour « #{dossier_en_construction.champs_public.first.value} » et pour la procédure « #{dossier_brouillon.procedure.libelle} » ") + expect(page).to have_text("Aucun dossier") end end end