Skip to content

Commit 2d97766

Browse files
authored
Merge pull request #2260 from jonodrew/member-search
Member search
2 parents 40dd75b + 923df32 commit 2d97766

File tree

11 files changed

+221
-0
lines changed

11 files changed

+221
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class Admin::MemberSearchController < Admin::ApplicationController
2+
def index
3+
member_params = params[:member_search] || {}
4+
name = member_params[:name]
5+
members = name.blank? ? Member.none : Member.find_members_by_name(name).select(:id, :name, :surname, :pronouns)
6+
callback_url = member_params[:callback_url] || params[:callback_url] || results_admin_member_search_index_path
7+
if members.size == 1
8+
query = { member_pick: { members: [members.first.id] } }
9+
query_string = query.to_query
10+
callback_url = "#{callback_url}?#{query_string}"
11+
redirect_to callback_url and return
12+
end
13+
14+
render 'index', locals: { members: members, callback_url: callback_url }
15+
end
16+
17+
def results
18+
members = Member.find(params[:member_pick][:members])
19+
render 'show', locals: { members: members }
20+
end
21+
end

app/models/member.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ def recent_notes
118118
member_notes.where('created_at > ?', notes_from_date)
119119
end
120120

121+
def self.find_members_by_name(name)
122+
name.strip!
123+
name.eql?('') ? self.none : where("CONCAT(name, ' ', surname) ILIKE ?", "%#{name}%")
124+
end
125+
121126
private
122127

123128
def invitations_on(date)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
= simple_form_for :member_search, url: admin_member_search_index_path, method: :get, html: {multipart: true, novalidate: true } do |f|
2+
.row
3+
.col-12.col-md-6
4+
= f.input :name, label: 'Member Name', input_html: { placeholder: 'Enter member name' }
5+
= f.button :submit, 'Search', class: 'btn btn-primary mt-3', name: nil
6+
= f.hidden_field 'callback_url', value: callback_url
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.container.py-4.py-lg-5
2+
.row.mb-4
3+
.col
4+
%h1 Member search
5+
6+
.row
7+
.col.col-md-10.col-lg-8
8+
= render partial: 'search_form', locals: { callback_url: callback_url }
9+
10+
.row
11+
.col.col-md-10.col-lg-8
12+
- if members.present?
13+
%h2 Select Member
14+
= simple_form_for :member_pick, url: callback_url, method: :get do |f|
15+
.row
16+
.col-12.col-md-6
17+
= f.label :members, 'Member Name'
18+
- members.each do |member|
19+
.form-check
20+
= f.check_box :members, { multiple: true, checked: member.id }, member.id, nil
21+
= f.label "members_#{member.id}", member.name_and_surname, class: 'form-check-label'
22+
= f.button :submit, 'Take me back', class: 'btn btn-primary mt-3', name: nil
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.container.py-4.py-lg-5
2+
.row.mb-4
3+
.col
4+
%h1 Results
5+
6+
.row
7+
.col.col-md-10.col-lg-8
8+
%h2 Search Results
9+
%ul.list-group
10+
- members.each do |member|
11+
%li.list-group-item
12+
= link_to member.name_and_surname, admin_member_path(member)

config/routes.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@
140140
end
141141

142142
resources :testimonials, only: %i[index]
143+
144+
resources :member_search, path: 'member-search', only: [:index, :results] do
145+
collection do
146+
get 'index'
147+
get 'results'
148+
end
149+
end
143150
end
144151

145152
get '/login', to: 'auth_services#new'

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
services:
22
db:
33
image: postgres:17
4+
ports:
5+
- 5433:5432
46
volumes:
57
- db_data:/var/lib/postgresql/data
68
environment:
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
RSpec.describe Admin::MemberSearchController, type: :controller do
2+
let(:member) {Fabricate.build(:member)}
3+
4+
describe 'GET #index' do
5+
context "when user is not logged in" do
6+
before do
7+
get :index
8+
end
9+
it "redirects to the home page" do
10+
expect(response).to redirect_to(root_path)
11+
end
12+
end
13+
14+
context "when user is an admin" do
15+
let(:fake_relation) { instance_double('ActiveRecord::Relation') }
16+
let(:fake_juliet) { instance_double('Member', id: 1, name: 'Juliet', surname: 'Montague') }
17+
18+
before do
19+
login_as_admin(member)
20+
get :index
21+
end
22+
it "shows user the search page" do
23+
expect(response).to have_http_status(:ok)
24+
end
25+
26+
context "and when admin user searches for a single existing user" do
27+
before do
28+
allow(Member).to receive(:find_members_by_name).with('Juliet').and_return(fake_relation)
29+
allow(fake_relation).to receive(:select).with(any_args).and_return([fake_juliet])
30+
get :index, params: {member_search: {name: "Juliet", callback_url: root_path}}
31+
end
32+
it "redirects to the calling service" do
33+
expect(response).to have_http_status(:found)
34+
35+
uri = URI.parse(response.location)
36+
redirect_params = Rack::Utils.parse_nested_query(uri.query)
37+
38+
expect(redirect_params["member_pick"]["members"]).to eq(["1"])
39+
end
40+
end
41+
42+
context "and when an admin user searches and there are multiple results" do
43+
let(:fake_romeo) { double('Member', id: 2, name: 'Romeo', surname: 'Capulet')}
44+
45+
before do
46+
allow(Member).to receive(:find_members_by_name).with('e').and_return(fake_relation)
47+
allow(fake_relation).to receive(:select).with(any_args).and_return([fake_juliet, fake_romeo])
48+
get :index, params: {member_search: {name: 'e', callback_url: root_path}}
49+
end
50+
it "presents the found members on the index page" do
51+
expect(response).to have_http_status(:ok)
52+
end
53+
end
54+
end
55+
end
56+
end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
RSpec.describe 'Add a user to an existing workshop', js: true, type: :feature do
2+
let(:member) {Fabricate(:member)}
3+
4+
let!(:juliet) {Fabricate(:member, name: 'Juliet', surname: 'Capulet')}
5+
let!(:romeo) { Fabricate(:member, name: 'Romeo', surname: 'Montague') }
6+
let(:workshop) {Fabricate(:workshop)}
7+
8+
before do
9+
login_as_admin(member)
10+
@start_page = "/admin/workshops/#{workshop.id}"
11+
end
12+
13+
scenario 'An admin searches and gets an exact match' do
14+
visit @start_page
15+
16+
params = {callback_url: @start_page.to_s}.to_query
17+
visit "/admin/member-search?#{params}"
18+
fill_in 'Member Name', with: juliet.name_and_surname
19+
click_on 'Search'
20+
expect(page).to have_current_path(@start_page, ignore_query: true)
21+
end
22+
23+
scenario 'An admin adds a member to a workshop' do
24+
visit @start_page
25+
26+
params = {callback_url: @start_page.to_s}.to_query
27+
visit "/admin/member-search?#{params}"
28+
fill_in 'Member Name', with: 'e'
29+
click_on 'Search'
30+
expect(page).to have_current_path('/admin/member-search/index', ignore_query: true)
31+
32+
expect(page).to have_content('Romeo Montague')
33+
expect(page).to have_unchecked_field('Romeo Montague')
34+
check('Romeo Montague')
35+
click_button'Take me back'
36+
37+
expect(page).to have_current_path(@start_page, ignore_query: true)
38+
uri = URI.parse(page.current_url)
39+
params = Rack::Utils.parse_nested_query(uri.query).with_indifferent_access
40+
41+
expect(params[:member_pick][:members]).to eq([romeo.id.to_s])
42+
end
43+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
RSpec.feature 'admin member search', type: :feature do
2+
scenario 'search returns single member to requesting service' do
3+
Fabricate(:member, :name => 'Romeo', :surname => 'Montague')
4+
Fabricate(:member, :name => 'Juliet', :surname => 'Capulet')
5+
member = Fabricate(:member)
6+
login_as_admin(member)
7+
visit admin_member_search_index_path(callback_url: results_admin_member_search_index_path)
8+
9+
fill_in 'member_search_name', with: 'Julie'
10+
click_button 'Search'
11+
12+
expect(page).to have_current_path(results_admin_member_search_index_path, ignore_query: true)
13+
14+
expect(page).to have_content('Juliet Capulet')
15+
end
16+
17+
18+
end

0 commit comments

Comments
 (0)