'; //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/controllers/concerns/data_table.rb b/app/controllers/concerns/data_table.rb
new file mode 100644
index 0000000..a6268de
--- /dev/null
+++ b/app/controllers/concerns/data_table.rb
@@ -0,0 +1,23 @@
+
+module DataTable
+ extend ActiveSupport::Concern
+
+ def apply_filter(user, params)
+ p selection_to_json(user, user.transactions)
+ selection_to_json(user, user.transactions)
+ end
+
+ private
+
+ def selection_to_json(user, selection)
+ { data: selection.map { |transaction| {
+ amount: transaction.signed_amount_for(user),
+ origin: transaction.origin,
+ message: transaction.message,
+ peer: transaction.peer_of(user).try(:name),
+ time: transaction.created_at
+ } }
+ }
+ end
+end
+
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index f11be9d..6dcaeb3 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,13 +1,13 @@
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: TransactionDatatable.new(
- view_context, {user: @user})
- }
+ format.json { render json: apply_filter(@user, params) }
end
end
diff --git a/app/datatables/transaction_datatable.rb b/app/datatables/transaction_datatable.rb
deleted file mode 100644
index 69ebfa3..0000000
--- a/app/datatables/transaction_datatable.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-class TransactionDatatable < AjaxDatatablesRails::Base
- include TransactionsHelper
-
- def sortable_columns
- @sortable_columns ||= ['Transaction.amount']
- end
-
- def searchable_columns
- @searchable_columns ||= []
- end
-
- private
- def data
- records.map do |record|
- [ amount_in_perspective(record, options[:user]),
- record.origin,
- record.message,
- get_transaction_peer(record, options[:user]).name,
- record.created_at.strftime('%d/%m/%y %H:%M')
- ]
- end
- end
-
- def get_raw_records
- options[:user].transactions.eager_load(:debtor, :creditor)
- end
-end
diff --git a/app/helpers/transactions_helper.rb b/app/helpers/transactions_helper.rb
index 0300537..36098d1 100644
--- a/app/helpers/transactions_helper.rb
+++ b/app/helpers/transactions_helper.rb
@@ -1,11 +1,2 @@
module TransactionsHelper
- def get_transaction_peer(transaction, user)
- return transaction.creditor if user == transaction.debtor
- return transaction.debtor if user == transaction.creditor
- end
-
- def amount_in_perspective(transaction, user)
- return -transaction.amount if user == transaction.debtor
- return transaction.amount if user == transaction.creditor
- end
end
diff --git a/app/models/transaction.rb b/app/models/transaction.rb
index 2e6f346..eb5cc51 100644
--- a/app/models/transaction.rb
+++ b/app/models/transaction.rb
@@ -26,6 +26,16 @@ class Transaction < ActiveRecord::Base
Client.find_by name: origin
end
+ def peer_of(user)
+ return creditor if user == debtor
+ return debtor if user == creditor
+ end
+
+ def signed_amount_for(user)
+ return -amount if user == debtor
+ return amount if user == creditor
+ end
+
private
def recalculate_balances
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 119b6ea..b6524c2 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,6 +1,12 @@
%h2= @user.name
%table#transactions.display{data: { source: user_path(@user) }}
%thead
+ %tr
+ %td Amount
+ %td Origin
+ %td Message
+ %td Peer
+ %td Time
%tr
%th Amount
%th Origin
@@ -11,10 +17,28 @@
:javascript
$(document).ready(function() {
- $('#transactions').DataTable({
- 'processing': true,
- 'serverSide': true,
- 'ajax': $('#transactions').data('source'),
- 'pagingType': 'full_numbers'
- });
+ $('#transactions').dataTable({
+ processing: true,
+ serverSide: true,
+ searching: true,
+ ordering: false,
+ ajax: $('#transactions').data('source'),
+ pagingType: 'full_numbers',
+ columns: [
+ { data: 'amount', name: 'Amount' },
+ { data: 'origin', name: 'Origin' },
+ { data: 'message', name: 'Message' },
+ { data: 'peer', name: 'Peer' },
+ { data: 'time', name: 'Time' }
+ ]
+ }).columnFilter({
+ sPlaceHolder: 'head:before',
+ aoColumns: [
+ { type: 'number-range' },
+ { type: 'text' },
+ { type: 'text' },
+ { type: 'text' },
+ { type: 'date-range' }
+ ]
+ });
});