feat(stats): Add table with export to csv
This commit is contained in:
1 parent
e2bab9ab8d
commit
25563043f8
2 files changed
+162
-49
No files matched your search
+24
-5
@@ -1,9 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
function is_valid_start_param() {
|
||||||
|
return isset($_GET['start']) && strlen($_GET['start']) > 0 && date_create($_GET['start']);
|
||||||
|
}
|
||||||
|
|
||||||
$semester_start = date_create('first day of august');
|
$semester_start = date_create('first day of august');
|
||||||
if($semester_start > date_create()) {
|
if($semester_start > date_create()) {
|
||||||
$semester_start = date_modify($semester_start, '-1 year');
|
$semester_start = date_modify($semester_start, '-1 year');
|
||||||
}
|
}
|
||||||
$start = isset($_GET['start']) ? date_create($_GET['start']) : $semester_start;
|
$start = is_valid_start_param() ? date_create($_GET['start']) : $semester_start;
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -13,23 +18,33 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="stats.css">
|
<link rel="stylesheet" href="stats.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-6"></div>
|
<div class="col-xs-12">
|
||||||
|
<h1>Medlemskapsstatistikk for DNS</h1>
|
||||||
|
<div class="disclaimer"><em>Disse tallene kan avvike fra faktiske salgtall.</em></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-6">
|
||||||
|
|
||||||
<h2>Salg per dag</h2>
|
<h2>Salg per dag</h2>
|
||||||
<label for="start">Fra</label> <input id="start" name="start" type="date" value="<?php echo date_format($start, 'Y-m-d'); ?>" />
|
<label for="start">Fra</label> <input id="start" name="start" type="date" value="<?php echo date_format($start, 'Y-m-d'); ?>" />
|
||||||
<div id="sales-chart"></div>
|
<div id="sales-chart"></div>
|
||||||
<h2>Salg totalt</h2>
|
|
||||||
|
<h3>Salg totalt</h3>
|
||||||
<div id="sums">
|
<div id="sums">
|
||||||
<div>SMS: <span class="sms big-number"></span></div>
|
<div>SMS: <span class="sms big-number"></span></div>
|
||||||
<div>App: <span class="app big-number"></span></div>
|
<div>App: <span class="app big-number"></span></div>
|
||||||
<div>Bar: <span class="bar big-number"></span></div>
|
<div>Bar: <span class="bar big-number"></span></div>
|
||||||
<div>Totalt: <span class="sum big-number"></span></div>
|
<div>Totalt: <span class="sum big-number"></span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
<h2>Salg idag</h2>
|
<h2>Salg idag</h2>
|
||||||
<em>Dato: <span class="today-date-wrap"></span></em>
|
<em>Dato: <span class="today-date-wrap"></span></em>
|
||||||
<div id="sales-chart-today" style="min-width: 310px; height: 400px; max-width: 600px;"></div>
|
<div id="sales-chart-today" style="min-width: 310px; height: 400px; max-width: 600px;"></div>
|
||||||
@@ -39,7 +54,11 @@
|
|||||||
<div>Bar: <span class="bar-today big-number"></span></div>
|
<div>Bar: <span class="bar-today big-number"></span></div>
|
||||||
<div>Totalt: <span class="sum-today big-number"></span></div>
|
<div>Totalt: <span class="sum-today big-number"></span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="disclaimer"><br><br><br><em>Disse tallene kan avvike fra faktiske salgtall.</em></div>
|
</div>
|
||||||
|
<div class="col-xs-12 col-md-6">
|
||||||
|
<h2>Salg per dag</h2>
|
||||||
|
<button class="export-data-btn btn btn-default btn-sm"><span class="glyphicon glyphicon-download-alt"></span> Eksporter til CSV</button>
|
||||||
|
<table id="sales-table" class="table table-striped"></table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+138
-44
@@ -1,8 +1,10 @@
|
|||||||
var barSales;
|
var barSales;
|
||||||
var appSales;
|
var appSales;
|
||||||
var smsSales;
|
var smsSales;
|
||||||
|
var salesData;
|
||||||
var salesChart;
|
var salesChart;
|
||||||
var salesChartToday;
|
var salesChartToday;
|
||||||
|
var saleTypes = ['bar', 'sms', 'app'];
|
||||||
|
|
||||||
var urls = {
|
var urls = {
|
||||||
cards: 'https://kassa.neuf.no/stats/card-sales/',
|
cards: 'https://kassa.neuf.no/stats/card-sales/',
|
||||||
@@ -15,6 +17,7 @@ function sumSales(memo, num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toHighchartsSeries(memberships) {
|
function toHighchartsSeries(memberships) {
|
||||||
|
memberships = _.sortBy(memberships, 'date');
|
||||||
return _.map(memberships, function (el) {
|
return _.map(memberships, function (el) {
|
||||||
return [moment.utc(el.date).valueOf(), el.sales]
|
return [moment.utc(el.date).valueOf(), el.sales]
|
||||||
});
|
});
|
||||||
@@ -41,7 +44,119 @@ function getAppSales(start) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function recalc(start) {
|
function totals() {
|
||||||
|
var barTotal = _.reduce(barSales, sumSales, 0);
|
||||||
|
$('.bar').html(barTotal);
|
||||||
|
var smsTotal = _.reduce(smsSales, sumSales, 0);
|
||||||
|
$('.sms').html(smsTotal);
|
||||||
|
var appTotal = _.reduce(appSales, sumSales, 0);
|
||||||
|
$('.app').html(appTotal);
|
||||||
|
|
||||||
|
$('.sum').html(barTotal + smsTotal + appTotal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function today() {
|
||||||
|
var today = moment.utc().format('YYYY-MM-DD');
|
||||||
|
$('.today-date-wrap').text(today);
|
||||||
|
var todayBarSales = _.findWhere(barSales, {date: today}) || {sales: 0};
|
||||||
|
$('.bar-today').html(todayBarSales.sales);
|
||||||
|
var todaySmsSales = _.findWhere(smsSales, {date: today}) || {sales: 0};
|
||||||
|
$('.sms-today').html(todaySmsSales.sales);
|
||||||
|
var todayAppSales = _.findWhere(appSales, {date: today}) || {sales: 0};
|
||||||
|
$('.app-today').html(todayAppSales.sales);
|
||||||
|
|
||||||
|
$('.sum-today').html(todayBarSales.sales + todaySmsSales.sales + todayAppSales.sales);
|
||||||
|
|
||||||
|
salesChartToday = new Highcharts.Chart({
|
||||||
|
chart: {
|
||||||
|
renderTo: 'sales-chart-today',
|
||||||
|
plotBackgroundColor: null,
|
||||||
|
plotBorderWidth: null,
|
||||||
|
plotShadow: false,
|
||||||
|
type: 'pie'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: false
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
name: 'Salg', data: [
|
||||||
|
{name: 'Bar-salg', y: todayBarSales.sales},
|
||||||
|
{name: 'SMS-salg', y: todaySmsSales.sales},
|
||||||
|
{name: 'App-salg', y: todayAppSales.sales}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupSalesByDate() {
|
||||||
|
var types = {
|
||||||
|
'bar': barSales,
|
||||||
|
'sms': smsSales,
|
||||||
|
'app': appSales
|
||||||
|
};
|
||||||
|
var salesByDate = {};
|
||||||
|
_.each(types, function (source, type) {
|
||||||
|
_.each(source, function (el) {
|
||||||
|
var d = {};
|
||||||
|
d[el.date] = {
|
||||||
|
'bar': 0,
|
||||||
|
'sms': 0,
|
||||||
|
'app': 0
|
||||||
|
};
|
||||||
|
d[el.date][type]= el.sales;
|
||||||
|
|
||||||
|
$.extend(true, salesByDate, d);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var table_friendly = [];
|
||||||
|
_.each(salesByDate, function (sales, date) {
|
||||||
|
sales.date = date;
|
||||||
|
table_friendly.push(sales);
|
||||||
|
});
|
||||||
|
table_friendly = _.sortBy(table_friendly, 'date').reverse();
|
||||||
|
|
||||||
|
return table_friendly;
|
||||||
|
}
|
||||||
|
|
||||||
|
function salesTable() {
|
||||||
|
var html = '<thead><tr><th>Dato</th><th>Bar</th><th>SMS</th><th>App</th></tr></thead>';
|
||||||
|
html += '<tbody>';
|
||||||
|
salesData = groupSalesByDate();
|
||||||
|
|
||||||
|
_.each(salesData, function(el) {
|
||||||
|
html += '<tr><td>' + el.date + '</td>';
|
||||||
|
_.each(saleTypes, function(type) {
|
||||||
|
html += '<td>' + el[type] + '</td>';
|
||||||
|
});
|
||||||
|
html += '</tr>';
|
||||||
|
});
|
||||||
|
html += '</tbody>';
|
||||||
|
$('#sales-table').html(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toCSV(data) {
|
||||||
|
var csvLines = data.map(function(d){
|
||||||
|
return d.date + ',' + saleTypes.map(function(t) { return d[t]; }).join(',');
|
||||||
|
});
|
||||||
|
var csvHeader = 'date,' +saleTypes.join(',') + '\n';
|
||||||
|
return csvHeader + csvLines.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadCSVFile(csvData, fileName) {
|
||||||
|
csvData = 'data:text/csv;charset=utf-8,' + csvData;
|
||||||
|
var encodedUri = encodeURI(csvData);
|
||||||
|
|
||||||
|
var link = document.createElement("a");
|
||||||
|
link.setAttribute("href", encodedUri);
|
||||||
|
link.setAttribute("download", fileName);
|
||||||
|
document.body.appendChild(link); // Required for FF
|
||||||
|
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalc(start, cb) {
|
||||||
salesChart = new Highcharts.Chart({
|
salesChart = new Highcharts.Chart({
|
||||||
chart: {
|
chart: {
|
||||||
renderTo: 'sales-chart'
|
renderTo: 'sales-chart'
|
||||||
@@ -69,47 +184,12 @@ function recalc(start) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$.when(getSmsSales(start), getAppSales(start), getBarSales(start)).done(function() {
|
$.when(getSmsSales(start), getAppSales(start), getBarSales(start)).done(function() {
|
||||||
/* Totals */
|
totals();
|
||||||
var barTotal = _.reduce(barSales, sumSales, 0);
|
salesTable();
|
||||||
$('.bar').html(barTotal);
|
|
||||||
var smsTotal = _.reduce(smsSales, sumSales, 0);
|
|
||||||
$('.sms').html(smsTotal);
|
|
||||||
var appTotal = _.reduce(appSales, sumSales, 0);
|
|
||||||
$('.app').html(appTotal);
|
|
||||||
|
|
||||||
$('.sum').html(barTotal + smsTotal + appTotal);
|
if(cb) {
|
||||||
|
cb();
|
||||||
/* Today */
|
}
|
||||||
var today = moment.utc().format('YYYY-MM-DD');
|
|
||||||
$('.today-date-wrap').text(today);
|
|
||||||
var todayBarSales = _.findWhere(barSales, {date:today}) || {sales: 0};
|
|
||||||
$('.bar-today').html(todayBarSales.sales);
|
|
||||||
var todaySmsSales = _.findWhere(smsSales, {date:today}) || {sales: 0};
|
|
||||||
$('.sms-today').html(todaySmsSales.sales);
|
|
||||||
var todayAppSales = _.findWhere(appSales, {date:today}) || {sales: 0};
|
|
||||||
$('.app-today').html(todayAppSales.sales);
|
|
||||||
|
|
||||||
$('.sum-today').html(todayBarSales.sales + todaySmsSales.sales + todayAppSales.sales);
|
|
||||||
|
|
||||||
salesChartToday = new Highcharts.Chart({
|
|
||||||
chart: {
|
|
||||||
renderTo: 'sales-chart-today',
|
|
||||||
plotBackgroundColor: null,
|
|
||||||
plotBorderWidth: null,
|
|
||||||
plotShadow: false,
|
|
||||||
type: 'pie'
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: false
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
name: 'Salg', data: [
|
|
||||||
{name: 'Bar-salg', y: todayBarSales.sales},
|
|
||||||
{name: 'SMS-salg', y: todaySmsSales.sales},
|
|
||||||
{name: 'App-salg', y: todayAppSales.sales}
|
|
||||||
]}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,13 +198,27 @@ $(document).ready(function() {
|
|||||||
credits: {enabled: false}
|
credits: {enabled: false}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var $exportBtn = $('.export-data-btn');
|
||||||
var $startInput = $('#start');
|
var $startInput = $('#start');
|
||||||
var start = $startInput.val();
|
|
||||||
recalc(start);
|
|
||||||
|
|
||||||
|
var start = $startInput.val();
|
||||||
|
recalc(start, function() {
|
||||||
|
today();
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Dynamic date change */
|
||||||
$startInput.on('input', function(e) {
|
$startInput.on('input', function(e) {
|
||||||
start = $(e.target).val();
|
start = $(e.target).val();
|
||||||
history.replaceState(null, null, '?start=' + start);
|
history.replaceState(null, null, '?start=' + start);
|
||||||
recalc(start);
|
recalc(start);
|
||||||
})
|
});
|
||||||
|
|
||||||
|
/* Export to CSV */
|
||||||
|
$exportBtn.on('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var fileName = 'medlemskapsstats-' + $startInput.val() + '.csv';
|
||||||
|
var csvData = toCSV(salesData);
|
||||||
|
downloadCSVFile(csvData, fileName);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user