Browse Source

CLDC-3564 Update filter search (#2535)

* Add search controller

* Update on confirm

* lint

* Update search endpoint

* remove assigned to filter options

* Explicitly define host in the url

* Update search controller

* Allow searching organisations

* Allow filtering by user without js

* Allow filtering by org without js

* Add filter_type to method calls

* Update filter helpers

* Hide text search input when js is enabled

* Some feature test updates

* fix model test

* Delete a random file 👀

* lint

* Update more tests

* Update inner text for filter

* Keep csv filters the same

* Clear free text filters for csv dowloads

* User path helper

* Update scheme filters and log scopes

* Update which users we can filter by
pull/2540/head^2
kosiakkatrina 4 months ago committed by GitHub
parent
commit
72890e516e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 15
      app/controllers/organisations_controller.rb
  2. 11
      app/controllers/users_controller.rb
  3. 3
      app/frontend/controllers/index.js
  4. 46
      app/frontend/controllers/search_controller.js
  5. 49
      app/frontend/modules/search.js
  6. 82
      app/helpers/filters_helper.rb
  7. 4
      app/models/lettings_log.rb
  8. 3
      app/models/log.rb
  9. 1
      app/models/organisation.rb
  10. 5
      app/models/user.rb
  11. 11
      app/services/filter_manager.rb
  12. 1
      app/views/filters/_radio_filter.html.erb
  13. 20
      app/views/filters/_text_select_filter.html.erb
  14. 3
      app/views/filters/assigned_to.html.erb
  15. 3
      app/views/filters/managed_by.html.erb
  16. 3
      app/views/filters/owned_by.html.erb
  17. 15
      app/views/logs/_log_filters.html.erb
  18. 2
      app/views/schemes/_scheme_filters.html.erb
  19. 8
      config/routes.rb
  20. 4
      spec/features/lettings_log_spec.rb
  21. 6
      spec/features/organisation_spec.rb
  22. 7
      spec/features/user_spec.rb
  23. 298
      spec/helpers/filters_helper_spec.rb
  24. 6
      spec/models/user_spec.rb
  25. 45
      spec/requests/organisations_controller_spec.rb
  26. 61
      spec/requests/users_controller_spec.rb

15
app/controllers/organisations_controller.rb

@ -4,8 +4,8 @@ class OrganisationsController < ApplicationController
include DuplicateLogsHelper
before_action :authenticate_user!
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
before_action :find_resource, except: %i[index new create search]
before_action :authenticate_scope!, except: %i[index search]
before_action :session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
before_action :session_filters, only: %i[users schemes email_schemes_csv download_schemes_csv]
before_action -> { filter_manager.serialize_filters_to_session }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
@ -280,6 +280,17 @@ class OrganisationsController < ApplicationController
render "schemes/changes"
end
def search
org_options = current_user.support? ? Organisation.all : Organisation.affiliated_organisations(current_user.organisation)
organisations = org_options.search_by(params["query"]).limit(20)
org_data = organisations.each_with_object({}) do |org, hash|
hash[org.id] = { value: org.name }
end
render json: org_data.to_json
end
private
def filter_type

11
app/controllers/users_controller.rb

@ -32,6 +32,17 @@ class UsersController < ApplicationController
end
end
def search
user_options = current_user.support? ? User.all : User.own_and_managing_org_users(current_user.organisation)
users = user_options.search_by(params["query"]).limit(20)
user_data = users.each_with_object({}) do |user, hash|
hash[user.id] = { value: user.name, hint: user.email }
end
render json: user_data.to_json
end
def resend_invite
@user.send_confirmation_instructions
flash[:notice] = "Invitation sent to #{@user.email}"

3
app/frontend/controllers/index.js

@ -13,6 +13,8 @@ import GovukfrontendController from './govukfrontend_controller.js'
import NumericQuestionController from './numeric_question_controller.js'
import SearchController from './search_controller.js'
import FilterLayoutController from './filter_layout_controller.js'
application.register('accessible-autocomplete', AccessibleAutocompleteController)
application.register('conditional-filter', ConditionalFilterController)
@ -20,3 +22,4 @@ application.register('conditional-question', ConditionalQuestionController)
application.register('govukfrontend', GovukfrontendController)
application.register('numeric-question', NumericQuestionController)
application.register('filter-layout', FilterLayoutController)
application.register('search', SearchController)

46
app/frontend/controllers/search_controller.js

@ -0,0 +1,46 @@
import { Controller } from '@hotwired/stimulus'
import accessibleAutocomplete from 'accessible-autocomplete'
import 'accessible-autocomplete/dist/accessible-autocomplete.min.css'
import { searchSuggestion, fetchAndPopulateSearchResults, confirmSelectedOption, searchableName } from '../modules/search'
const options = []
const populateOptions = (results, selectEl) => {
selectEl.innerHTML = ''
Object.keys(results).forEach((key) => {
const option = document.createElement('option')
option.value = key
option.innerHTML = results[key].value
if (results[key].hint) { option.setAttribute('data-hint', results[key].hint) }
option.setAttribute('text', searchableName(results[key]))
selectEl.appendChild(option)
options.push(option)
})
}
export default class extends Controller {
connect () {
const selectEl = this.element
const matches = /^(\w+)\[(\w+)\]$/.exec(selectEl.name)
const rawFieldName = matches ? `${matches[1]}[${matches[2]}_raw]` : ''
const searchUrl = JSON.parse(this.element.dataset.info).search_url
document.querySelectorAll('.non-js-text-search-input-field').forEach((el) => {
el.style.display = 'none'
})
accessibleAutocomplete.enhanceSelectElement({
defaultValue: '',
selectElement: selectEl,
minLength: 1,
source: (query, populateResults) => {
fetchAndPopulateSearchResults(query, populateResults, searchUrl, populateOptions, selectEl)
},
autoselect: true,
placeholder: 'Start typing to search',
templates: { suggestion: (value) => searchSuggestion(value, options) },
name: rawFieldName,
onConfirm: (val) => confirmSelectedOption(selectEl, val)
})
}
}

49
app/frontend/modules/search.js

@ -117,6 +117,22 @@ export const suggestion = (value, options) => {
}
}
export const searchSuggestion = (value, options) => {
try {
const option = options.find((o) => o.getAttribute('text') === value)
if (option) {
const result = enhanceOption(option)
const html = result.append ? `<span class="autocomplete__option__append">${result.text}</span> <span>${result.append}</span>` : `<span>${result.text}</span>`
return result.hint ? `${html}<div class="autocomplete__option__hint">${result.hint}</div>` : html
} else {
return '<span>No results found</span>'
}
} catch (error) {
console.error('Error fetching user option:', error)
return value
}
}
export const enhanceOption = (option) => {
return {
text: option.text,
@ -128,6 +144,39 @@ export const enhanceOption = (option) => {
}
}
export const fetchAndPopulateSearchResults = async (query, populateResults, relativeUrlRoute, populateOptions, selectEl) => {
if (/\S/.test(query)) {
const results = await fetchUserOptions(query, relativeUrlRoute)
populateOptions(results, selectEl)
populateResults(Object.values(results).map((o) => searchableName(o)))
}
}
export const fetchUserOptions = async (query, searchUrl) => {
try {
const response = await fetch(`${searchUrl}?query=${encodeURIComponent(query)}`)
const results = await response.json()
return results
} catch (error) {
console.error('Error fetching user options:', error)
return []
}
}
export const getSearchableName = (option) => {
return option.getAttribute('data-hint') ? option.text + ' ' + option.getAttribute('data-hint') : option.text
}
export const searchableName = (option) => {
return option.hint ? option.value + ' ' + option.hint : option.value
}
export const confirmSelectedOption = (selectEl, val) => {
const arrayOfOptions = Array.from(selectEl.options).filter(function (option, index, arr) { return option.value !== '' })
const selectedOption = [].filter.call(
arrayOfOptions,
(option) => option.getAttribute('text') === val
)[0]
if (selectedOption) selectedOption.selected = true
}

82
app/helpers/filters_helper.rb

@ -11,8 +11,8 @@ module FiltersHelper
return true if !selected_filters.key?("owning_organisation") && filter == "owning_organisation_select" && value == :all
return true if !selected_filters.key?("managing_organisation") && filter == "managing_organisation_select" && value == :all
return true if selected_filters["owning_organisation"].present? && filter == "owning_organisation_select" && value == :specific_org
return true if selected_filters["managing_organisation"].present? && filter == "managing_organisation_select" && value == :specific_org
return true if (selected_filters["owning_organisation"].present? || selected_filters["owning_organisation_text_search"].present?) && filter == "owning_organisation_select" && value == :specific_org
return true if (selected_filters["managing_organisation"].present? || selected_filters["managing_organisation_text_search"].present?) && filter == "managing_organisation_select" && value == :specific_org
return false if selected_filters[filter].blank?
@ -84,16 +84,54 @@ module FiltersHelper
JSON.parse(session[session_name_for(filter_type)])[filter] || ""
end
def owning_organisation_filter_options(user)
def all_owning_organisation_filter_options(user)
organisation_options = user.support? ? Organisation.all : ([user.organisation] + user.organisation.stock_owners + user.organisation.absorbed_organisations).uniq
[OpenStruct.new(id: "", name: "Select an option")] + organisation_options.map { |org| OpenStruct.new(id: org.id, name: org.name) }
end
def assigned_to_filter_options(user)
def owning_organisation_filter_options(user, filter_type)
if applied_filters(filter_type)["owning_organisation"].present?
organisation_id = applied_filters(filter_type)["owning_organisation"]
org = if user.support?
Organisation.where(id: organisation_id)&.first
else
Organisation.affiliated_organisations(user.organisation).where(id: organisation_id)&.first
end
return [OpenStruct.new(id: org.id, name: org.name)] if org.present?
end
[OpenStruct.new(id: "", name: "Select an option")]
end
def assigned_to_csv_filter_options(user)
user_options = user.support? ? User.all : (user.organisation.users + user.organisation.managing_agents.flat_map(&:users) + user.organisation.stock_owners.flat_map(&:users)).uniq
[OpenStruct.new(id: "", name: "Select an option", hint: "")] + user_options.map { |user_option| OpenStruct.new(id: user_option.id, name: user_option.name, hint: user_option.email) }
end
def assigned_to_filter_options(filter_type)
if applied_filters(filter_type)["assigned_to"] == "specific_user" && applied_filters(filter_type)["user"].present?
user_id = applied_filters(filter_type)["user"]
selected_user = if current_user.support?
User.where(id: user_id)&.first
else
User.own_and_managing_org_users(current_user.organisation).where(id: user_id)&.first
end
return [OpenStruct.new(id: selected_user.id, name: selected_user.name, hint: selected_user.email)] if selected_user.present?
end
[OpenStruct.new(id: "", name: "Select an option", hint: "")]
end
def filter_search_url(category)
case category
when :user
search_users_path
when :owning_organisation, :managing_organisation
search_organisations_path
end
end
def collection_year_options
years = {
current_collection_start_year.to_s => year_combo(current_collection_start_year),
@ -125,11 +163,26 @@ module FiltersHelper
end
end
def managing_organisation_filter_options(user)
def managing_organisation_csv_filter_options(user)
organisation_options = user.support? ? Organisation.all : ([user.organisation] + user.organisation.managing_agents + user.organisation.absorbed_organisations).uniq
[OpenStruct.new(id: "", name: "Select an option")] + organisation_options.map { |org| OpenStruct.new(id: org.id, name: org.name) }
end
def managing_organisation_filter_options(user, filter_type)
if applied_filters(filter_type)["managing_organisation"].present?
organisation_id = applied_filters(filter_type)["managing_organisation"]
org = if user.support?
Organisation.where(id: organisation_id)&.first
else
Organisation.affiliated_organisations(user.organisation).where(id: organisation_id)&.first
end
return [OpenStruct.new(id: org.id, name: org.name)] if org.present?
end
[OpenStruct.new(id: "", name: "Select an option")]
end
def show_scheme_managing_org_filter?(user)
org = user.organisation
@ -176,8 +229,8 @@ module FiltersHelper
{ id: "status", label: "Status", value: formatted_status_filter(session_filters) },
filter_type == "lettings_logs" ? { id: "needstype", label: "Needs type", value: formatted_needstype_filter(session_filters) } : nil,
{ id: "assigned_to", label: "Assigned to", value: formatted_assigned_to_filter(session_filters) },
{ id: "owned_by", label: "Owned by", value: formatted_owned_by_filter(session_filters) },
{ id: "managed_by", label: "Managed by", value: formatted_managed_by_filter(session_filters) },
{ id: "owned_by", label: "Owned by", value: formatted_owned_by_filter(session_filters, filter_type) },
{ id: "managed_by", label: "Managed by", value: formatted_managed_by_filter(session_filters, filter_type) },
].compact
end
@ -221,7 +274,7 @@ private
filters.each.sum do |category, category_filters|
if %w[years status needstypes bulk_upload_id].include?(category)
category_filters.count(&:present?)
elsif %w[user owning_organisation managing_organisation].include?(category)
elsif %w[user owning_organisation managing_organisation user_text_search owning_organisation_text_search managing_organisation_text_search].include?(category)
1
else
0
@ -256,26 +309,27 @@ private
return "All" if session_filters["assigned_to"].include?("all")
return "You" if session_filters["assigned_to"].include?("you")
selected_user_option = assigned_to_filter_options(current_user).find { |x| x.id == session_filters["user"].to_i }
User.own_and_managing_org_users(current_user.organisation).find(session_filters["user"].to_i).name
selected_user_option = User.own_and_managing_org_users(current_user.organisation).find(session_filters["user"].to_i)
return unless selected_user_option
"#{selected_user_option.name} (#{selected_user_option.hint})"
"#{selected_user_option.name} (#{selected_user_option.email})"
end
def formatted_owned_by_filter(session_filters)
def formatted_owned_by_filter(session_filters, filter_type)
return "All" if params["id"].blank? && (session_filters["owning_organisation"].blank? || session_filters["owning_organisation"]&.include?("all"))
session_org_id = session_filters["owning_organisation"] || params["id"]
selected_owning_organisation_option = owning_organisation_filter_options(current_user).find { |org| org.id == session_org_id.to_i }
selected_owning_organisation_option = owning_organisation_filter_options(current_user, filter_type).find { |org| org.id == session_org_id.to_i }
return unless selected_owning_organisation_option
selected_owning_organisation_option&.name
end
def formatted_managed_by_filter(session_filters)
def formatted_managed_by_filter(session_filters, filter_type)
return "All" if session_filters["managing_organisation"].blank? || session_filters["managing_organisation"].include?("all")
selected_managing_organisation_option = managing_organisation_filter_options(current_user).find { |org| org.id == session_filters["managing_organisation"].to_i }
selected_managing_organisation_option = managing_organisation_filter_options(current_user, filter_type).find { |org| org.id == session_filters["managing_organisation"].to_i }
return unless selected_managing_organisation_option
selected_managing_organisation_option&.name

4
app/models/lettings_log.rb

@ -132,6 +132,10 @@ class LettingsLog < Log
illness_type_10: false)
}
scope :filter_by_user_text_search, ->(param, user) { where(assigned_to: user.support? ? User.search_by(param) : User.own_and_managing_org_users(user.organisation).search_by(param)) }
scope :filter_by_owning_organisation_text_search, ->(param, _user) { where(owning_organisation: Organisation.search_by(param)) }
scope :filter_by_managing_organisation_text_search, ->(param, _user) { where(managing_organisation: Organisation.search_by(param)) }
AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze
OPTIONAL_FIELDS = %w[tenancycode propcode chcharge].freeze
RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze

3
app/models/log.rb

@ -53,6 +53,9 @@ class Log < ApplicationRecord
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) }
scope :filter_by_owning_organisation, ->(owning_organisation, _user = nil) { where(owning_organisation:) }
scope :filter_by_managing_organisation, ->(managing_organisation, _user = nil) { where(managing_organisation:) }
scope :filter_by_user_text_search, ->(param, user) { where(assigned_to: user.support? ? User.search_by(param) : User.own_and_managing_org_users(user.organisation).search_by(param)) }
scope :filter_by_owning_organisation_text_search, ->(param, _user) { where(owning_organisation: Organisation.search_by(param)) }
scope :filter_by_managing_organisation_text_search, ->(param, _user) { where(managing_organisation: Organisation.search_by(param)) }
attr_accessor :skip_update_status, :skip_update_uprn_confirmed, :select_best_address_match, :skip_dpo_validation

1
app/models/organisation.rb

@ -18,6 +18,7 @@ class Organisation < ApplicationRecord
belongs_to :absorbing_organisation, class_name: "Organisation", optional: true
has_many :absorbed_organisations, class_name: "Organisation", foreign_key: "absorbing_organisation_id"
scope :visible, -> { where(discarded_at: nil) }
scope :affiliated_organisations, ->(organisation) { where(id: (organisation.child_organisations + [organisation] + organisation.parent_organisations + organisation.absorbed_organisations).map(&:id)) }
def affiliated_stock_owners
ids = []

5
app/models/user.rb

@ -77,6 +77,7 @@ class User < ApplicationRecord
scope :deactivated, -> { where(active: false) }
scope :active_status, -> { where(active: true).where.not(last_sign_in_at: nil) }
scope :visible, -> { where(discarded_at: nil) }
scope :own_and_managing_org_users, ->(organisation) { where(organisation: organisation.child_organisations + [organisation]) }
def lettings_logs
if support?
@ -209,9 +210,9 @@ class User < ApplicationRecord
def logs_filters(specific_org: false)
if (support? && !specific_org) || organisation.has_managing_agents? || organisation.has_stock_owners?
%w[years status needstypes assigned_to user managing_organisation owning_organisation bulk_upload_id]
%w[years status needstypes assigned_to user managing_organisation owning_organisation bulk_upload_id user_text_search owning_organisation_text_search managing_organisation_text_search]
else
%w[years status needstypes assigned_to user bulk_upload_id]
%w[years status needstypes assigned_to user bulk_upload_id user_text_search]
end
end

11
app/services/filter_manager.rb

@ -24,6 +24,9 @@ class FilterManager
next if category == "owning_organisation" && all_orgs
next if category == "managing_organisation" && all_orgs
next if category == "assigned_to"
next if category == "user_text_search" && filters["assigned_to"] != "specific_user"
next if category == "owning_organisation_text_search" && all_orgs
next if category == "managing_organisation_text_search" && all_orgs
logs = logs.public_send("filter_by_#{category}", values, user)
end
@ -94,11 +97,19 @@ class FilterManager
new_filters[filter] = params[filter] if params[filter].present?
end
if params["action"] == "download_csv"
new_filters["assigned_to"] = "all" if new_filters["assigned_to"] == "specific_user" && new_filters["user_text_search"].present?
new_filters["owning_organisation_select"] = "all" if new_filters["owning_organisation_select"] == "specific_organisation" && new_filters["owning_organisation_text_search"].present?
new_filters["managing_organisation_select"] = "all" if new_filters["managing_organisation_select"] == "specific_organisation" && new_filters["managing_organisation_text_search"].present?
end
new_filters = new_filters.except("owning_organisation") if params["owning_organisation_select"] == "all"
new_filters = new_filters.except("managing_organisation") if params["managing_organisation_select"] == "all"
new_filters = new_filters.except("user") if params["assigned_to"] == "all"
new_filters["user"] = current_user.id.to_s if params["assigned_to"] == "you"
new_filters = new_filters.except("user_text_search") if params["assigned_to"] == "all" || params["assigned_to"] == "you"
new_filters = new_filters.except("owning_organisation_text_search") if params["owning_organisation_select"] == "all"
new_filters = new_filters.except("managing_organisation_text_search") if params["managing_organisation_select"] == "all"
end
if (filter_type.include?("schemes") || filter_type.include?("users") || filter_type.include?("scheme_locations")) && params["status"].present?

1
app/views/filters/_radio_filter.html.erb

@ -10,6 +10,7 @@
collection: option[:conditional_filter][:options],
category: option[:conditional_filter][:category],
label: option[:conditional_filter][:label],
caption_text: option[:conditional_filter][:caption_text],
secondary: true,
hint_text: option[:conditional_filter][:hint_text],
} %>

20
app/views/filters/_text_select_filter.html.erb

@ -0,0 +1,20 @@
<span class="non-js-text-search-input-field">
<%= f.govuk_text_field "#{category}_text_search".to_sym,
label: { text: label, hidden: secondary },
"data-controller": "search conditional-filter",
caption: { text: caption_text },
"data-info": { search_url: filter_search_url(category.to_sym) }.to_json,
value: selected_option("#{category}_text_search", @filter_type) %>
</span>
<%= f.govuk_select(category.to_sym,
label: { text: label, hidden: secondary },
"data-controller": "search conditional-filter",
"hidden": true,
"data-info": { search_url: filter_search_url(category.to_sym) }.to_json) do %>
<% collection.each do |answer| %>
<option value="<%= answer.id %>"
data-hint="<%= answer.hint %>"
<%= answer.id.to_s == selected_option(category, @filter_type).to_s ? "selected" : "" %>
<%= answer.id == "" ? "disabled" : "" %>><%= answer.name %></option>
<% end %>
<% end %>

3
app/views/filters/assigned_to.html.erb

@ -11,7 +11,8 @@
type: "select",
label: "User",
category: "user",
options: assigned_to_filter_options(current_user),
caption_text: "User's name or email",
options: assigned_to_csv_filter_options(current_user),
},
},
},

3
app/views/filters/managed_by.html.erb

@ -9,7 +9,8 @@
type: "select",
label: "Managed by",
category: "managing_organisation",
options: managing_organisation_filter_options(current_user),
options: managing_organisation_csv_filter_options(current_user),
caption_text: "Organisation name",
},
},
},

3
app/views/filters/owned_by.html.erb

@ -9,7 +9,8 @@
type: "select",
label: "Owning Organisation",
category: "owning_organisation",
options: owning_organisation_filter_options(current_user),
options: all_owning_organisation_filter_options(current_user),
caption_text: "Organisation name",
},
},
},

15
app/views/logs/_log_filters.html.erb

@ -66,10 +66,11 @@
"specific_user": {
label: "Specific user",
conditional_filter: {
type: "select",
type: "text_select",
label: "User",
category: "user",
options: assigned_to_filter_options(current_user),
options: assigned_to_filter_options(@filter_type),
caption_text: "User's name or email",
},
},
},
@ -86,10 +87,11 @@
"specific_org": {
label: "Specific owning organisation",
conditional_filter: {
type: "select",
type: "text_select",
label: "Owning Organisation",
category: "owning_organisation",
options: owning_organisation_filter_options(current_user),
options: owning_organisation_filter_options(current_user, @filter_type),
caption_text: "Organisation name",
},
},
},
@ -107,10 +109,11 @@
"specific_org": {
label: "Specific managing organisation",
conditional_filter: {
type: "select",
type: "text_select",
label: user_or_org_lettings_path? ? "Managed by" : "Reported by",
category: "managing_organisation",
options: managing_organisation_filter_options(current_user),
options: managing_organisation_filter_options(current_user, @filter_type),
caption_text: "Organisation name",
},
},
},

2
app/views/schemes/_scheme_filters.html.erb

@ -35,7 +35,7 @@
type: "select",
label: "Owning Organisation",
category: "owning_organisation",
options: owning_organisation_filter_options(current_user),
options: all_owning_organisation_filter_options(current_user),
},
},
},

8
config/routes.rb

@ -125,6 +125,10 @@ Rails.application.routes.draw do
get "edit-dpo", to: "users#dpo"
get "edit-key-contact", to: "users#key_contact"
collection do
get :search
end
member do
get "deactivate", to: "users#deactivate"
get "reactivate", to: "users#reactivate"
@ -191,6 +195,10 @@ Rails.application.routes.draw do
get "delete-confirmation", to: "organisations#delete_confirmation"
delete "delete", to: "organisations#delete"
end
collection do
get :search
end
end
resources :merge_requests, path: "/merge-request" do

4
spec/features/lettings_log_spec.rb

@ -89,9 +89,9 @@ RSpec.describe "Lettings Log Features" do
check("In progress")
choose("You")
choose("Specific owning organisation")
select(stock_owner_1.name, from: "owning_organisation")
fill_in("owning-organisation-text-search-field", with: "stock")
choose("Specific managing organisation")
select(managing_agent_1.name, from: "managing_organisation")
fill_in("managing-organisation-text-search-field", with: "managing")
click_button("Apply filters")
end

6
spec/features/organisation_spec.rb

@ -199,14 +199,14 @@ RSpec.describe "User Features" do
it "can filter lettings logs by year" do
check("years-2022-field")
click_button("Apply filters")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2022&status[]=&needstypes[]=&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&years[]=2022&status[]=&needstypes[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).not_to have_link first_log.id.to_s, href: "/lettings-logs/#{first_log.id}"
end
it "can filter lettings logs by needstype" do
check("needstypes-1-field")
click_button("Apply filters")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&status[]=&needstypes[]=&needstypes[]=1&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=")
expect(page).to have_current_path("/organisations/#{org_id}/lettings-logs?years[]=&status[]=&needstypes[]=&needstypes[]=1&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
other_general_needs_logs.each do |general_needs_log|
expect(page).to have_link general_needs_log.id.to_s, href: "/lettings-logs/#{general_needs_log.id}"
end
@ -245,7 +245,7 @@ RSpec.describe "User Features" do
end
check("years-2022-field")
click_button("Apply filters")
expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?years[]=&years[]=2022&status[]=&assigned_to=all&user=&owning_organisation_select=all&owning_organisation=&managing_organisation_select=all&managing_organisation=")
expect(page).to have_current_path("/organisations/#{org_id}/sales-logs?years[]=&years[]=2022&status[]=&assigned_to=all&user_text_search=&user=&owning_organisation_select=all&owning_organisation_text_search=&owning_organisation=&managing_organisation_select=all&managing_organisation_text_search=&managing_organisation=")
expect(page).not_to have_link first_log.id.to_s, href: "/sales-logs/#{first_log.id}"
end
end

7
spec/features/user_spec.rb

@ -796,12 +796,13 @@ RSpec.describe "User Features" do
visit("/lettings-logs")
choose("owning-organisation-select-specific-org-field", allow_label_click: true)
expect(page).to have_field("owning-organisation-field", with: "")
find("#owning-organisation-field").click.native.send_keys("F", "i", "l", "t", :down, :enter)
find("#owning-organisation-field").click.native.send_keys("F", "i", "l", "t")
select(parent_organisation.name, from: "owning-organisation-field-select", visible: false)
click_button("Apply filters")
expect(page).to have_current_path("/lettings-logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&%5Bneedstypes%5D%5B%5D=&assigned_to=all&owning_organisation_select=specific_org&owning_organisation=#{parent_organisation.id}&managing_organisation_select=all")
expect(page).to have_current_path("/lettings-logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&%5Bneedstypes%5D%5B%5D=&assigned_to=all&user_text_search=&owning_organisation_select=specific_org&owning_organisation_text_search=&owning_organisation=#{parent_organisation.id}&managing_organisation_select=all&managing_organisation_text_search=")
choose("owning-organisation-select-all-field", allow_label_click: true)
click_button("Apply filters")
expect(page).to have_current_path("/lettings-logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&%5Bneedstypes%5D%5B%5D=&assigned_to=all&owning_organisation_select=all&managing_organisation_select=all")
expect(page).to have_current_path("/lettings-logs?%5Byears%5D%5B%5D=&%5Bstatus%5D%5B%5D=&%5Bneedstypes%5D%5B%5D=&assigned_to=all&user_text_search=&owning_organisation_select=all&owning_organisation_text_search=&managing_organisation_select=all&managing_organisation_text_search=")
end
end
end

298
spec/helpers/filters_helper_spec.rb

@ -175,27 +175,146 @@ RSpec.describe FiltersHelper do
context "with a support user" do
let(:user) { FactoryBot.create(:user, :support, organisation: child_organisation) }
it "returns a list of all organisations" do
expect(owning_organisation_filter_options(user)).to match_array([
OpenStruct.new(id: "", name: "Select an option"),
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
OpenStruct.new(id: 99, name: "Other organisation"),
])
context "when no organisation is selected in the filters" do
it "returns an empty list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
context "when a specific child organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": child_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
])
end
end
context "when a specific parent organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": parent_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
])
end
end
context "when a specific absorbed organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": absorbed_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
])
end
end
context "when a specific non related organisation is selected in the filters" do
let(:unrelated_organisation) { create(:organisation, name: "Unrelated organisation") }
before do
session[:lettings_logs_filters] = { "owning_organisation": unrelated_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: unrelated_organisation.id, name: "Unrelated organisation"),
])
end
end
context "when a non existing organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": 143_542_542 }.to_json
end
it "returns an empty list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
end
context "with a data coordinator user" do
let(:user) { FactoryBot.create(:user, :data_coordinator, organisation: child_organisation) }
it "returns a list of parent orgs and your own organisation" do
expect(owning_organisation_filter_options(user.reload)).to eq([
OpenStruct.new(id: "", name: "Select an option"),
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
])
context "when no organisation is selected in the filters" do
it "returns an empty list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
context "when a specific child organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": child_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
])
end
end
context "when a specific parent organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": parent_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
])
end
end
context "when a specific absorbed organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": absorbed_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
])
end
end
context "when a specific non related organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": create(:organisation).id }.to_json
end
it "returns an empty list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
context "when a non existing organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "owning_organisation": 143_542_542 }.to_json
end
it "returns an empty list" do
expect(owning_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
end
end
@ -214,27 +333,146 @@ RSpec.describe FiltersHelper do
context "with a support user" do
let(:user) { FactoryBot.create(:user, :support, organisation: parent_organisation) }
it "returns a list of all organisations" do
expect(managing_organisation_filter_options(user)).to eq([
OpenStruct.new(id: "", name: "Select an option"),
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
OpenStruct.new(id: 99, name: "Other organisation"),
])
context "when no organisation is selected in the filters" do
it "returns an empty list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
context "when a specific child organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": child_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
])
end
end
context "when a specific parent organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": parent_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
])
end
end
context "when a specific absorbed organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": absorbed_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
])
end
end
context "when a specific non related organisation is selected in the filters" do
let(:unrelated_organisation) { create(:organisation, name: "Unrelated organisation") }
before do
session[:lettings_logs_filters] = { "managing_organisation": unrelated_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: unrelated_organisation.id, name: "Unrelated organisation"),
])
end
end
context "when a non existing organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": 143_542_542 }.to_json
end
it "returns an empty list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
end
context "with a data coordinator user" do
let(:user) { FactoryBot.create(:user, :data_coordinator, organisation: parent_organisation) }
it "returns a list of child orgs and your own organisation" do
expect(managing_organisation_filter_options(user.reload)).to eq([
OpenStruct.new(id: "", name: "Select an option"),
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
])
context "when no organisation is selected in the filters" do
it "returns an empty list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
context "when a specific child organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": child_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: child_organisation.id, name: "Child organisation"),
])
end
end
context "when a specific parent organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": parent_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: parent_organisation.id, name: "Parent organisation"),
])
end
end
context "when a specific absorbed organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": absorbed_organisation.id }.to_json
end
it "returns the selected organisation in the list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: absorbed_organisation.id, name: "Absorbed organisation"),
])
end
end
context "when a specific non related organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": create(:organisation).id }.to_json
end
it "returns an empty list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
context "when a non existing organisation is selected in the filters" do
before do
session[:lettings_logs_filters] = { "managing_organisation": 143_542_542 }.to_json
end
it "returns an empty list" do
expect(managing_organisation_filter_options(user.reload, "lettings_logs")).to eq([
OpenStruct.new(id: "", name: "Select an option"),
])
end
end
end
end

6
spec/models/user_spec.rb

@ -164,7 +164,7 @@ RSpec.describe User, type: :model do
end
it "can filter lettings logs by user, year and status" do
expect(user.logs_filters).to match_array(%w[years status needstypes assigned_to user bulk_upload_id])
expect(user.logs_filters).to match_array(%w[years status needstypes assigned_to user bulk_upload_id user_text_search])
end
end
@ -174,7 +174,7 @@ RSpec.describe User, type: :model do
end
it "can filter lettings logs by user, year, status, managing_organisation and owning_organisation" do
expect(user.logs_filters).to match_array(%w[years status needstypes assigned_to user managing_organisation owning_organisation bulk_upload_id])
expect(user.logs_filters).to match_array(%w[years status needstypes assigned_to user managing_organisation owning_organisation bulk_upload_id managing_organisation_text_search owning_organisation_text_search user_text_search])
end
end
end
@ -215,7 +215,7 @@ RSpec.describe User, type: :model do
end
it "can filter lettings logs by user, year, status, managing_organisation and owning_organisation" do
expect(user.logs_filters).to match_array(%w[years status needstypes assigned_to user owning_organisation managing_organisation bulk_upload_id])
expect(user.logs_filters).to match_array(%w[years status needstypes assigned_to user owning_organisation managing_organisation bulk_upload_id managing_organisation_text_search owning_organisation_text_search user_text_search])
end
end

45
spec/requests/organisations_controller_spec.rb

@ -75,6 +75,13 @@ RSpec.describe OrganisationsController, type: :request do
end
end
end
describe "#search" do
it "redirects to the sign in page" do
get "/organisations/search"
expect(response).to redirect_to("/account/sign-in")
end
end
end
context "when user is signed in" do
@ -807,6 +814,25 @@ RSpec.describe OrganisationsController, type: :request do
end
end
end
describe "#search" do
let(:parent_organisation) { create(:organisation, name: "parent test organisation") }
let(:child_organisation) { create(:organisation, name: "child test organisation") }
before do
user.organisation.update!(name: "test organisation")
create(:organisation_relationship, parent_organisation: user.organisation, child_organisation:)
create(:organisation_relationship, child_organisation: user.organisation, parent_organisation:)
create(:organisation, name: "other organisation test organisation")
end
it "only searches within the current user's organisation, managing agents and stock owners" do
get "/organisations/search", headers:, params: { query: "test organisation" }
result = JSON.parse(response.body)
expect(result.count).to eq(3)
expect(result.keys).to match_array([user.organisation.id.to_s, parent_organisation.id.to_s, child_organisation.id.to_s])
end
end
end
context "with a data provider user" do
@ -2077,6 +2103,25 @@ RSpec.describe OrganisationsController, type: :request do
end
end
end
describe "#search" do
let(:parent_organisation) { create(:organisation, name: "parent test organisation") }
let(:child_organisation) { create(:organisation, name: "child test organisation") }
let!(:other_organisation) { create(:organisation, name: "other organisation test organisation") }
before do
user.organisation.update!(name: "test organisation")
create(:organisation_relationship, parent_organisation: user.organisation, child_organisation:)
create(:organisation_relationship, child_organisation: user.organisation, parent_organisation:)
end
it "searches within all the organisations" do
get "/organisations/search", headers:, params: { query: "test organisation" }
result = JSON.parse(response.body)
expect(result.count).to eq(4)
expect(result.keys).to match_array([user.organisation.id.to_s, parent_organisation.id.to_s, child_organisation.id.to_s, other_organisation.id.to_s])
end
end
end
end

61
spec/requests/users_controller_spec.rb

@ -117,6 +117,13 @@ RSpec.describe UsersController, type: :request do
expect(response).to redirect_to("/account/sign-in")
end
end
describe "#search" do
it "redirects to the sign in page" do
get "/users/search"
expect(response).to redirect_to("/account/sign-in")
end
end
end
context "when user is signed in as a data provider" do
@ -404,6 +411,25 @@ RSpec.describe UsersController, type: :request do
expect(response).to have_http_status(:unauthorized)
end
end
describe "#search" do
let(:parent_relationship) { create(:organisation_relationship, parent_organisation: user.organisation) }
let(:child_relationship) { create(:organisation_relationship, child_organisation: user.organisation) }
let!(:org_user) { create(:user, organisation: user.organisation, name: "test_name") }
let!(:managing_user) { create(:user, organisation: parent_relationship.child_organisation, name: "managing_agent_test_name") }
before do
create(:user, organisation: child_relationship.parent_organisation, name: "stock_owner_test_name")
create(:user, name: "other_organisation_test_name")
end
it "only searches within the current user's organisation and managing agents" do
get "/users/search", headers:, params: { query: "test_name" }
result = JSON.parse(response.body)
expect(result.count).to eq(2)
expect(result.keys).to match_array([org_user.id.to_s, managing_user.id.to_s])
end
end
end
context "when user is signed in as a data coordinator" do
@ -1174,6 +1200,25 @@ RSpec.describe UsersController, type: :request do
expect(response).to have_http_status(:unauthorized)
end
end
describe "#search" do
let(:parent_relationship) { create(:organisation_relationship, parent_organisation: user.organisation) }
let(:child_relationship) { create(:organisation_relationship, child_organisation: user.organisation) }
let!(:org_user) { create(:user, organisation: user.organisation, email: "test_name@example.com") }
let!(:managing_user) { create(:user, organisation: parent_relationship.child_organisation, email: "managing_agent_test_name@example.com") }
before do
create(:user, email: "other_organisation_test_name@example.com")
create(:user, organisation: child_relationship.parent_organisation, email: "stock_owner_test_name@example.com")
end
it "only searches within the current user's organisation and managing agents" do
get "/users/search", headers:, params: { query: "test_name" }
result = JSON.parse(response.body)
expect(result.count).to eq(2)
expect(result.keys).to match_array([org_user.id.to_s, managing_user.id.to_s])
end
end
end
context "when user is signed in as a support user" do
@ -2111,6 +2156,22 @@ RSpec.describe UsersController, type: :request do
expect(page).not_to have_link("User to be deleted")
end
end
describe "#search" do
let(:parent_relationship) { create(:organisation_relationship, parent_organisation: user.organisation) }
let(:child_relationship) { create(:organisation_relationship, child_organisation: user.organisation) }
let!(:org_user) { create(:user, organisation: user.organisation, name: "test_name") }
let!(:managing_user) { create(:user, organisation: child_relationship.parent_organisation, name: "stock_owner_test_name") }
let!(:owner_user) { create(:user, organisation: parent_relationship.child_organisation, name: "managing_agent_test_name") }
let!(:other_user) { create(:user, name: "other_organisation_test_name") }
it "searches all users" do
get "/users/search", headers:, params: { query: "test_name" }
result = JSON.parse(response.body)
expect(result.count).to eq(4)
expect(result.keys).to match_array([org_user.id.to_s, managing_user.id.to_s, owner_user.id.to_s, other_user.id.to_s])
end
end
end
describe "title link" do

Loading…
Cancel
Save