Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_by_name(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_by_name(name)
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_by_name).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_by_name).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 'finds the member' do
expect(Member.find_members_by_name(member.name).first).to eq(member)
end
end

describe 'search by last name' do
it 'finds the member' do
expect(Member.find_members_by_name(member.surname).first).to eq(member)
end
end

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

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

end
end