Browse Source

Organisation search (#610)

* Add search to organisations

* Fix title

* Spec page title

* Don't seed org in test
pull/619/head
baarkerlounger 3 years ago committed by baarkerlounger
parent
commit
d8fa4df515
  1. 10
      app/components/search_component.rb
  2. 13
      app/controllers/modules/search_filter.rb
  3. 9
      app/controllers/modules/users_filter.rb
  4. 16
      app/controllers/organisations_controller.rb
  5. 12
      app/controllers/users_controller.rb
  6. 3
      app/models/organisation.rb
  7. 8
      app/views/organisations/_organisation_list.html.erb
  8. 15
      app/views/organisations/index.html.erb
  9. 2
      app/views/users/_user_list.html.erb
  10. 9
      app/views/users/index.html.erb
  11. 90
      db/seeds.rb
  12. 1
      spec/components/search_component_spec.rb
  13. 56
      spec/controllers/modules/search_filter_spec.rb
  14. 31
      spec/controllers/modules/users_filter_spec.rb
  15. 21
      spec/models/organisation_spec.rb
  16. 52
      spec/requests/organisations_controller_spec.rb
  17. 4
      spec/requests/users_controller_spec.rb

10
app/components/search_component.rb

@ -9,6 +9,16 @@ class SearchComponent < ViewComponent::Base
end end
def path(current_user) def path(current_user)
if request.path.include?("users")
user_path(current_user)
elsif request.path.include?("organisations")
organisations_path
end
end
private
def user_path(current_user)
current_user.support? ? users_path : users_organisation_path(current_user.organisation) current_user.support? ? users_path : users_organisation_path(current_user.organisation)
end end
end end

13
app/controllers/modules/search_filter.rb

@ -0,0 +1,13 @@
module Modules::SearchFilter
def filtered_collection(base_collection, search_term = nil)
if search_term.present?
base_collection.search_by(search_term)
else
base_collection
end
end
def filtered_users(base_collection, search_term = nil)
filtered_collection(base_collection, search_term).filter_by_active.includes(:organisation)
end
end

9
app/controllers/modules/users_filter.rb

@ -1,9 +0,0 @@
module Modules::UsersFilter
def filtered_users(base_collection, search_term = nil)
if search_term.present?
base_collection.search_by(search_term)
else
base_collection
end.filter_by_active.includes(:organisation)
end
end

16
app/controllers/organisations_controller.rb

@ -1,7 +1,7 @@
class OrganisationsController < ApplicationController class OrganisationsController < ApplicationController
include Pagy::Backend include Pagy::Backend
include Modules::CaseLogsFilter include Modules::CaseLogsFilter
include Modules::UsersFilter include Modules::SearchFilter
before_action :authenticate_user!, except: [:index] before_action :authenticate_user!, except: [:index]
before_action :find_resource, except: [:index] before_action :find_resource, except: [:index]
@ -10,7 +10,10 @@ class OrganisationsController < ApplicationController
def index def index
redirect_to organisation_path(current_user.organisation) unless current_user.support? redirect_to organisation_path(current_user.organisation) unless current_user.support?
@pagy, @organisations = pagy(Organisation.all) all_organisations = Organisation.all
@pagy, @organisations = pagy(filtered_collection(all_organisations, search_term))
@searched = search_term.presence
@total_count = all_organisations.size
end end
def show def show
@ -18,8 +21,9 @@ class OrganisationsController < ApplicationController
end end
def users def users
@pagy, @users = pagy(filtered_users(@organisation.users, params["search"])) @pagy, @users = pagy(filtered_users(@organisation.users, search_term))
@searched = params["search"].presence @searched = search_term.presence
@total_count = @organisation.users.size
render "users/index" render "users/index"
end end
@ -64,6 +68,10 @@ private
params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone) params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone)
end end
def search_term
params["search"]
end
def authenticate_scope! def authenticate_scope!
render_not_found if current_user.organisation != @organisation && !current_user.support? render_not_found if current_user.organisation != @organisation && !current_user.support?
end end

12
app/controllers/users_controller.rb

@ -2,7 +2,7 @@ class UsersController < ApplicationController
include Pagy::Backend include Pagy::Backend
include Devise::Controllers::SignInOut include Devise::Controllers::SignInOut
include Helpers::Email include Helpers::Email
include Modules::UsersFilter include Modules::SearchFilter
before_action :authenticate_user! before_action :authenticate_user!
before_action :find_resource, except: %i[new create] before_action :find_resource, except: %i[new create]
before_action :authenticate_scope!, except: %i[new] before_action :authenticate_scope!, except: %i[new]
@ -10,8 +10,10 @@ class UsersController < ApplicationController
def index def index
redirect_to users_organisation_path(current_user.organisation) unless current_user.support? redirect_to users_organisation_path(current_user.organisation) unless current_user.support?
@pagy, @users = pagy(filtered_users(User.all, params["search"])) all_users = User.all
@searched = params["search"].presence @pagy, @users = pagy(filtered_users(all_users, search_term))
@searched = search_term.presence
@total_count = all_users.size
respond_to do |format| respond_to do |format|
format.html format.html
@ -91,6 +93,10 @@ private
[attribute.to_s.humanize.capitalize, message].join(" ") [attribute.to_s.humanize.capitalize, message].join(" ")
end end
def search_term
params["search"]
end
def password_params def password_params
{ password: SecureRandom.hex(8) } { password: SecureRandom.hex(8) }
end end

3
app/models/organisation.rb

@ -6,6 +6,9 @@ class Organisation < ApplicationRecord
has_many :organisation_las has_many :organisation_las
has_many :organisation_rent_periods has_many :organisation_rent_periods
scope :search_by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
scope :search_by, ->(param) { search_by_name(param) }
has_paper_trail has_paper_trail
PROVIDER_TYPE = { PROVIDER_TYPE = {

8
app/views/organisations/_organisation_list.html.erb

@ -1,8 +1,10 @@
<% content_for :title, title %>
<%= govuk_table do |table| %> <%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<strong><%= @pagy.count %></strong> total <%= title.downcase %>. <% if searched.present? %>
<strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total organisations. <%= govuk_link_to("Clear search", request.path) %>
<% else %>
<strong><%= pagy.count %></strong> total organisations.
<% end %>
<% end %> <% end %>
<%= table.head do |head| %> <%= table.head do |head| %>
<%= head.row do |row| %> <%= head.row do |row| %>

15
app/views/organisations/index.html.erb

@ -1,2 +1,15 @@
<%= render partial: "organisation_list", locals: { organisations: @organisations, title: "Organisations", pagy: @pagy } %> <% item_label = @pagy.count > 1 ? "organisations" : "organisation" %>
<% if @searched.present? %>
<% title = "Organisations (#{@pagy.count} #{item_label} matching ‘#{@searched}’ of #{@total_count} total organisations)" %>
<% else %>
<% title = "Organisations" %>
<% end %>
<% content_for :title, title %>
<%= render SearchComponent.new(current_user:, search_label: "Search by organisation name", value: @searched) %>
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%= render partial: "organisation_list", locals: { organisations: @organisations, title: "Organisations", pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "organisations" } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "organisations" } %>

2
app/views/users/_user_list.html.erb

@ -2,7 +2,7 @@
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %> <%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<span class="govuk-!-margin-right-4"> <span class="govuk-!-margin-right-4">
<% if searched.present? %> <% if searched.present? %>
<strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_user_count %></strong> total users. <%= govuk_link_to("Clear search", request.path) %> <strong><%= pagy.count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total users. <%= govuk_link_to("Clear search", request.path) %>
<% else %> <% else %>
<strong><%= pagy.count %></strong> total users. <strong><%= pagy.count %></strong> total users.
<% end %> <% end %>

9
app/views/users/index.html.erb

@ -1,10 +1,7 @@
<% item_label = @pagy.count > 1 ? "users" : "user" %>
<% if @searched.present? %> <% if @searched.present? %>
<% item_label = @pagy.count > 1 ? "users" : "user" %> <% title = "Your organisation (#{@pagy.count} #{item_label} matching ‘#{@searched}’ of #{@total_count} total users)" %>
<% total_user_count = User.all.count %>
<% title = "Your organisation (#{@pagy.count} #{item_label} matching ‘#{@searched}’ of #{total_user_count} total users)" %>
<% else %> <% else %>
<% item_label = "" %>
<% total_user_count = nil %>
<% title = "Your organisation (Users)" %> <% title = "Your organisation (Users)" %>
<% end %> <% end %>
@ -22,5 +19,5 @@
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m"> <hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%= render partial: "users/user_list", locals: { users: @users, title:, pagy: @pagy, searched: @searched, item_label:, total_user_count: } %> <%= render partial: "users/user_list", locals: { users: @users, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "users" } %> <%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "users" } %>

90
db/seeds.rb

@ -7,56 +7,58 @@
# Character.create(name: 'Luke', movie: movies.first) # Character.create(name: 'Luke', movie: movies.first)
# rubocop:disable Rails/Output # rubocop:disable Rails/Output
org = Organisation.find_or_create_by!( unless Rails.env.test?
name: "DLUHC", org = Organisation.find_or_create_by!(
address_line1: "2 Marsham Street", name: "DLUHC",
address_line2: "London", address_line1: "2 Marsham Street",
postcode: "SW1P 4DF", address_line2: "London",
holds_own_stock: false, postcode: "SW1P 4DF",
other_stock_owners: "None", holds_own_stock: false,
managing_agents: "None", other_stock_owners: "None",
provider_type: "LA", managing_agents: "None",
) do provider_type: "LA",
info = "Seeded DLUHC Organisation" ) do
if Rails.env.development? info = "Seeded DLUHC Organisation"
pp info if Rails.env.development?
else pp info
Rails.logger.info info else
Rails.logger.info info
end
end end
end
if Rails.env.development? && User.count.zero? if Rails.env.development? && User.count.zero?
User.create!( User.create!(
email: "provider@example.com", email: "provider@example.com",
password: "password", password: "password",
organisation: org, organisation: org,
role: "data_provider", role: "data_provider",
confirmed_at: Time.zone.now, confirmed_at: Time.zone.now,
) )
User.create!( User.create!(
email: "coordinator@example.com", email: "coordinator@example.com",
password: "password", password: "password",
organisation: org, organisation: org,
role: "data_coordinator", role: "data_coordinator",
confirmed_at: Time.zone.now, confirmed_at: Time.zone.now,
) )
User.create!( User.create!(
email: "support@example.com", email: "support@example.com",
password: "password", password: "password",
organisation: org, organisation: org,
role: "support", role: "support",
confirmed_at: Time.zone.now, confirmed_at: Time.zone.now,
) )
pp "Seeded 3 dummy users" pp "Seeded 3 dummy users"
end end
if LaRentRange.count.zero? && !Rails.env.test? if LaRentRange.count.zero?
Dir.glob("config/rent_range_data/*.csv").each do |path| Dir.glob("config/rent_range_data/*.csv").each do |path|
start_year = File.basename(path, ".csv") start_year = File.basename(path, ".csv")
Rake::Task["data_import:rent_ranges"].invoke(start_year, path) Rake::Task["data_import:rent_ranges"].invoke(start_year, path)
end
end end
end end
# rubocop:enable Rails/Output # rubocop:enable Rails/Output

1
spec/components/search_component_spec.rb

@ -7,6 +7,7 @@ RSpec.describe SearchComponent, type: :component do
let(:value) { nil } let(:value) { nil }
before do before do
allow(request).to receive(:path).and_return("/users")
render_inline(described_class.new(current_user:, search_label:, value:)) render_inline(described_class.new(current_user:, search_label:, value:))
end end

56
spec/controllers/modules/search_filter_spec.rb

@ -0,0 +1,56 @@
require "rails_helper"
RSpec.describe Modules::SearchFilter do
subject(:instance) { Class.new.include(described_class).new }
describe "filtered_collection" do
before do
FactoryBot.create_list(:organisation, 5)
FactoryBot.create(:organisation, name: "Acme LTD")
end
let(:organisation_list) { Organisation.all }
context "when given a search term" do
let(:search_term) { "Acme" }
it "filters the collection on search term" do
expect(instance.filtered_collection(organisation_list, search_term).count).to eq(1)
end
end
context "when not given a search term" do
let(:search_term) { nil }
it "does not filter the given collection" do
expect(instance.filtered_collection(organisation_list, search_term).count).to eq(6)
end
end
end
describe "filtered_users" do
before do
FactoryBot.create_list(:user, 5)
FactoryBot.create(:user, name: "Joe Blogg")
FactoryBot.create(:user, name: "Tom Blogg", active: false)
end
let(:user_list) { User.all }
context "when given a search term" do
let(:search_term) { "Blogg" }
it "filters the collection on search term and active users" do
expect(instance.filtered_users(user_list, search_term).count).to eq(1)
end
end
context "when not given a search term" do
let(:search_term) { nil }
it "filters the collection on active users" do
expect(instance.filtered_users(user_list, search_term).count).to eq(6)
end
end
end
end

31
spec/controllers/modules/users_filter_spec.rb

@ -1,31 +0,0 @@
require "rails_helper"
RSpec.describe Modules::UsersFilter do
describe "filtered_users" do
subject(:instance) { Class.new.include(described_class).new }
before do
FactoryBot.create_list(:user, 5)
FactoryBot.create(:user, name: "Joe Blogg")
FactoryBot.create(:user, name: "Tom Blogg", active: false)
end
let(:user_list) { User.all }
context "when given a search term" do
let(:search_term) { "Blogg" }
it "filters the collection on search term and active users" do
expect(instance.filtered_users(user_list, search_term).count).to eq(1)
end
end
context "when not given a search term" do
let(:search_term) { nil }
it "filters the collection on active users" do
expect(instance.filtered_users(user_list, search_term).count).to eq(6)
end
end
end
end

21
spec/models/organisation_spec.rb

@ -138,4 +138,25 @@ RSpec.describe Organisation, type: :model do
expect(organisation.paper_trail.previous_version.name).to eq("DLUHC") expect(organisation.paper_trail.previous_version.name).to eq("DLUHC")
end end
end end
describe "scopes" do
before do
FactoryBot.create(:organisation, name: "Joe Bloggs")
FactoryBot.create(:organisation, name: "Tom Smith")
end
context "when searching by name" do
it "returns case insensitive matching records" do
expect(described_class.search_by_name("Joe").count).to eq(1)
expect(described_class.search_by_name("joe").count).to eq(1)
end
end
context "when searching by all searchable field" do
it "returns case insensitive matching records" do
expect(described_class.search_by("Joe").count).to eq(1)
expect(described_class.search_by("joe").count).to eq(1)
end
end
end
end end

52
spec/requests/organisations_controller_spec.rb

@ -364,6 +364,10 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_content("#{total_number_of_orgs} total organisations") expect(page).to have_content("#{total_number_of_orgs} total organisations")
end end
it "shows a search bar" do
expect(page).to have_field("search", type: "search")
end
context "when viewing a specific organisation" do context "when viewing a specific organisation" do
let(:number_of_org1_case_logs) { 2 } let(:number_of_org1_case_logs) { 2 }
let(:number_of_org2_case_logs) { 4 } let(:number_of_org2_case_logs) { 4 }
@ -478,6 +482,54 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_title("Organisations (page 2 of 2)") expect(page).to have_title("Organisations (page 2 of 2)")
end end
end end
context "when searching" do
let!(:searched_organisation) { FactoryBot.create(:organisation, name: "Unusual name") }
let!(:other_organisation) { FactoryBot.create(:organisation, name: "Some other name") }
let(:search_param) { "Unusual" }
before do
get "/organisations?search=#{search_param}"
end
it "returns matching results" do
expect(page).to have_content(searched_organisation.name)
expect(page).not_to have_content(other_organisation.name)
end
it "updates the table caption" do
expect(page).to have_content("1 organisation found matching ‘#{search_param}’ of 29 total organisations.")
end
it "has search in the title" do
expect(page).to have_title("Organisations (1 organisation matching ‘#{search_param}’ of 29 total organisations) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
context "when the search term matches more than 1 result" do
let(:search_param) { "name" }
it "returns matching results" do
expect(page).to have_content(searched_organisation.name)
expect(page).to have_content(other_organisation.name)
end
it "updates the table caption" do
expect(page).to have_content("2 organisations found matching ‘#{search_param}’ of 29 total organisations.")
end
it "has search in the title" do
expect(page).to have_title("Organisations (2 organisations matching ‘#{search_param}’ of 29 total organisations) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
end
context "when search results require pagination" do
let(:search_param) { "DLUHC" }
it "has search and pagination in the title" do
expect(page).to have_title("Organisations (27 organisations matching ‘#{search_param}’ of 29 total organisations) (page 1 of 2) - Submit social housing lettings and sales data (CORE) - GOV.UK")
end
end
end
end end
end end
end end

4
spec/requests/users_controller_spec.rb

@ -381,7 +381,7 @@ RSpec.describe UsersController, type: :request do
end end
it "updates the table caption" do it "updates the table caption" do
expect(page).to have_content("1 user found matching ‘filter’ of 5 total users.") expect(page).to have_content("1 user found matching ‘filter’ of 4 total users.")
end end
end end
@ -417,7 +417,7 @@ RSpec.describe UsersController, type: :request do
end end
it "updates the table caption" do it "updates the table caption" do
expect(page).to have_content("2 users found matching ‘joe’ of 5 total users.") expect(page).to have_content("2 users found matching ‘joe’ of 4 total users.")
end end
end end
end end

Loading…
Cancel
Save