Skip to content
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

Add filters and paging to the workshop materials admin list #16309

Merged
merged 2 commits into from
Jul 13, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Pd::TeacherApplicationController < ApplicationController
include Pd::PageHelper

EMAIL_TEMPLATE_PREFIX = '2017_teacher_application_'.freeze
DEFAULT_MANAGE_PAGE_SIZE = 25

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
class Pd::WorkshopMaterialOrdersController < ApplicationController
include Pd::PageHelper

DEFAULT_PAGE_SIZE = 25

load_and_authorize_resource :enrollment, class: 'Pd::Enrollment', find_by: 'code',
id_param: :enrollment_code

Expand Down Expand Up @@ -36,9 +40,29 @@ def admin_index
order_errors: @workshop_material_orders.with_order_errors.count
}

# Search emails
@workshop_material_orders = @workshop_material_orders.search_emails(params[:email]) if params[:email]

# filter: [ordered | shipped | errors]
if params[:filter]
case params[:filter].try(:downcase)
when 'ordered'
@workshop_material_orders = @workshop_material_orders.successfully_ordered
when 'shipped'
@workshop_material_orders = @workshop_material_orders.shipped
when 'errors'
@workshop_material_orders = @workshop_material_orders.with_order_errors
else
params.delete :filter
end
end

@workshop_material_orders = @workshop_material_orders.page(page).per(page_size)
view_options(full_width: true)
end

private

def workshop_material_order_params
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if this is standard practice, but should we also have a param helper method here to filter out params other than email / filter and such?

Copy link
Contributor Author

@aoby aoby Jul 12, 2017

Choose a reason for hiding this comment

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

This method is for Strong Parameters since these are being passed into the model in Create. Without specifying them explicitly Rails won't allow them in. This method is called by CanCan's load_and_authorize_resource in the create action and then used to populate the model.

We don't need this for params we reference explicitly in the controller that aren't passed to a model, and I'm not sure what it would add.

params.require(:pd_workshop_material_order).permit(
:school_or_company,
Expand All @@ -50,4 +74,13 @@ def workshop_material_order_params
:phone_number
)
end

def page
params[:page] || 1
end

def page_size
return DEFAULT_PAGE_SIZE unless params.key? :page_size
params[:page_size] == 'All' ? @workshop_material_orders.count : params[:page_size]
end
end
48 changes: 48 additions & 0 deletions dashboard/app/helpers/pd/page_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Helper for simple paging controls
module Pd::PageHelper
# Renders a page header with paging buttons and a page size drop down.
# Each page button is a link to the same controller action with the appropriate page: param,
# and existing values of permitted_params.
# Each page_size button is similar, but with page_size: set.
# @param collection_name [String] Name to be displayed in the page header, e.g. "Applications"
# @param collection [ActiveRecord::Relation] collection of models with paging applied
# @param permitted_params [Array<String, Symbol>] params to be preserved in paging urls
def page_header(collection_name, collection, permitted_params: [])
current_page = collection.current_page

base_params = params.permit(permitted_params + [:page_size])
page_buttons = [
new_page_button('<<', base_params.merge(page: 1), disabled: collection.first_page?),
new_page_button('<', base_params.merge(page: current_page - 1), disabled: collection.first_page?),
new_page_button('>', base_params.merge(page: current_page + 1), disabled: collection.last_page?),
new_page_button('>>', base_params.merge(page: collection.total_pages), disabled: collection.last_page?)
]

page_size = params[:page_size] || collection.limit_value
page_size_buttons = %w(25 50 All).map do |page_size_option|
new_page_size_button page_size_option, base_params
end

locals = {
collection: collection,
collection_name: collection_name,
page_buttons: page_buttons,
page_size: page_size,
page_size_buttons: page_size_buttons
}

render partial: 'pd/page_header', locals: locals
end

def new_page_button(text, params, disabled: false)
link_to text, params, class: btn_class(disabled: disabled)
end

def new_page_size_button(page_size, base_params)
link_to page_size, base_params.merge(page_size: page_size)
end

def btn_class(disabled: false)
"btn btn-default#{(disabled && ' disabled').presence}"
end
end
3 changes: 3 additions & 0 deletions dashboard/app/models/pd/workshop_material_order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ def allowed_course?
scope :successfully_ordered, -> {where.not(order_id: nil)}
scope :shipped, -> {where(order_status: MimeoRestClient::STATUS_SHIPPED)}
scope :with_order_errors, -> {where.not(order_error: nil)}
scope :search_emails, ->(email_substring) do
joins(:enrollment).where('pd_enrollments.email LIKE ?', "%#{email_substring.strip.downcase}%")
end

def full_address
[street, apartment_or_suite, city, state, zip_code].compact.join(', ')
Expand Down
24 changes: 24 additions & 0 deletions dashboard/app/views/pd/_page_header.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
%h5.page-header
Page
= local_assigns[:collection].current_page
of
= local_assigns[:collection].total_pages
(
= local_assigns[:collection].current_page
of
= local_assigns[:collection].total_count
= local_assigns[:collection_name].pluralize
)

- local_assigns[:page_buttons].each do |page_button|
= page_button

%span.dropdown{style: 'position: absolute; right: 25px'}
Show
%button.btn.btn-default.dropdown-toggle{'data-toggle': 'dropdown'}
= local_assigns[:page_size]
%span.caret
%ul.dropdown-menu
- local_assigns[:page_size_buttons].each do |page_size_button|
%li
= page_size_button
21 changes: 1 addition & 20 deletions dashboard/app/views/pd/teacher_application/manage.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,7 @@
Filter:
= params[:q]

%h5
- base_params = params.permit(:q, :page_size)
- current_page = @teacher_applications.current_page
- total_pages = @teacher_applications.total_pages
- total_count = @teacher_applications.total_count
= "Page #{current_page} of #{total_pages}"
= "(#{@teacher_applications.count} of #{total_count} applications)"
= link_to '<<', base_params.merge(page: 1), class: 'btn btn-default', disabled: @teacher_applications.first_page?
= link_to '<', base_params.merge(page: current_page - 1), class: 'btn btn-default', disabled: @teacher_applications.first_page?
= link_to '>', base_params.merge(page: current_page + 1), class: 'btn btn-default', disabled: @teacher_applications.last_page?
= link_to '>>', base_params.merge(page: total_pages), class: 'btn btn-default', disabled: @teacher_applications.last_page?
%span.dropdown{style: 'position: absolute; right: 25px'}
Show
%button.btn.btn-default.dropdown-toggle{'data-toggle': 'dropdown'}
= params[:page_size] || @teacher_applications.limit_value
%span.caret
%ul.dropdown-menu
- %w(25 50 All).each do |page_size|
%li
= link_to page_size, base_params.merge(page_size: page_size)
= page_header 'applications', @teacher_applications, permitted_params: [:q]

%table.table.table-hover.table-condensed.table-auto-width
%thead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,37 @@
%td= value

%h3 Orders
%label Filter
%span.dropdown
%button.btn.btn-default.dropdown-toggle{'data-toggle': 'dropdown'}
= params[:filter] || 'None'
%span.caret
%ul.dropdown-menu
- %w(None Ordered Shipped Errors).each do |filter_option|
%li
= link_to filter_option, params: {filter: filter_option}

%label Search Email
= form_tag url_for(action: 'admin_index'), method: 'get', class: 'form-inline', enforce_utf8: false do
= text_field_tag :email, params[:email], class: 'form-control'
%button.btn{type: 'submit'}
%i.fa.fa-search
- if params[:email]
= link_to 'Remove Filter', nil, params: {}, class: 'btn btn-default'

%h4
- if params[:email]
Email:
= params[:email]
- if params[:filter]
Filter:
= params[:filter]

= page_header 'orders', @workshop_material_orders, permitted_params: [:email, :filter]

%table.table.table-hover.table-condensed.table-auto-width
%thead
%th Id
%th Created
%th Email
%th Name
Expand All @@ -29,6 +58,7 @@
%tbody
- @workshop_material_orders.each do |order|
%tr
%td= order.id
%td= order.created_at.to_date
%td= order.enrollment.email
%td= order.enrollment.full_name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,23 @@ class Pd::WorkshopMaterialOrdersControllerTest < ::ActionController::TestCase
test_user_gets_response_for :admin_index, user: :admin, response: :success
test_user_gets_response_for :admin_index, user: :teacher, response: :forbidden

test 'admin index has page header' do
sign_in create(:admin)
get :admin_index
assert_select 'h5.page-header' do
assert_select 'a.btn', '<<'
assert_select 'a.btn', '<'
assert_select 'a.btn', '>'
assert_select 'a.btn', '>>'

assert_select 'span.dropdown', /Show/ do
assert_select 'a', '25'
assert_select 'a', '50'
assert_select 'a', 'All'
end
end
end

private

def assert_thanks
Expand Down
129 changes: 129 additions & 0 deletions dashboard/test/helpers/pd/page_helper_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
require 'test_helper'

class Pd::PageHelperTest < ActionView::TestCase
include Pd::PageHelper

test 'btn_class default' do
assert_equal 'btn btn-default', btn_class
end

test 'btn_class disabled' do
assert_equal 'btn btn-default disabled', btn_class(disabled: true)
end

test 'new_page_button' do
expects(:link_to).with('<', {page: 1}, class: 'btn btn-default')
new_page_button '<', page: 1
end

test 'new_page_size_button' do
expects(:link_to).with(50, {key: 'value', page_size: 50})
new_page_size_button 50, {key: 'value'}
end

test 'page_header first page' do
assert_page_header(
{
current_page: 1,
first_page?: true,
last_page?: false,
total_pages: 10,
limit_value: 25
},
[
{text: '<<', page: 1, disabled: true},
{text: '<', page: 0, disabled: true},
{text: '>', page: 2, disabled: false},
{text: '>>', page: 10, disabled: false}
]
)
end

test 'page_header middle page' do
assert_page_header(
{
current_page: 5,
first_page?: false,
last_page?: false,
total_pages: 10,
limit_value: 25
},
[
{text: '<<', page: 1, disabled: false},
{text: '<', page: 4, disabled: false},
{text: '>', page: 6, disabled: false},
{text: '>>', page: 10, disabled: false}
]
)
end

test 'page_header last page' do
assert_page_header(
{
current_page: 10,
first_page?: false,
last_page?: true,
total_pages: 10,
limit_value: 25
},
[
{text: '<<', page: 1, disabled: false},
{text: '<', page: 9, disabled: false},
{text: '>', page: 11, disabled: true},
{text: '>>', page: 10, disabled: true}
]
)
end

private

# Sets up expectations for, and then calls, page_header
# @param collection_params [Hash] param hash for mock collection class
# Expected keys are [:current_page, :first_page?, :last_page?, :total_pages, :limit_value]
# @param page_button_params [Array<Hash>] An array of hashes, each with keys
# [:text, :page, :disabled] representing expected params to the new_page_button method
def assert_page_header(collection_params, page_button_params)
collection = OpenStruct.new(collection_params)
page_buttons = set_page_button_expectations page_button_params

mock_params = mock
stubs(:params).returns(mock_params)
mock_params.expects(:permit).with([:allow, :page_size]).returns({})
mock_params.expects(:[], :page_size).returns(nil)

page_size_buttons = 3.times.map {mock}
expects(:new_page_size_button).with('25', {}).returns(page_size_buttons[0])
expects(:new_page_size_button).with('50', {}).returns(page_size_buttons[1])
expects(:new_page_size_button).with('All', {}).returns(page_size_buttons[2])

expects(:render).with(partial: 'pd/page_header', locals:
{
collection: collection,
collection_name: 'collection name',
page_buttons: page_buttons,
page_size: 25,
page_size_buttons: page_size_buttons
}
)

# Call page_header helper method which should meet the above expectations
page_header 'collection name', collection, permitted_params: [:allow]
end

# Creates expectations for new_page_button method calls with params based on the supplied param set,
# and returns the generated mock buttons
# @param buttons_param_set [Array<Hash>] An array of hashes, each with keys
# [:text, :page, :disabled] representing expected params to the new_page_button method
# @return array of mock buttons that will be returned from the expectations
def set_page_button_expectations(buttons_param_set)
buttons_param_set.map do |button_params|
text = button_params[:text]
page = button_params[:page]
disabled = button_params[:disabled]

mock.tap do |mock_button|
expects(:new_page_button).with(text, {page: page}, disabled: disabled).returns(mock_button)
end
end
end
end
14 changes: 14 additions & 0 deletions dashboard/test/models/pd/workshop_material_order_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -311,4 +311,18 @@ class Pd::WorkshopMaterialOrderTest < ActiveSupport::TestCase
order.user = nil
refute order.valid?
end

test 'search_emails' do
included = [
create(:pd_workshop_material_order, enrollment: create(:pd_enrollment, email: 'included1@example.net')),
create(:pd_workshop_material_order, enrollment: create(:pd_enrollment, email: 'included2@example.net')),
create(:pd_workshop_material_order, enrollment: create(:pd_enrollment, email: 'IncludedWithMismatchedCase@example.net'))
]

# excluded
create(:pd_workshop_material_order, enrollment: create(:pd_enrollment, email: 'excluded@example.net'))

found = Pd::WorkshopMaterialOrder.search_emails('include')
assert_equal included.map(&:id).sort, found.pluck(:id).sort
end
end