make dossiers search compatible with filter by procedure

This commit is contained in:
Lisa Durand 2023-07-26 17:51:04 +02:00
parent 08bb62d417
commit 5ff5923612
8 changed files with 79 additions and 36 deletions

View file

@ -1,4 +1,5 @@
= form_with(url: dossiers_path, method: :get, data: { controller: 'autosubmit' } ) do |f| = 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' = f.label :procedure_id, t('.procedure.label'), class: 'sr-only'
.fr-input-group .fr-input-group
= f.select :procedure_id, options_for_select(@procedures_for_select, params[:procedure_id]), { prompt: t('.procedures.prompt') }, class: 'fr-select' = f.select :procedure_id, options_for_select(@procedures_for_select, params[:procedure_id]), { prompt: t('.procedures.prompt') }, class: 'fr-select'

View file

@ -33,21 +33,28 @@ module Users
.distinct(:procedure_id) .distinct(:procedure_id)
.order(:libelle) .order(:libelle)
.pluck(:libelle, :id) .pluck(:libelle, :id)
@procedure_id = params[:procedure_id]
@procedure_id = params[:procedure_id]
if @procedure_id.present? if @procedure_id.present?
ordered_dossiers = ordered_dossiers.where(procedures: { id: @procedure_id }) ordered_dossiers = ordered_dossiers.where(procedures: { id: @procedure_id })
deleted_dossiers = deleted_dossiers.where(procedures: { id: @procedure_id }) deleted_dossiers = deleted_dossiers.where(procedures: { id: @procedure_id })
end 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_visibles = ordered_dossiers.visible_by_user
@dossiers_traites = current_user.dossiers.state_termine.merge(dossiers_visibles)
@dossiers_invites = current_user.dossiers_invites.merge(dossiers_visibles) @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) @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) @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_close_to_expiration = current_user.dossiers.close_to_expiration.merge(@dossiers_visibles)
@dossiers_supprimes_definitivement = deleted_dossiers @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]) @statut = statut(@user_dossiers, @dossiers_traites, @dossiers_invites, @dossiers_supprimes_recemment, @dossiers_supprimes_definitivement, @dossier_transferes, @dossiers_close_to_expiration, params[:statut])

View file

@ -10,7 +10,7 @@ class DossierSearchService
def self.matching_dossiers_for_user(search_terms, user) def self.matching_dossiers_for_user(search_terms, user)
dossier_by_exact_id_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 end
private private
@ -38,7 +38,7 @@ class DossierSearchService
def self.dossier_by_full_text_for_user(search_terms, dossiers) def self.dossier_by_full_text_for_user(search_terms, dossiers)
ts_vector = "to_tsvector('french', search_terms)" 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 dossiers
.visible_by_user .visible_by_user
@ -49,9 +49,9 @@ class DossierSearchService
def self.dossier_by_exact_id_for_user(search_terms, user) def self.dossier_by_exact_id_for_user(search_terms, user)
id = search_terms.to_i 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. 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 else
Dossier.none Dossier.includes(:procedure).none
end end
end end

View file

@ -94,7 +94,7 @@
- else - else
- if filter.filter_params.present? - if filter.present?
.blank-tab .blank-tab
%h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title') %h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title')
%p.empty-text-details %p.empty-text-details
@ -102,6 +102,13 @@
%br %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' = 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 - else
.blank-tab .blank-tab
%h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title') %h2.empty-text= t('views.users.dossiers.dossiers_list.no_result_title')

View file

@ -14,7 +14,8 @@
- if current_user.dossiers.count > 2 || current_user.dossiers_invites.count > 2 - if current_user.dossiers.count > 2 || current_user.dossiers_invites.count > 2
.fr-col .fr-col
#search-2.fr-search-bar #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' = 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" = 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 %button.fr-btn.fr-btn--sm
@ -71,7 +72,7 @@
.fr-container .fr-container
.fr-grid-row.fr-grid-row--center .fr-grid-row.fr-grid-row--center
.fr-col-xl-10 .fr-col-xl-10
- if @statut == "en-cours" - if @statut == "en-cours" && @search_terms.blank?
- if @first_brouillon_recently_updated.present? - 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| = render Dsfr::CalloutComponent.new(title: t('users.dossiers.header.callout.first_brouillon_recently_updated_title'), heading_level: 'h2') do |c|
- c.with_body do - 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' = 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? - if @search_terms.present?
%h2.page-title Résultat de la recherche pour « #{@search_terms} » %h2.page-title
= render partial: "dossiers_list", locals: { dossiers: @dossiers } = 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 - else
= render Dossiers::UserFilterComponent.new(statut: @statut, filter: @filter, procedure_id: @procedure_id ) = 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 -# /!\ in this context, @dossiers is a collection of DeletedDossier not Dossier
= render partial: "deleted_dossiers_list", locals: { deleted_dossiers: @dossiers } = render partial: "deleted_dossiers_list", locals: { deleted_dossiers: @dossiers }
- else - 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 }

View file

@ -456,6 +456,8 @@ en:
search: search:
search_file: Search a file (File number, keywords) search_file: Search a file (File number, keywords)
simple: Search simple: Search
result_term_title: Search result for « %{search_terms} »
result_procedure_title: and procedure « %{procedure_libelle} »
secondary_menu: Secondary menu secondary_menu: Secondary menu
index: index:
dossiers: "My files" dossiers: "My files"
@ -463,6 +465,8 @@ en:
n_dossier: "File n." n_dossier: "File n."
no_result_title: No files no_result_title: No files
no_result_text_html: "To fill a procedure, contact your administration asking for the procedure link. <br> It should look like %{app_base}/commencer/xxx." no_result_text_html: "To fill a procedure, contact your administration asking for the procedure link. <br> 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_text_with_filter: found with selected filters
no_result_reset_filter: Reset 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. 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.

View file

@ -459,6 +459,9 @@ fr:
search: search:
search_file: Rechercher un dossier (N° de dossier, mots-clés) search_file: Rechercher un dossier (N° de dossier, mots-clés)
simple: Rechercher 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 secondary_menu: Menu secondaire
index: index:
dossiers: "Mes dossiers" dossiers: "Mes dossiers"
@ -466,6 +469,8 @@ fr:
n_dossier: "dossier Nº " n_dossier: "dossier Nº "
no_result_title: Aucun dossier 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. <br> Celui ci doit ressembler à %{app_base}/commencer/xxx." no_result_text_html: "Pour remplir une démarche, contactez votre administration en lui demandant le lien de la démarche. <br> 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_text_with_filter: ne correspond aux filtres sélectionnés
no_result_reset_filter: Réinitialiser les filtres 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 dinformation. 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 dinformation.

View file

@ -190,7 +190,7 @@ describe 'user access to the list of their dossiers', js: true, retry: 3 do
end end
end end
describe "recherche" do describe "user search bar" do
context "when the dossier does not exist" do context "when the dossier does not exist" do
before do before do
page.find_by_id('q').set(10000000) 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 it "shows an error message on the dossiers page" do
expect(current_path).to eq(dossiers_path) expect(current_path).to eq(dossiers_path)
expect(page).to have_content("Vous navez 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
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 it "shows an error message on the dossiers page" do
expect(current_path).to eq(dossiers_path) expect(current_path).to eq(dossiers_path)
expect(page).to have_content("Vous navez 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
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 find('.fr-search-bar .fr-btn').click
end end
it "redirects to the dossier page" do it "appears in the result list" do
expect(current_path).to eq(dossier_path(dossier_en_construction)) 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
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) page.find_by_id('q').set(dossier_en_construction.champs_public.first.value)
end end
context 'when it only matches one dossier' do context 'when it matches multiple dossiers' 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
let!(:dossier_with_champs) { create(:dossier, :with_populated_champs, :en_construction, user: user) } let!(:dossier_with_champs) { create(:dossier, :with_populated_champs, :en_construction, user: user) }
before do before do
find('.fr-search-bar .fr-btn').click find('.fr-search-bar .fr-btn').click
end end
it "redirects to the search results" do it "appears in the result list" do
expect(current_path).to eq(recherche_dossiers_path) expect(current_path).to eq(dossiers_path)
expect(page).to have_content(dossier_en_construction.id) expect(page).to have_link(dossier_en_construction.procedure.libelle)
expect(page).to have_content(dossier_with_champs.id) 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 end
end end