diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3e7257e2..8154a821 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -8,7 +8,14 @@ class UsersController < ApplicationController # GET /users # GET /users.json def index - @users = User.all + (@filterrific = initialize_filterrific( + User.all, + params[:filterrific], + select_options: { + sorted_by: User.options_for_sorted_by + } + )) || return + @users = @filterrific.find.page(params[:page]) end # GET /users/new diff --git a/app/models/user.rb b/app/models/user.rb index 0db95239..0482c600 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,10 +10,58 @@ class User < ApplicationRecord validates :email, presence: true, uniqueness: { case_sensitive: false } + filterrific( + default_filter_params: { sorted_by: 'email_desc' }, + available_filters: %i[ + sorted_by + search_query + ] + ) + scope :admins, -> { where(is_admin: true) } + scope :search_query, ->(query) { + return nil if query.blank? + + # condition query, parse into individual keywords + terms = query.downcase.split(/\s+/) + # replace "*" with "%" for wildcard searches, + # append '%', remove duplicate '%'s + terms = terms.map do |e| + ('%' + e.gsub('*', '%') + '%').gsub(/%+/, '%') + end + # configure number of OR conditions for provision + # of interpolation arguments. Adjust this if you + # change the number of OR conditions. + num_or_conditions = 1 + where( + terms.map do + or_clauses = [ + 'LOWER(users.email) LIKE ?' + ].join(' OR ') + "(#{or_clauses})" + end.join(' AND '), + *terms.map { |e| [e] * num_or_conditions }.flatten + ) + } + + scope :sorted_by, ->(sort_option) { + # extract the sort direction from the param value. + direction = /desc$/.match?(sort_option) ? 'desc' : 'asc' + case sort_option.to_s + when /^email_/ + order(Arel.sql("LOWER(users.email) #{direction}")) + when /^years_experience_/ + order(Arel.sql("users.years_experience #{direction}")) + when /^admin_/ + order(Arel.sql("users.is_admin #{direction}")) + else + raise(ArgumentError, "Invalid sort option: #{sort_option.inspect}") + end + } + def admin? is_admin end @@ -22,6 +70,17 @@ def sha_email sha = Digest::SHA1.new sha.hexdigest email end + + def self.options_for_sorted_by + [ + ['Email (a-z)', 'email_asc'], + ['Email (z-a)', 'email_desc'], + ['Years Experience (lowest first)', 'years_experience_asc'], + ['Years Experience (highest first)', 'years_experience_desc'], + ['Admin? (false first)', 'admin_asc'], + ['Admin? (true first)', 'admin_desc'] + ] + end end # == Schema Information diff --git a/app/views/users/_list.html.erb b/app/views/users/_list.html.erb new file mode 100644 index 00000000..2cfeb071 --- /dev/null +++ b/app/views/users/_list.html.erb @@ -0,0 +1,45 @@ +<% if users.load.empty? %> + No users at this time! +<% else %> +
+
+
+
+ <%= page_entries_info users, model: 'user', class: 'align-middle' %> +
+
+ <%= link_to "Reset filters", reset_filterrific_url, class: 'btn btn-secondary align-middle' %> +
+
+ <%= render_filterrific_spinner %> +
+ + + + + + + + + + + + <% @users.each do |user| %> + + + + + <% if user.admin? %> + + <% else %> + + <% end %> + + + + <% end %> + +
<%= filterrific_sorting_link(@filterrific, :email) %><%= filterrific_sorting_link(@filterrific, :years_experience) %><%= filterrific_sorting_link(@filterrific, :admin) %>
<%= user.email %><%= user.years_experience %><%= user.is_admin %><%= link_to 'Demote', user_demote_path(user), method: :put %><%= link_to 'Promote', user_promote_path(user), method: :put %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %>
+
+ <%= will_paginate users, renderer: WillPaginate::ActionView::BootstrapLinkRenderer %> +<% end %> \ No newline at end of file diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 1178ae26..03647ff0 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -1,31 +1,31 @@ <% if can? :manage, User %> -

Users

- - - - - - - - - - - - <% @users.each do |user| %> - - - - - <% if user.admin? %> - - <% else %> - - <% end %> - - - +

Users

+ <%= link_to 'New User', new_user_path %> +
+
+ <%= form_for_filterrific @filterrific do |f| %> +
+
+
+ + <%= f.text_field( + :search_query, + class: 'filterrific-periodically-observed' + ) %> +
+
+
+
+ + <%= f.select(:sorted_by, @filterrific.select_options[:sorted_by], + {}, + { class: 'form-control' } + ) %> +
+
+
<% end %> -
-
EmailYears experienceIs admin
<%= user.email %><%= user.years_experience %><%= user.is_admin %><%= link_to 'Demote', user_demote_path(user), method: :put %><%= link_to 'Promote', user_promote_path(user), method: :put %><%= link_to 'Edit', edit_user_path(user) %><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %>
-<% end %> \ No newline at end of file + +<% end %> + +<%= render 'list', users: @users %> \ No newline at end of file diff --git a/app/views/users/index.js.erb b/app/views/users/index.js.erb new file mode 100644 index 00000000..2c85890e --- /dev/null +++ b/app/views/users/index.js.erb @@ -0,0 +1,7 @@ +<% js = escape_javascript( + render( + partial: 'users/list', + locals: { users: @users } + ) +) %> +$("#filterrific_results").html("<%= js %>"); \ No newline at end of file diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 0ec8d600..c56185f5 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -4,7 +4,7 @@ RSpec.describe UsersController, type: :controller do context 'admin' do - let(:current_user) { build_stubbed(:admin) } + let(:current_user) { create(:admin) } before do sign_in current_user allow(request.env['warden']).to receive(:authenticate!).and_return(current_user) @@ -13,15 +13,7 @@ describe 'GET #index' do before do - @users = [build_stubbed(:user), build_stubbed(:user)] - allow_any_instance_of(User).to receive(:admin?).and_return(true) - allow(User).to receive(:all).and_return(@users) - allow(User).to receive(:where).and_return(@users) - end - - it 'calls all' do - expect(User).to receive(:all) - get :index + @users = [create(:user), create(:user), current_user] end it 'returns a success response' do @@ -31,7 +23,7 @@ it 'assigns users to @users' do get :index - expect(assigns(:users)).to eq(@users) + expect(assigns(:users).size).to eq(3) end it 'renders the index template' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6b1c7046..e6cde374 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -27,6 +27,36 @@ end end end + + describe 'search_query' do + it 'gets all users with query' do + user1 = create(:user, email: 'unicorns@gmail.com') + user2 = create(:user, email: 'oh_no@test.com') + user3 = create(:user, email: 'bye@test.com') + aggregate_failures do + expect(User.search_query('unicorns').length).to eq 1 + expect(User.search_query('unicorns')).to include user1 + expect(User.search_query('unicorns')).not_to include user2 + expect(User.search_query('unicorns')).not_to include user3 + end + end + end + + describe 'sorted_by' do + it 'gets all users sorted' do + user1 = create(:user, email: 'Alphabets@test.com', is_admin: true, years_experience: 3) + user2 = create(:user, email: 'Zebra@test.com', is_admin: false, years_experience: 4) + user3 = create(:user, email: 'Cows@test.com', is_admin: false, years_experience: 5) + aggregate_failures do + expect(User.sorted_by('email_asc').first).to eq user1 + expect(User.sorted_by('email_desc').first).to eq user2 + expect(User.sorted_by('years_experience_asc').first).to eq user1 + expect(User.sorted_by('years_experience_desc').first).to eq user3 + expect(User.sorted_by('admin_desc').first).to eq user1 + expect { User.sorted_by('oh_no') }.to raise_error(ArgumentError, 'Invalid sort option: "oh_no"') + end + end + end end context 'methods' do