Update Admin user datatables to use AJAX
This commit is contained in:
parent
600e394cf8
commit
64ceaf50f4
6 changed files with 290 additions and 53 deletions
|
|
@ -1,9 +1,11 @@
|
||||||
$(function () {
|
$(function () {
|
||||||
$('.datatable').DataTable({
|
$.extend(true, $.fn.dataTable.defaults, {
|
||||||
// ajax: ...,
|
"stateSave": true,
|
||||||
stateSave: true,
|
"autoWidth": false,
|
||||||
autoWidth: false,
|
"pagingType": "full_numbers",
|
||||||
pagingType: 'full_numbers',
|
|
||||||
"lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
|
"lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.datatable:not([data-source])').DataTable();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,12 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@users = User.all
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json do
|
||||||
|
render json: UserDatatable.new(view_context)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This action allow admins to manually toggle confirmation state of another user
|
# This action allow admins to manually toggle confirmation state of another user
|
||||||
|
|
|
||||||
69
app/datatables/user_datatable.rb
Normal file
69
app/datatables/user_datatable.rb
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserDatatable < AjaxDatatablesRails::Base
|
||||||
|
def_delegator :@view, :show_roles
|
||||||
|
def_delegator :@view, :admin_user_path
|
||||||
|
def_delegator :@view, :edit_admin_user_path
|
||||||
|
|
||||||
|
def view_columns
|
||||||
|
# Declare strings in this format: ModelName.column_name
|
||||||
|
# or in aliased_join_table.column_name format
|
||||||
|
@view_columns ||= {
|
||||||
|
id: { source: 'User.id', cond: :eq },
|
||||||
|
confirmed_at: { source: 'User.confirmed_at', searchable: false },
|
||||||
|
email: { source: 'User.email' },
|
||||||
|
name: { source: 'User.name' },
|
||||||
|
attended: { source: 'attended_count', searchable: false },
|
||||||
|
roles: { source: 'Role.name' },
|
||||||
|
view_url: { source: 'User.id', searchable: false, orderable: false },
|
||||||
|
edit_url: { source: 'User.id', searchable: false, orderable: false }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def data
|
||||||
|
records.map do |record|
|
||||||
|
{
|
||||||
|
id: record.id,
|
||||||
|
confirmed_at: record.confirmed_at,
|
||||||
|
email: record.email,
|
||||||
|
name: record.name,
|
||||||
|
attended: record.attended_count,
|
||||||
|
roles: record.roles.any? ? show_roles(record.get_roles) : 'None',
|
||||||
|
view_url: admin_user_path(record),
|
||||||
|
edit_url: edit_admin_user_path(record),
|
||||||
|
DT_RowId: record.id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# rubocop:disable Naming/AccessorMethodName
|
||||||
|
def get_raw_records
|
||||||
|
User.left_outer_joins(:registrations, :roles).distinct
|
||||||
|
.select('users.*, COUNT(CASE WHEN registrations.attended = "t" THEN 1 END) AS attended_count').group('users.id')
|
||||||
|
end
|
||||||
|
# rubocop:enable Naming/AccessorMethodName
|
||||||
|
|
||||||
|
def records_total_count
|
||||||
|
fetch_records.unscope(:group).count(:all)
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_filtered_count
|
||||||
|
filter_records(fetch_records).unscope(:group).count(:all)
|
||||||
|
end
|
||||||
|
|
||||||
|
# ==== These methods represent the basic operations to perform on records
|
||||||
|
# and feel free to override them
|
||||||
|
|
||||||
|
# def filter_records(records)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def sort_records(records)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def paginate_records(records)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# ==== Insert 'presenter'-like methods below if necessary
|
||||||
|
end
|
||||||
|
|
@ -220,6 +220,10 @@ class User < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def attended_count
|
||||||
|
attributes['attended_count'] || registrations.where(attended: true).count
|
||||||
|
end
|
||||||
|
|
||||||
def confirmed?
|
def confirmed?
|
||||||
!confirmed_at.nil?
|
!confirmed_at.nil?
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -10,52 +10,67 @@
|
||||||
= link_to 'Add User', new_admin_user_path, :class => 'button btn btn-default btn-info'
|
= link_to 'Add User', new_admin_user_path, :class => 'button btn btn-default btn-info'
|
||||||
.row
|
.row
|
||||||
.col-md-12.table-responsive
|
.col-md-12.table-responsive
|
||||||
%table.datatable
|
%table.datatable#users{ data: { source: admin_users_path(format: :json) } }
|
||||||
%thead
|
%thead
|
||||||
%th ID
|
%th{ width: '0' } ID
|
||||||
%th Confirmed?
|
%th{ width: '0' } Confirmed?
|
||||||
%th Email
|
%th{ width: '0' } Email
|
||||||
%th Name
|
%th{ width: '50%' } Name
|
||||||
%th Attended Conferences
|
%th{ width: '0' } Conferences Attended
|
||||||
%th Roles
|
%th{ width: '50%' } Roles
|
||||||
%th View
|
%th{ width: '0' } Actions
|
||||||
%th Edit
|
|
||||||
%tbody
|
%tbody
|
||||||
- @users.each do |user|
|
|
||||||
%tr
|
:javascript
|
||||||
%td
|
$(function () {
|
||||||
= user.id
|
$('#users.datatable').DataTable({
|
||||||
%td{ 'data-order' => "#{user.confirmed?}" }
|
"processing": true,
|
||||||
- if can? :toggle_confirmation, user
|
"serverSide": true,
|
||||||
= check_box_tag user.id, user.id, user.confirmed?,
|
"ajax": $('#users.datatable').data('source'),
|
||||||
method: :patch,
|
"drawCallback": function(settings) {
|
||||||
url: "/admin/users/#{user.id}/toggle_confirmation?user[to_confirm]=",
|
checkboxSwitch("[class='switch-checkbox']");
|
||||||
class: 'switch-checkbox',
|
},
|
||||||
readonly: false,
|
"columns": [
|
||||||
data: { size: 'small', on_color: 'success', off_color: 'warning', on_text: 'Yes', off_text: 'No' }
|
{ "data": "id" },
|
||||||
- else
|
{
|
||||||
= check_box_tag user.id, user.id, user.confirmed?,
|
"data": "confirmed_at",
|
||||||
method: :patch,
|
"render": function (data, type, row, meta) {
|
||||||
url: "/admin/users/#{user.id}/toggle_confirmation?user[to_confirm]=",
|
return '<input type="checkbox" class="switch-checkbox" '+
|
||||||
class: 'switch-checkbox',
|
'id="user_'+row.id+'_confirmed" '+
|
||||||
readonly: true,
|
'name="user_'+row.id+'_confirmed" '+
|
||||||
data: { size: 'small', on_color: 'success', off_color: 'warning', on_text: 'Yes', off_text: 'No' }
|
(row.confirmed_at === '' ? '' : 'checked="checked" ')+
|
||||||
%td
|
'url="/admin/users/'+row.id+'/toggle_confirmation?user[to_confirm]=" '+
|
||||||
= user.email
|
'/>'
|
||||||
%td
|
},
|
||||||
= user.name
|
"searchable": false
|
||||||
%td
|
},
|
||||||
= user.registrations.where(attended: true).count
|
{ "data": "email" },
|
||||||
%td
|
{
|
||||||
- unless user.get_roles.blank?
|
"data": "name",
|
||||||
= show_roles(user.get_roles.first(2))
|
"className": "truncate",
|
||||||
- if user.get_roles.count > 2
|
"render": function (data, type, row, meta) {
|
||||||
= '...'
|
return '<span data-toggle="tooltip" title="'+data+'">'+data+'</span>'
|
||||||
- else
|
}
|
||||||
None
|
},
|
||||||
%td
|
{ "data": "attended" },
|
||||||
- if can? :show, user
|
{
|
||||||
= link_to 'View', admin_user_path(user), class: 'btn btn-success'
|
"data": "roles",
|
||||||
%td
|
"className": "truncate" ,
|
||||||
- if can? :update, user
|
"render": function (data, type, row, meta) {
|
||||||
= link_to 'Edit', edit_admin_user_path(user), class: 'btn btn-primary'
|
return '<span data-toggle="tooltip" title="'+data+'">'+data+'</span>'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": null,
|
||||||
|
"className": "actions",
|
||||||
|
"sortable": false,
|
||||||
|
"render": function (data, type, row, meta) {
|
||||||
|
return '<div class="btn-group">'+
|
||||||
|
'<a class="btn-info" href="'+data.view_url+'">View</a>'+
|
||||||
|
'<a class="btn-primary" href="'+data.edit_url+'">Edit</a>'+
|
||||||
|
'</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
142
spec/datatables/user_datatable_spec.rb
Normal file
142
spec/datatables/user_datatable_spec.rb
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe UserDatatable do
|
||||||
|
subject! do
|
||||||
|
described_class.new(view)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:data_cols) do
|
||||||
|
[:id, :confirmed_at, :email, :name, :attended, :roles, :view_url, :edit_url, :DT_RowId]
|
||||||
|
end
|
||||||
|
let(:view) do
|
||||||
|
view = double(
|
||||||
|
'view',
|
||||||
|
params: {
|
||||||
|
'draw' => '1',
|
||||||
|
'columns' => {
|
||||||
|
'0' => {
|
||||||
|
'data' => 'id',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'true',
|
||||||
|
'orderable' => 'true',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
},
|
||||||
|
'1' => {
|
||||||
|
'data' => 'confirmed_at',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'false',
|
||||||
|
'orderable' => 'true',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
},
|
||||||
|
'2' => {
|
||||||
|
'data' => 'email',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'true',
|
||||||
|
'orderable' => 'true',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
},
|
||||||
|
'3' => {
|
||||||
|
'data' => 'name',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'true',
|
||||||
|
'orderable' => 'true',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
},
|
||||||
|
'4' => {
|
||||||
|
'data' => 'attended',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'true',
|
||||||
|
'orderable' => 'true',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
},
|
||||||
|
'5' => {
|
||||||
|
'data' => 'roles',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'true',
|
||||||
|
'orderable' => 'true',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
},
|
||||||
|
'6' => {
|
||||||
|
'data' => '',
|
||||||
|
'name' => '',
|
||||||
|
'searchable' => 'true',
|
||||||
|
'orderable' => 'false',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'order' => { '0' => { 'column' => '0', 'dir' => 'asc' } },
|
||||||
|
'start' => '0',
|
||||||
|
'length' => '10',
|
||||||
|
'search' => { 'value' => '', 'regex' => 'false' },
|
||||||
|
'_' => '1532637360488'
|
||||||
|
}.with_indifferent_access
|
||||||
|
)
|
||||||
|
allow(view).to receive(:admin_user_path) do |arg|
|
||||||
|
"/admin/users/#{arg.to_param}"
|
||||||
|
end
|
||||||
|
allow(view).to receive(:edit_admin_user_path) do |arg|
|
||||||
|
"/admin/users/#{arg.to_param}/edit"
|
||||||
|
end
|
||||||
|
return view
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(AjaxDatatablesRails).to receive(:old_rails?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'implements AjaxDatatablesRails::Base' do
|
||||||
|
it { is_expected.to respond_to(:view_columns) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'outputs' do
|
||||||
|
let(:user) { User.first }
|
||||||
|
let(:output) { subject.as_json }
|
||||||
|
|
||||||
|
it 'recordsTotal' do
|
||||||
|
expect(output[:recordsTotal]).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'recordsFiltered' do
|
||||||
|
expect(output[:recordsFiltered]).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'data length' do
|
||||||
|
expect(output[:data].length).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has expected data columns' do
|
||||||
|
expect(output[:data].first.keys).to eq(data_cols)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'data columns:' do
|
||||||
|
let(:user_data) { output[:data].first }
|
||||||
|
|
||||||
|
it 'id' do
|
||||||
|
expect(user_data[:id].to_i).to eq(user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'name' do
|
||||||
|
expect(user_data[:name]).to eq(user.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'email' do
|
||||||
|
expect(user_data[:email]).to eq(user.email)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'confirmed_at' do
|
||||||
|
|
||||||
|
expect(Date.parse(user_data[:confirmed_at])).to eq(user.confirmed_at.to_date)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'attended' do
|
||||||
|
expect(user_data[:attended].to_i).to eq(user.attended_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'roles' do
|
||||||
|
expect(user_data[:roles]).to eq('None')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue