Skip to content

Commit

Permalink
Merge pull request #16309 from code-dot-org/pd-order-list-paging
Browse files Browse the repository at this point in the history
Add filters and paging to the workshop materials admin list
  • Loading branch information
aoby committed Jul 13, 2017
2 parents d71f9fd + 4316943 commit d0fc7ec
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 20 deletions.
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
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

0 comments on commit d0fc7ec

Please sign in to comment.