Skip to content

Member search #2260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
21 changes: 21 additions & 0 deletions app/controllers/admin/member_search_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Admin::MemberSearchController < Admin::ApplicationController
def index
member_params = params[:member_search] || {}
name = member_params[:name]
members = name.blank? ? Member.none : Member.find_members(name).select(:id, :name, :surname, :pronouns)
callback_url = member_params[:callback_url] || params[:callback_url] || results_admin_member_search_index_path
if members.size == 1
query = { member_pick: { members: [members.first.id] } }
query_string = query.to_query
callback_url = "#{callback_url}?#{query_string}"
redirect_to callback_url and return
end

render 'index', locals: { members: members, callback_url: callback_url }
end

def results
members = Member.find(params[:member_pick][:members])
render 'show',locals: { members: members }
end
end
5 changes: 5 additions & 0 deletions app/models/member.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ def recent_notes
member_notes.where('created_at > ?', notes_from_date)
end

def self.find_members(name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonodrew can we rename this method to find_members_by_name? Just to be explicit.

name.strip!
name.eql?('') ? self.none : where("CONCAT(name, ' ', surname) ILIKE ?", "%#{name}%")
end

private

def invitations_on(date)
Expand Down
6 changes: 6 additions & 0 deletions app/views/admin/member_search/_search_form.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
= simple_form_for :member_search, url: admin_member_search_index_path, method: :get, html: {multipart: true, novalidate: true } do |f|
.row
.col-12.col-md-6
= f.input :name, label: 'Member Name', input_html: { placeholder: 'Enter member name' }
= f.button :submit, 'Search', class: 'btn btn-primary mt-3', name: nil
= f.hidden_field 'callback_url', value: callback_url
22 changes: 22 additions & 0 deletions app/views/admin/member_search/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.container.py-4.py-lg-5
.row.mb-4
.col
%h1 Member search

.row
.col.col-md-10.col-lg-8
= render partial: 'search_form', locals: { callback_url: callback_url }

.row
.col.col-md-10.col-lg-8
- if members.present?
%h2 Select Member
= simple_form_for :member_pick, url: callback_url, method: :get do |f|
.row
.col-12.col-md-6
= f.label :members, 'Member Name'
- members.each do |member|
.form-check
= f.check_box :members, { multiple: true, checked: member.id }, member.id, nil
= f.label "members_#{member.id}", member.name_and_surname, class: 'form-check-label'
= f.button :submit, 'Take me back', class: 'btn btn-primary mt-3', name: nil
12 changes: 12 additions & 0 deletions app/views/admin/member_search/show.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.container.py-4.py-lg-5
.row.mb-4
.col
%h1 Results

.row
.col.col-md-10.col-lg-8
%h2 Search Results
%ul.list-group
- members.each do |member|
%li.list-group-item
= link_to member.name_and_surname, admin_member_path(member)
7 changes: 7 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@
end

resources :testimonials, only: %i[index]

resources :member_search, path: 'member-search', only: [:index, :results] do
collection do
get 'index'
get 'results'
end
end
end

get '/login', to: 'auth_services#new'
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
services:
db:
image: postgres:17
ports:
- 5433:5432
volumes:
- db_data:/var/lib/postgresql/data
environment:
Expand Down
56 changes: 56 additions & 0 deletions spec/controllers/admin/member_search_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
RSpec.describe Admin::MemberSearchController, type: :controller do
let(:member) {Fabricate.build(:member)}

describe 'GET #index' do
context "when user is not logged in" do
before do
get :index
end
it "redirects to the home page" do
expect(response).to redirect_to(root_path)
end
end

context "when user is an admin" do
let(:fake_relation) { instance_double('ActiveRecord::Relation') }
let(:fake_juliet) { instance_double('Member', id: 1, name: 'Juliet', surname: 'Montague') }

before do
login_as_admin(member)
get :index
end
it "shows user the search page" do
expect(response).to have_http_status(:ok)
end

context "and when admin user searches for a single existing user" do
before do
allow(Member).to receive(:find_members).with('Juliet').and_return(fake_relation)
allow(fake_relation).to receive(:select).with(any_args).and_return([fake_juliet])
get :index, params: {member_search: {name: "Juliet", callback_url: root_path}}
end
it "redirects to the calling service" do
expect(response).to have_http_status(:found)

uri = URI.parse(response.location)
redirect_params = Rack::Utils.parse_nested_query(uri.query)

expect(redirect_params["member_pick"]["members"]).to eq(["1"])
end
end

context "and when an admin user searches and there are multiple results" do
let(:fake_romeo) { double('Member', id: 2, name: 'Romeo', surname: 'Capulet')}

before do
allow(Member).to receive(:find_members).with('e').and_return(fake_relation)
allow(fake_relation).to receive(:select).with(any_args).and_return([fake_juliet, fake_romeo])
get :index, params: {member_search: {name: 'e', callback_url: root_path}}
end
it "presents the found members on the index page" do
expect(response).to have_http_status(:ok)
end
end
end
end
end
43 changes: 43 additions & 0 deletions spec/features/admin/add_user_to_workshop_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
RSpec.describe 'Add a user to an existing workshop', js: true, type: :feature do
let(:member) {Fabricate(:member)}

let!(:juliet) {Fabricate(:member, name: 'Juliet', surname: 'Capulet')}
let!(:romeo) { Fabricate(:member, name: 'Romeo', surname: 'Montague') }
let(:workshop) {Fabricate(:workshop)}

before do
login_as_admin(member)
@start_page = "/admin/workshops/#{workshop.id}"
end

scenario 'An admin searches and gets an exact match' do
visit @start_page

params = {callback_url: @start_page.to_s}.to_query
visit "/admin/member-search?#{params}"
fill_in 'Member Name', with: juliet.name_and_surname
click_on 'Search'
expect(page).to have_current_path(@start_page, ignore_query: true)
end

scenario 'An admin adds a member to a workshop' do
visit @start_page

params = {callback_url: @start_page.to_s}.to_query
visit "/admin/member-search?#{params}"
fill_in 'Member Name', with: 'e'
click_on 'Search'
expect(page).to have_current_path('/admin/member-search/index', ignore_query: true)

expect(page).to have_content('Romeo Montague')
expect(page).to have_unchecked_field('Romeo Montague')
check('Romeo Montague')
click_button'Take me back'

expect(page).to have_current_path(@start_page, ignore_query: true)
uri = URI.parse(page.current_url)
params = Rack::Utils.parse_nested_query(uri.query).with_indifferent_access

expect(params[:member_pick][:members]).to eq([romeo.id.to_s])
end
end
18 changes: 18 additions & 0 deletions spec/features/admin/member_search_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
RSpec.feature 'admin member search', type: :feature do
scenario 'search returns single member to requesting service' do
Fabricate(:member, :name => 'Romeo', :surname => 'Montague')
Fabricate(:member, :name => 'Juliet', :surname => 'Capulet')
member = Fabricate(:member)
login_as_admin(member)
visit admin_member_search_index_path(callback_url: results_admin_member_search_index_path)

fill_in 'member_search_name', with: 'Julie'
click_button 'Search'

expect(page).to have_current_path(results_admin_member_search_index_path, ignore_query: true)

expect(page).to have_content('Juliet Capulet')
end


end
29 changes: 29 additions & 0 deletions spec/models/member_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,33 @@
expect(member.flag_to_organisers?).to be true
end
end

describe '#find_members' do
describe 'search by first name' do
it "should find the member" do
expect(Member.find_members(member.name).first).to eq(member)
end
end

describe 'search by last name' do
it "should find the member" do
expect(Member.find_members(member.surname).first).to eq(member)
end
end

describe 'search by full name' do
it "should find the member" do
expect(Member.find_members("#{member.name} #{member.surname}").first).to eq(member)
end
end

describe 'search bar is empty' do
it 'should return no members' do
Fabricate(:member)
expect(Member.all.size).to be > 0
expect(Member.find_members('').size).to eq(0)
end
end

end
end
Loading