From b4695d623fd88b3272db967d82d6938577659dd8 Mon Sep 17 00:00:00 2001 From: Felix Van der Jeugt Date: Thu, 10 Sep 2015 00:35:59 +0200 Subject: [PATCH] replace datatables for user with datagrid for transactions --- Gemfile | 6 +- Gemfile.lock | 13 +- app/assets/javascripts/application.js | 3 - .../jquery.dataTables.columnFilter.js | 829 ------------------ app/assets/stylesheets/application.css | 3 +- app/controllers/concerns/data_table.rb | 59 -- app/controllers/transactions_controller.rb | 4 +- app/controllers/users_controller.rb | 3 - app/grids/transactions_grid.rb | 17 + app/views/transactions/index.html.haml | 4 + app/views/users/show.html.haml | 41 - 11 files changed, 36 insertions(+), 946 deletions(-) delete mode 100644 app/assets/javascripts/jquery.dataTables.columnFilter.js delete mode 100644 app/controllers/concerns/data_table.rb create mode 100644 app/grids/transactions_grid.rb diff --git a/Gemfile b/Gemfile index 5e461b4..f2947fb 100644 --- a/Gemfile +++ b/Gemfile @@ -26,8 +26,9 @@ gem "haml-rails", "~> 0.9" # Pure for css gem 'purecss-rails' -# Use datatables -gem 'jquery-datatables-rails' +# Use DataGrid +gem 'datagrid' +gem 'kaminari' # Use Select2 for selecting users gem 'select2-rails' @@ -90,3 +91,4 @@ gem 'simple_form' # Errors on errbit gem 'airbrake' + diff --git a/Gemfile.lock b/Gemfile.lock index 8f0edc5..ca42fcb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,6 +78,8 @@ GEM simplecov (~> 0.10.0) term-ansicolor (~> 1.3) thor (~> 0.19.1) + datagrid (1.3.9) + rails (>= 3.2.18) debug_inspector (0.0.2) devise (3.5.2) bcrypt (~> 3.0) @@ -124,17 +126,15 @@ GEM jbuilder (2.3.1) activesupport (>= 3.0.0, < 5) multi_json (~> 1.2) - jquery-datatables-rails (3.3.0) - actionpack (>= 3.1) - jquery-rails - railties (>= 3.1) - sass-rails jquery-rails (4.0.5) rails-dom-testing (~> 1.0) railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) jwt (1.5.1) + kaminari (0.16.3) + actionpack (>= 3.0.0) + activesupport (>= 3.0.0) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.3) @@ -292,14 +292,15 @@ DEPENDENCIES capistrano-rbenv coffee-rails (~> 4.1.0) coveralls + datagrid devise factory_girl_rails faker haml-rails (~> 0.9) high_voltage (~> 2.4.0) jbuilder (~> 2.0) - jquery-datatables-rails jquery-rails + kaminari mysql2 (~> 0.3.0) omniauth-oauth2 purecss-rails diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f0b3d27..1398093 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,9 +12,6 @@ // //= require jquery //= require jquery_ujs -//= require dataTables/jquery.dataTables -//= require dataTables/extras/dataTables.responsive -//= require dataTables/jquery.dataTables //= require select2 //= require turbolinks //= require_tree . diff --git a/app/assets/javascripts/jquery.dataTables.columnFilter.js b/app/assets/javascripts/jquery.dataTables.columnFilter.js deleted file mode 100644 index ea4cec3..0000000 --- a/app/assets/javascripts/jquery.dataTables.columnFilter.js +++ /dev/null @@ -1,829 +0,0 @@ -/* -* File: jquery.dataTables.columnFilter.js -* Version: 1.5.6. -* Author: Jovan Popovic -* -* Copyright 2011-2014 Jovan Popovic, all rights reserved. -* -* This source file is free software, under either the GPL v2 license or a -* BSD style license, as supplied with this software. -* -* This source file is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -* or FITNESS FOR A PARTICULAR PURPOSE. -* -* Parameters:" -* @sPlaceHolder String Place where inline filtering function should be placed ("tfoot", "thead:before", "thead:after"). Default is "tfoot" -* @sRangeSeparator String Separator that will be used when range values are sent to the server-side. Default value is "~". -* @sRangeFormat string Default format of the From ... to ... range inputs. Default is From {from} to {to} -* @aoColumns Array Array of the filter settings that will be applied on the columns -*/ -(function ($) { - - - $.fn.columnFilter = function (options) { - - var asInitVals, i, label, th; - - //var sTableId = "table"; - var sRangeFormat = "From {from} to {to}"; - //Array of the functions that will override sSearch_ parameters - var afnSearch_ = new Array(); - var aiCustomSearch_Indexes = new Array(); - - var oFunctionTimeout = null; - - var fnOnFiltered = function () { }; - - function _fnGetColumnValues(oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty) { - /// - ///Return values in the column - /// - ///DataTables settings - ///Id of the column - ///Return only distinct values - ///Return values only from the filtered rows - ///Ignore empty cells - - // check that we have a column id - if (typeof iColumn == "undefined") return new Array(); - - // by default we only wany unique data - if (typeof bUnique == "undefined") bUnique = true; - - // by default we do want to only look at filtered data - if (typeof bFiltered == "undefined") bFiltered = true; - - // by default we do not wany to include empty values - if (typeof bIgnoreEmpty == "undefined") bIgnoreEmpty = true; - - // list of rows which we're going to loop through - var aiRows; - - // use only filtered rows - if (bFiltered == true) aiRows = oSettings.aiDisplay; - // use all rows - else aiRows = oSettings.aiDisplayMaster; // all row numbers - - // set up data array - var asResultData = new Array(); - - for (var i = 0, c = aiRows.length; i < c; i++) { - var iRow = aiRows[i]; - var aData = oTable.fnGetData(iRow); - var sValue = aData[iColumn]; - - // ignore empty values? - if (bIgnoreEmpty == true && sValue.length == 0) continue; - - // ignore unique values? - else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue; - - // else push the value onto the result data array - else asResultData.push(sValue); - } - - return asResultData.sort(); - } - - function _fnColumnIndex(iColumnIndex) { - if (properties.bUseColVis) - return iColumnIndex; - else - return oTable.fnSettings().oApi._fnVisibleToColumnIndex(oTable.fnSettings(), iColumnIndex); - //return iColumnIndex; - //return oTable.fnSettings().oApi._fnColumnIndexToVisible(oTable.fnSettings(), iColumnIndex); - } - - function fnCreateInput(oTable, regex, smart, bIsNumber, iFilterLength, iMaxLenght) { - var sCSSClass = "text_filter form-control"; - if (bIsNumber) - sCSSClass = "number_filter form-control"; - - label = label.replace(/(^\s*)|(\s*$)/g, ""); - var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch; - var search_init = 'search_init '; - var inputvalue = label; - if (currentFilter != '' && currentFilter != '^') { - if (bIsNumber && currentFilter.charAt(0) == '^') - inputvalue = currentFilter.substr(1); //ignore trailing ^ - else - inputvalue = currentFilter; - search_init = ''; - } - - var input = $(''); - if (iMaxLenght != undefined && iMaxLenght != -1) { - input.attr('maxlength', iMaxLenght); - } - th.html(input); - if (bIsNumber) - th.wrapInner(''); - else - th.wrapInner(''); - - asInitVals[i] = label; - var index = i; - - if (bIsNumber && !oTable.fnSettings().oFeatures.bServerSide) { - input.keyup(function () { - /* Filter on the column all numbers that starts with the entered value */ - oTable.fnFilter('^' + this.value, _fnColumnIndex(index), true, false); //Issue 37 - fnOnFiltered(); - }); - } else { - input.keyup(function () { - if (oTable.fnSettings().oFeatures.bServerSide && iFilterLength != 0) { - //If filter length is set in the server-side processing mode - //Check has the user entered at least iFilterLength new characters - - var currentFilter = oTable.fnSettings().aoPreSearchCols[index].sSearch; - var iLastFilterLength = $(this).data("dt-iLastFilterLength"); - if (typeof iLastFilterLength == "undefined") - iLastFilterLength = 0; - var iCurrentFilterLength = this.value.length; - if (Math.abs(iCurrentFilterLength - iLastFilterLength) < iFilterLength - //&& currentFilter.length == 0 //Why this? - ) { - //Cancel the filtering - return; - } - else { - //Remember the current filter length - $(this).data("dt-iLastFilterLength", iCurrentFilterLength); - } - } - /* Filter on the column (the index) of this element */ - oTable.fnFilter(this.value, _fnColumnIndex(index), regex, smart); //Issue 37 - fnOnFiltered(); - }); - } - - input.focus(function () { - if ($(this).hasClass("search_init")) { - $(this).removeClass("search_init"); - this.value = ""; - } - }); - input.blur(function () { - if (this.value == "") { - $(this).addClass("search_init"); - this.value = asInitVals[index]; - } - }); - } - - function fnCreateRangeInput(oTable) { - - //var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch; - th.html(_fnRangeLabelPart(0)); - var sFromId = oTable.attr("id") + '_range_from_' + i; - var from = $(''); - th.append(from); - th.append(_fnRangeLabelPart(1)); - var sToId = oTable.attr("id") + '_range_to_' + i; - var to = $(''); - th.append(to); - th.append(_fnRangeLabelPart(2)); - th.wrapInner(''); - var index = i; - aiCustomSearch_Indexes.push(i); - - - - //------------start range filtering function - - - /* Custom filtering function which will filter data in column four between two values - * Author: Allan Jardine, Modified by Jovan Popovic - */ - //$.fn.dataTableExt.afnFiltering.push( - oTable.dataTableExt.afnFiltering.push( - function (oSettings, aData, iDataIndex) { - if (oTable.attr("id") != oSettings.sTableId) - return true; - // Try to handle missing nodes more gracefully - if (document.getElementById(sFromId) == null) - return true; - var iMin = document.getElementById(sFromId).value * 1; - var iMax = document.getElementById(sToId).value * 1; - var iValue = aData[_fnColumnIndex(index)] == "-" ? 0 : aData[_fnColumnIndex(index)] * 1; - if (iMin == "" && iMax == "") { - return true; - } - else if (iMin == "" && iValue <= iMax) { - return true; - } - else if (iMin <= iValue && "" == iMax) { - return true; - } - else if (iMin <= iValue && iValue <= iMax) { - return true; - } - return false; - } - ); - //------------end range filtering function - - - - $('#' + sFromId + ',#' + sToId, th).keyup(function () { - - var iMin = document.getElementById(sFromId).value * 1; - var iMax = document.getElementById(sToId).value * 1; - if (iMin != 0 && iMax != 0 && iMin > iMax) - return; - - oTable.fnDraw(); - fnOnFiltered(); - }); - - - } - - - function fnCreateDateRangeInput(oTable) { - - var aoFragments = sRangeFormat.split(/[}{]/); - - th.html(""); - //th.html(_fnRangeLabelPart(0)); - var sFromId = oTable.attr("id") + '_range_from_' + i; - var from = $(''); - from.datepicker(); - //th.append(from); - //th.append(_fnRangeLabelPart(1)); - var sToId = oTable.attr("id") + '_range_to_' + i; - var to = $(''); - //th.append(to); - //th.append(_fnRangeLabelPart(2)); - - for (ti = 0; ti < aoFragments.length; ti++) { - - if (aoFragments[ti] == properties.sDateFromToken) { - th.append(from); - } else { - if (aoFragments[ti] == properties.sDateToToken) { - th.append(to); - } else { - th.append(aoFragments[ti]); - } - } - - - } - - - th.wrapInner(''); - to.datepicker(); - var index = i; - aiCustomSearch_Indexes.push(i); - - - //------------start date range filtering function - - //$.fn.dataTableExt.afnFiltering.push( - oTable.dataTableExt.afnFiltering.push( - function (oSettings, aData, iDataIndex) { - if (oTable.attr("id") != oSettings.sTableId) - return true; - - var dStartDate = from.datepicker("getDate"); - - var dEndDate = to.datepicker("getDate"); - - if (dStartDate == null && dEndDate == null) { - return true; - } - - var dCellDate = null; - try { - if (aData[_fnColumnIndex(index)] == null || aData[_fnColumnIndex(index)] == "") - return false; - dCellDate = $.datepicker.parseDate($.datepicker.regional[""].dateFormat, aData[_fnColumnIndex(index)]); - } catch (ex) { - return false; - } - if (dCellDate == null) - return false; - - - if (dStartDate == null && dCellDate <= dEndDate) { - return true; - } - else if (dStartDate <= dCellDate && dEndDate == null) { - return true; - } - else if (dStartDate <= dCellDate && dCellDate <= dEndDate) { - return true; - } - return false; - } - ); - //------------end date range filtering function - - $('#' + sFromId + ',#' + sToId, th).change(function () { - oTable.fnDraw(); - fnOnFiltered(); - }); - - - } - - function fnCreateColumnSelect(oTable, aData, iColumn, nTh, sLabel, bRegex, oSelected, bMultiselect) { - if (aData == null) - aData = _fnGetColumnValues(oTable.fnSettings(), iColumn, true, false, true); - var index = iColumn; - var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch; - if (currentFilter == null || currentFilter == "")//Issue 81 - currentFilter = oSelected; - - var r = ''; - } - var j = 0; - var iLen = aData.length; - for (j = 0; j < iLen; j++) { - if (typeof (aData[j]) != 'object') { - var selected = ''; - if (escape(aData[j]) == currentFilter - || escape(aData[j]) == escape(currentFilter) - ) - selected = 'selected ' - r += ''; - } - else { - var selected = ''; - if (bRegex) { - //Do not escape values if they are explicitely set to avoid escaping special characters in the regexp - if (aData[j].value == currentFilter) selected = 'selected '; - r += ''; - } else { - if (escape(aData[j].value) == currentFilter) selected = 'selected '; - r += ''; - } - } - } - - var select = $(r + ''); - nTh.html(select); - nTh.wrapInner(''); - - if(bMultiselect) { - select.change(function () { - if ($(this).val() != "") { - $(this).removeClass("search_init"); - } else { - $(this).addClass("search_init"); - } - var selectedOptions = $(this).val(); - var asEscapedFilters = []; - if(selectedOptions==null || selectedOptions==[]){ - var re = '^(.*)$'; - }else{ - $.each( selectedOptions, function( i, sFilter ) { - asEscapedFilters.push( fnRegExpEscape( sFilter ) ); - } ); - var re = '^(' + asEscapedFilters.join('|') + ')$'; - } - - oTable.fnFilter( re, index, true, false ); - }); - } else { - select.change(function () { - //var val = $(this).val(); - if ($(this).val() != "") { - $(this).removeClass("search_init"); - } else { - $(this).addClass("search_init"); - } - if (bRegex) - oTable.fnFilter($(this).val(), iColumn, bRegex); //Issue 41 - else - oTable.fnFilter(unescape($(this).val()), iColumn); //Issue 25 - fnOnFiltered(); - }); - if (currentFilter != null && currentFilter != "")//Issue 81 - oTable.fnFilter(unescape(currentFilter), iColumn); - } - } - - function fnCreateSelect(oTable, aData, bRegex, oSelected, bMultiselect) { - var oSettings = oTable.fnSettings(); - if ( (aData == null || typeof(aData) == 'function' ) && oSettings.sAjaxSource != "" && !oSettings.oFeatures.bServerSide) { - // Add a function to the draw callback, which will check for the Ajax data having - // been loaded. Use a closure for the individual column elements that are used to - // built the column filter, since 'i' and 'th' (etc) are locally "global". - oSettings.aoDrawCallback.push({ - "fn": (function (iColumn, nTh, sLabel) { - return function (oSettings) { - // Only rebuild the select on the second draw - i.e. when the Ajax - // data has been loaded. - if (oSettings.iDraw == 2 && oSettings.sAjaxSource != null && oSettings.sAjaxSource != "" && !oSettings.oFeatures.bServerSide) { - return fnCreateColumnSelect(oTable, aData && aData(oSettings.aoData, oSettings), _fnColumnIndex(iColumn), nTh, sLabel, bRegex, oSelected, bMultiselect); //Issue 37 - } - }; - })(i, th, label), - "sName": "column_filter_" + i - }); - } - // Regardless of the Ajax state, build the select on first pass - fnCreateColumnSelect(oTable, typeof(aData) == 'function' ? null: aData, _fnColumnIndex(i), th, label, bRegex, oSelected, bMultiselect); //Issue 37 - - } - - function fnRegExpEscape( sText ) { - return sText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); - }; - - function fnCreateDropdown(aData) { - var index = i; - var r = ''); - th.html(select); - th.wrapInner(''); - select.find('li').click(function () { - oTable.fnFilter($(this).data('value'), index); - }); - } - - - function fnCreateCheckbox(oTable, aData) { - - if (aData == null) - aData = _fnGetColumnValues(oTable.fnSettings(), i, true, true, true); - var index = i; - - var r = '', j, iLen = aData.length; - - //clean the string - var localLabel = label.replace('%', 'Perc').replace("&", "AND").replace("$", "DOL").replace("£", "STERL").replace("@", "AT").replace(/\s/g, "_"); - localLabel = localLabel.replace(/[^a-zA-Z 0-9]+/g, ''); - //clean the string - - //button label override - var labelBtn = label; - if (properties.sFilterButtonText != null || properties.sFilterButtonText != undefined) { - labelBtn = properties.sFilterButtonText; - } - - var relativeDivWidthToggleSize = 10; - var numRow = 12; //numero di checkbox per colonna - var numCol = Math.floor(iLen / numRow); - if (iLen % numRow > 0) { - numCol = numCol + 1; - }; - - //count how many column should be generated and split the div size - var divWidth = 100 / numCol - 2; - - var divWidthToggle = relativeDivWidthToggleSize * numCol; - - if (numCol == 1) { - divWidth = 20; - } - - var divRowDef = '
'; - var divClose = '
'; - - var uniqueId = oTable.attr("id") + localLabel; - var buttonId = "chkBtnOpen" + uniqueId; - var checkToggleDiv = uniqueId + "-flt-toggle"; - r += ''; //filter button witch open dialog - r += '
'; //dialog div - //r+= '
'; //reset button and its div - r += divRowDef; - - for (j = 0; j < iLen; j++) { - - //if last check close div - if (j % numRow == 0 && j != 0) { - r += divClose + divRowDef; - } - - var sLabel = aData[j]; - var sValue = aData[j]; - - if (typeof (aData[j]) == 'object') { - sLabel = aData[j].label; - sValue = aData[j].value; - } - - //check button - r += '' + sLabel + '
'; - - var checkbox = $(r); - th.html(checkbox); - th.wrapInner(''); - //on every checkbox selection - checkbox.change(function () { - - var search = ''; - var or = '|'; //var for select checks in 'or' into the regex - var resSize = $('input:checkbox[name="' + localLabel + '"]:checked').size(); - $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index) { - - //search = search + ' ' + $(this).val(); - //concatenation for selected checks in or - if ((index == 0 && resSize == 1) - || (index != 0 && index == resSize - 1)) { - or = ''; - } - //trim - search = search.replace(/^\s+|\s+$/g, ""); - search = search + $(this).val() + or; - or = '|'; - - }); - - - if (search != "") { - $('input:checkbox[name="' + localLabel + '"]').removeClass("search_init"); - } else { - $('input:checkbox[name="' + localLabel + '"]').addClass("search_init"); - } - /* Old code for setting search_init CSS class on checkboxes if any of them is checked - for (var jj = 0; jj < iLen; jj++) { - if (search != "") { - $('#' + aData[jj]).removeClass("search_init"); - } else { - $('#' + aData[jj]).addClass("search_init"); - } - } - */ - - //execute search - oTable.fnFilter(search, index, true, false); - fnOnFiltered(); - }); - } - - //filter button - $('#' + buttonId).button(); - //dialog - $('#' + checkToggleDiv).dialog({ - //height: 140, - autoOpen: false, - //show: "blind", - hide: "blind", - buttons: [{ - text: "Reset", - click: function () { - //$('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected - $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index3) { - $(this).attr('checked', false); - $(this).addClass("search_init"); - }); - oTable.fnFilter('', index, true, false); - fnOnFiltered(); - return false; - } - }, - { - text: "Close", - click: function () { $(this).dialog("close"); } - } - ] - }); - - - $('#' + buttonId).click(function () { - - $('#' + checkToggleDiv).dialog('open'); - var target = $(this); - $('#' + checkToggleDiv).dialog("widget").position({ my: 'top', - at: 'bottom', - of: target - }); - - return false; - }); - - var fnOnFilteredCurrent = fnOnFiltered; - - fnOnFiltered = function () { - var target = $('#' + buttonId); - $('#' + checkToggleDiv).dialog("widget").position({ my: 'top', - at: 'bottom', - of: target - }); - fnOnFilteredCurrent(); - }; - //reset - /* - $('#'+buttonId+"Reset").button(); - $('#'+buttonId+"Reset").click(function(){ - $('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected - $('input:checkbox[name="'+localLabel+'"]:checked').each(function(index3) { - $(this).attr('checked', false); - $(this).addClass("search_init"); - }); - oTable.fnFilter('', index, true, false); - return false; - }); - */ - } - - - - - function _fnRangeLabelPart(iPlace) { - switch (iPlace) { - case 0: - return sRangeFormat.substring(0, sRangeFormat.indexOf("{from}")); - case 1: - return sRangeFormat.substring(sRangeFormat.indexOf("{from}") + 6, sRangeFormat.indexOf("{to}")); - default: - return sRangeFormat.substring(sRangeFormat.indexOf("{to}") + 4); - } - } - - - - - var oTable = this; - - var defaults = { - sPlaceHolder: "foot", - sRangeSeparator: "~", - iFilteringDelay: 500, - aoColumns: null, - sRangeFormat: "From {from} to {to}", - sDateFromToken: "from", - sDateToToken: "to" - }; - - var properties = $.extend(defaults, options); - - return this.each(function () { - - if (!oTable.fnSettings().oFeatures.bFilter) - return; - asInitVals = new Array(); - - var aoFilterCells = oTable.fnSettings().aoFooter[0]; - - var oHost = oTable.fnSettings().nTFoot; //Before fix for ColVis - var sFilterRow = "tr"; //Before fix for ColVis - - if (properties.sPlaceHolder == "head:after") { - var tr = $("tr:first", oTable.fnSettings().nTHead).detach(); - //tr.appendTo($(oTable.fnSettings().nTHead)); - if (oTable.fnSettings().bSortCellsTop) { - tr.prependTo($(oTable.fnSettings().nTHead)); - //tr.appendTo($("thead", oTable)); - aoFilterCells = oTable.fnSettings().aoHeader[1]; - } - else { - tr.appendTo($(oTable.fnSettings().nTHead)); - //tr.prependTo($("thead", oTable)); - aoFilterCells = oTable.fnSettings().aoHeader[0]; - } - - sFilterRow = "tr:last"; - oHost = oTable.fnSettings().nTHead; - - } else if (properties.sPlaceHolder == "head:before") { - - if (oTable.fnSettings().bSortCellsTop) { - var tr = $("tr:first", oTable.fnSettings().nTHead).detach(); - tr.appendTo($(oTable.fnSettings().nTHead)); - aoFilterCells = oTable.fnSettings().aoHeader[1]; - } else { - aoFilterCells = oTable.fnSettings().aoHeader[0]; - } - /*else { - //tr.prependTo($("thead", oTable)); - sFilterRow = "tr:first"; - }*/ - - sFilterRow = "tr:first"; - - oHost = oTable.fnSettings().nTHead; - - - } - - //$(sFilterRow + " th", oHost).each(function (index) {//bug with ColVis - $(aoFilterCells).each(function (index) {//fix for ColVis - i = index; - var aoColumn = { type: "text", - bRegex: false, - bSmart: true, - iMaxLenght: -1, - iFilterLength: 0 - }; - if (properties.aoColumns != null) { - if (properties.aoColumns.length < i || properties.aoColumns[i] == null) - return; - aoColumn = properties.aoColumns[i]; - } - //label = $(this).text(); //Before fix for ColVis - label = $($(this)[0].cell).text(); //Fix for ColVis - if (aoColumn.sSelector == null) { - //th = $($(this)[0]);//Before fix for ColVis - th = $($(this)[0].cell); //Fix for ColVis - } - else { - th = $(aoColumn.sSelector); - if (th.length == 0) - th = $($(this)[0].cell); - } - - if (aoColumn != null) { - if (aoColumn.sRangeFormat != null) - sRangeFormat = aoColumn.sRangeFormat; - else - sRangeFormat = properties.sRangeFormat; - switch (aoColumn.type) { - case "null": - break; - case "number": - fnCreateInput(oTable, true, false, true, aoColumn.iFilterLength, aoColumn.iMaxLenght); - break; - case "select": - if (aoColumn.bRegex != true) - aoColumn.bRegex = false; - fnCreateSelect(oTable, aoColumn.values, aoColumn.bRegex, aoColumn.selected, aoColumn.multiple); - break; - case "number-range": - fnCreateRangeInput(oTable); - break; - case "date-range": - fnCreateDateRangeInput(oTable); - break; - case "checkbox": - fnCreateCheckbox(oTable, aoColumn.values); - break; - case "twitter-dropdown": - case "dropdown": - fnCreateDropdown(aoColumn.values); - break; - case "text": - default: - bRegex = (aoColumn.bRegex == null ? false : aoColumn.bRegex); - bSmart = (aoColumn.bSmart == null ? false : aoColumn.bSmart); - fnCreateInput(oTable, bRegex, bSmart, false, aoColumn.iFilterLength, aoColumn.iMaxLenght); - break; - - } - } - }); - - for (j = 0; j < aiCustomSearch_Indexes.length; j++) { - //var index = aiCustomSearch_Indexes[j]; - var fnSearch_ = function () { - var id = oTable.attr("id"); - return $("#" + id + "_range_from_" + aiCustomSearch_Indexes[j]).val() + properties.sRangeSeparator + $("#" + id + "_range_to_" + aiCustomSearch_Indexes[j]).val() - } - afnSearch_.push(fnSearch_); - } - - if (oTable.fnSettings().oFeatures.bServerSide) { - - var fnServerDataOriginal = oTable.fnSettings().fnServerData; - - oTable.fnSettings().fnServerData = function (sSource, aoData, fnCallback) { - - for (j = 0; j < aiCustomSearch_Indexes.length; j++) { - var index = aiCustomSearch_Indexes[j]; - - for (k = 0; k < aoData.length; k++) { - if (aoData[k].name == "sSearch_" + index) - aoData[k].value = afnSearch_[j](); - } - } - aoData.push({ "name": "sRangeSeparator", "value": properties.sRangeSeparator }); - - if (fnServerDataOriginal != null) { - try { - fnServerDataOriginal(sSource, aoData, fnCallback, oTable.fnSettings()); //TODO: See Issue 18 - } catch (ex) { - fnServerDataOriginal(sSource, aoData, fnCallback); - } - } - else { - $.getJSON(sSource, aoData, function (json) { - fnCallback(json) - }); - } - }; - - } - - }); - - }; - - - - -})(jQuery); \ No newline at end of file diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 776ece0..bd8d49c 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -11,9 +11,8 @@ * file per style scope. * *= require_tree . - *= require dataTables/extras/dataTables.responsive - *= require dataTables/jquery.dataTables *= require select2 + *= require datagrid *= require_self *= require purecss */ diff --git a/app/controllers/concerns/data_table.rb b/app/controllers/concerns/data_table.rb deleted file mode 100644 index d961a0f..0000000 --- a/app/controllers/concerns/data_table.rb +++ /dev/null @@ -1,59 +0,0 @@ - -module DataTable - extend ActiveSupport::Concern - - def apply_filter(user, params) - params = sanitize_params(params) - selection_to_json user, params[:draw], user.transactions - .where(created_at: params[:columns][:time][:lower]..params[:columns][:time][:upper]) - .where("('debtor' = :id AND 'creditor' LIKE :peer) OR ('creditor' = :id AND 'debtor' LIKE :peer)", id: user.id, peer: "%#{params[:columns][:peer][:value]}%") - .where("'issuer' LIKE :re", re: "%#{params[:columns][:issuer][:value]}%") - .where("'message' LIKE :re", re: "%#{params[:columns][:message][:value]}%") - # TODO: what about nil? - end - - private - - def sanitize_params(params) - # Parsing according to https://datatables.net/manual/server-side - clean = { - draw: params.require(:draw).to_i, - start: params.require(:start).to_i, - length: params.require(:length).to_i, - columns: Hash.new - } - params.require(:columns).each do |i, column| - type, value = column.require(:search)[:value].split(':') - h = clean[:columns][column.require(:data).to_sym] = { - name: column[:name], - searchable: column[:searchable] == 'true', - orderable: column[:orderable] == 'true', - type: type - } - if type == 'number-range' - h[:lower], h[:upper] = value.split('~').map &:to_i - elsif type == 'date-range' - h[:lower], h[:upper] = value.split('~').map &:to_datetime - else - h[:value] = value - end - end - return clean - end - - def selection_to_json(user, draw, selection) - { - draw: draw, - recordsTotal: user.transactions.count, - recordsFiltered: selection.count, - data: selection.offset(params[:start]).take(params[:length]).map { |transaction| { - time: transaction.created_at, - amount: transaction.signed_amount_for(user), - peer: transaction.peer_of(user).try(:name), - issuer: transaction.issuer.name, - message: transaction.message, - }} - } - end -end - diff --git a/app/controllers/transactions_controller.rb b/app/controllers/transactions_controller.rb index 7b5626d..6042114 100644 --- a/app/controllers/transactions_controller.rb +++ b/app/controllers/transactions_controller.rb @@ -6,7 +6,9 @@ class TransactionsController < ApplicationController before_action :authenticate_user_or_client!, only: :create def index - @transactions = Transaction.all + @grid = TransactionsGrid.new(params[:transactions_grid]) do |scope| + scope.page(params[:page]) + end end def new diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6dcaeb3..6fd3ae0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,13 +1,10 @@ class UsersController < ApplicationController load_and_authorize_resource - include DataTable - def show @user = User.find(params[:id]) respond_to do |format| format.html - format.json { render json: apply_filter(@user, params) } end end diff --git a/app/grids/transactions_grid.rb b/app/grids/transactions_grid.rb new file mode 100644 index 0000000..4a4d428 --- /dev/null +++ b/app/grids/transactions_grid.rb @@ -0,0 +1,17 @@ +class TransactionsGrid + + include Datagrid + + scope do + Transaction + end + + filter(:id, :integer) + filter(:created_at, :date, range: true) + + column(:id) + column(:amount) + column(:created_at) do |model| + model.created_at.to_date + end +end diff --git a/app/views/transactions/index.html.haml b/app/views/transactions/index.html.haml index e69de29..c08d315 100644 --- a/app/views/transactions/index.html.haml +++ b/app/views/transactions/index.html.haml @@ -0,0 +1,4 @@ += datagrid_form_for @grid, :method => :get, :url => transactions_path += paginate(@grid.assets) += datagrid_table @grid += paginate(@grid.assets) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 288c694..5dc47d5 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -26,44 +26,3 @@ %td.input-listen{ 'data-input-type': 'text' } %input{type: 'text', placeholder: 'Filter on Message'} -:javascript - $(document).ready(function() { - var table = $('#transactions').DataTable({ - processing: true, - serverSide: true, - searching: false, - lengthChange: false, - ordering: false, - ajax: $('#transactions').data('source'), - pagingType: 'full_numbers', - autoWidth: false, - responsive: true, - columns: [ - { data: 'time', name: 'Time', className: 'min-tablet-l'}, - { data: 'amount', name: 'Amount', className: 'min-mobile'}, - { data: 'peer', name: 'Peer', className: 'min-mobile'}, - { data: 'issuer', name: 'Issuer', className: 'min-desktop'}, - { data: 'message', name: 'Message', className: 'min-tablet-p'}, - ] - }); - - var tds = $('.input-listen'); - var i = 0; - table.columns().every(function() { - var column = this - tds.eq(i).find('input').on('keyup change', function() { - var value = null; - var td = $(this).parent(); - if(td.hasClass('bound')) { - value = td.find('.lower-bound').val() + '~' + td.find('.upper-bound').val(); - } else { - value = $(this).val() - } - value = td.attr('data-input-type') + ':' + value - if(column.search() !== value) { - column.search(value).draw(); - } - }); - i = i + 1; - }); - });