use ajax to load campaign listing instead of prerendering

This commit is contained in:
Andris Reinman 2016-04-29 11:52:02 +03:00
parent 4132ef8913
commit 5876977f43
5 changed files with 144 additions and 75 deletions

View file

@ -32,6 +32,84 @@ module.exports.list = (start, limit, callback) => {
});
};
module.exports.filter = (request, callback) => {
let columns = ['#', 'name', 'description', 'status', 'created'];
let processQuery = queryData => {
db.getConnection((err, connection) => {
if (err) {
return callback(err);
}
let query = 'SELECT COUNT(id) AS total FROM `campaigns`';
let values = [];
if (queryData.where) {
query += ' WHERE ' + queryData.where;
values = values.concat(queryData.values || []);
}
connection.query(query, values, (err, total) => {
if (err) {
connection.release();
return callback(err);
}
total = total && total[0] && total[0].total || 0;
let ordering = [];
if (request.order && request.order.length) {
request.order.forEach(order => {
let orderField = columns[Number(order.column)];
let orderDirection = (order.dir || '').toString().toLowerCase() === 'desc' ? 'DESC' : 'ASC';
if (orderField) {
ordering.push('`' + orderField + '` ' + orderDirection);
}
});
}
if (!ordering.length) {
ordering.push('`created` DESC');
}
let args = [Number(request.length) || 50, Number(request.start) || 0];
let query;
if (request.search && request.search.value) {
query = 'SELECT SQL_CALC_FOUND_ROWS * FROM `campaigns` WHERE name LIKE ? ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?';
let searchVal = '%' + request.search.value.replace(/\\/g, '\\\\').replace(/([%_])/g, '\\$1') + '%';
args = [searchVal].concat(queryData.values || []).concat(args);
} else {
query = 'SELECT SQL_CALC_FOUND_ROWS * FROM `campaigns` WHERE 1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' ORDER BY ' + ordering.join(', ') + ' LIMIT ? OFFSET ?';
args = [].concat(queryData.values || []).concat(args);
}
connection.query(query, args, (err, rows) => {
if (err) {
connection.release();
return callback(err);
}
connection.query('SELECT FOUND_ROWS() AS total', (err, filteredTotal) => {
connection.release();
if (err) {
return callback(err);
}
let subscriptions = rows.map(row => tools.convertKeys(row));
filteredTotal = filteredTotal && filteredTotal[0] && filteredTotal[0].total || 0;
return callback(null, subscriptions, total, filteredTotal);
});
});
});
});
};
processQuery(false);
};
module.exports.getByCid = (cid, callback) => {
cid = (cid || '').toString().trim();
if (!cid) {

View file

@ -29,8 +29,20 @@ $('.data-table').each(function () {
$('.data-table-ajax').each(function () {
var rowSort = $(this).data('rowSort') || false;
var columns = false;
var listArgs = $(this).data('listArgs') || false;
var ajaxUrl = '/lists/ajax/' + $(this).data('listId') + (listArgs ? '?' + listArgs : '');
var topicUrl = $(this).data('topicUrl') || '/lists';
var topicArgs = $(this).data('topicArgs') || false;
var topicId = $(this).data('topicId') || '';
var sortColumn = Number($(this).data('sortColumn')) || 1;
var sortOrder = ($(this).data('sortOrder') || 'asc').toString().trim().toLowerCase();
// allow only asc and desc
if (sortOrder !== 'desc') {
sortOrder = 'asc';
}
var ajaxUrl = topicUrl + '/ajax/' + topicId + (topicArgs ? '?' + topicArgs : '');
if (rowSort) {
columns = rowSort.split(',').map(function (sort) {
@ -48,11 +60,15 @@ $('.data-table-ajax').each(function () {
type: 'POST'
},
order: [
[1, 'asc']
[sortColumn, sortOrder]
],
columns: columns,
pageLength: 50,
processing: true
}).on('draw', function () {
$('.datestring').each(function () {
$(this).html(moment($(this).data('date')).fromNow());
});
});
});

View file

@ -9,6 +9,7 @@ let settings = require('../lib/models/settings');
let tools = require('../lib/tools');
let striptags = require('striptags');
let passport = require('../lib/passport');
let htmlescape = require('escape-html');
router.all('/*', (req, res, next) => {
if (!req.user) {
@ -20,43 +21,8 @@ router.all('/*', (req, res, next) => {
});
router.get('/', (req, res) => {
let limit = 999999999;
let start = 0;
campaigns.list(start, limit, (err, rows, total) => {
if (err) {
req.flash('danger', err.message || err);
return res.redirect('/');
}
res.render('campaigns/campaigns', {
rows: rows.map((row, i) => {
row.index = start + i + 1;
row.description = striptags(row.description);
switch (row.status) {
case 1:
row.statusText = 'Idling';
break;
case 2:
if (row.scheduled && row.scheduled > new Date()) {
row.statusText = 'Scheduled';
} else {
row.statusText = 'Sending';
}
break;
case 3:
row.statusText = 'Finished';
break;
case 4:
row.statusText = 'Paused';
break;
}
row.createdTimestamp = row.created.getTime();
row.created = row.created.toISOString();
return row;
}),
total
});
title: 'Campaigns'
});
});
@ -211,6 +177,47 @@ router.post('/delete', passport.parseForm, passport.csrfProtection, (req, res) =
});
});
router.post('/ajax', (req, res) => {
campaigns.filter(req.body, (err, data, total, filteredTotal) => {
if (err) {
return res.json({
error: err.message || err,
data: []
});
}
let getStatusText = data => {
switch (data.status) {
case 1:
return 'Idling';
case 2:
if (data.scheduled && data.scheduled > new Date()) {
return 'Scheduled';
}
return 'Sending';
case 3:
return 'Finished';
case 4:
return 'Paused';
}
return 'Other';
};
res.json({
draw: req.body.draw,
recordsTotal: total,
recordsFiltered: filteredTotal,
data: data.map((row, i) => [
(Number(req.body.start) || 0) + 1 + i,
htmlescape(row.name || ''),
htmlescape(striptags(row.description) || ''),
getStatusText(row),
'<span class="datestring" data-date="' + row.created.toISOString() + '" title="' + row.created.toISOString() + '">' + row.created.toISOString() + '</span>'
].concat('<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span><a href="/campaigns/edit/' + row.id + '">Edit</a>'))
});
});
});
router.get('/view/:id', passport.csrfProtection, (req, res) => {
campaigns.get(req.params.id, true, (err, campaign) => {
if (err || !campaign) {

View file

@ -12,7 +12,7 @@
<hr>
<div class="table-responsive">
<table class="table table-bordered table-hover data-table display nowrap" width="100%" data-row-sort="0,1,0,1,1,0">
<table data-topic-url="/campaigns" data-sort-column="4" data-sort-order="desc" class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,0,1,1,0">
<thead>
<th class="col-md-1">
#
@ -33,37 +33,5 @@
&nbsp;
</th>
</thead>
{{#if rows}}
<tbody>
{{#each rows}}
<tr>
<th scope="row">
{{index}}
</th>
<td>
<span class="glyphicon glyphicon-inbox" aria-hidden="true"></span>
<a href="/campaigns/view/{{id}}">
{{name}}
</a>
</td>
<td>
<p class="text-muted">{{description}}</p>
</td>
<td>
<p>{{statusText}}</p>
</td>
<td>
<span class="datestring" data-date="{{created}}" title="{{created}}">{{createdTimestamp}}</span>
</td>
<td>
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
<a href="/campaigns/edit/{{id}}">
Edit
</a>
</td>
</tr>
{{/each}}
</tbody>
{{/if}}
</table>
</div>

View file

@ -71,7 +71,7 @@
<p></p>
<div class="table-responsive">
<table data-list-id="{{id}}" {{#if useSegment}} data-list-args="segment={{useSegment}}" {{/if}} class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1{{customSort}},1,0">
<table data-topic-url="/list" data-topic-id="{{id}}" data-sort-column="1" data-sort-order="asc" {{#if useSegment}} data-topic-args="segment={{useSegment}}" {{/if}} class="table table-bordered table-hover data-table-ajax display nowrap" width="100%" data-row-sort="0,1,1,1{{customSort}},1,0">
<thead>
<tr>
<th class="col-md-1">