216 lines
5.7 KiB
Plaintext
216 lines
5.7 KiB
Plaintext
|
extends ./master.pug
|
||
|
|
||
|
mixin sortableLinks(col, label)
|
||
|
span.sortables.float-left.mr-2.d-inline-flex.flex-column(style="font-size: 50%")
|
||
|
a(href="#sort:" + col + ":asc") ▲
|
||
|
a(href="#sort:" + col + ":desc") ▼
|
||
|
block
|
||
|
|
||
|
block main
|
||
|
h2
|
||
|
a.float-right(href="/" style="font-size: 50%") ◀ All Countries
|
||
|
=data.name
|
||
|
|
||
|
div.card
|
||
|
div.card-body
|
||
|
canvas.mx-auto(id="main-chart" width="800" height="450")
|
||
|
script.
|
||
|
(function() {
|
||
|
const canvas = document.getElementById('main-chart');
|
||
|
const chart = new Chart(canvas.getContext('2d'), {
|
||
|
type: 'line',
|
||
|
data: {
|
||
|
labels: !{JSON.stringify(data.timeSeriesDaily.map(x => x.key))},
|
||
|
datasets: [
|
||
|
{
|
||
|
label: 'Deaths',
|
||
|
data: !{JSON.stringify(data.timeSeriesDaily.map(x => x.value))},
|
||
|
fill: true,
|
||
|
borderColor: 'rgb(196, 64, 64)',
|
||
|
backgroundColor: 'rgba(196, 64, 64, 0.5)',
|
||
|
}
|
||
|
],
|
||
|
},
|
||
|
options: {
|
||
|
responsive: false,
|
||
|
title: {
|
||
|
display: true,
|
||
|
position: 'top',
|
||
|
text: !{JSON.stringify(data.name || '')}
|
||
|
},
|
||
|
scales: {
|
||
|
yAxes: [
|
||
|
{
|
||
|
display: true,
|
||
|
ticks: {
|
||
|
precision: 0,
|
||
|
beginAtZero: true,
|
||
|
}
|
||
|
},
|
||
|
],
|
||
|
xAxes: [
|
||
|
{
|
||
|
display: true,
|
||
|
},
|
||
|
],
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}());
|
||
|
|
||
|
div#table.table-responsive: table.table.table-sm.table-hover
|
||
|
thead: tr
|
||
|
th #
|
||
|
th: +sortableLinks("name") State/Province
|
||
|
th: +sortableLinks("total") Deaths
|
||
|
th: +sortableLinks("yesterday") …since yesterday
|
||
|
th: +sortableLinks("week") …since last week
|
||
|
th: +sortableLinks("month") …month-to-date
|
||
|
th Last 14 days
|
||
|
tbody: each item, i in data.states
|
||
|
- const yesterday = item.timeSeriesDaily[item.timeSeriesDaily.length - 1].delta || 0;
|
||
|
- const lastWeek = item.timeSeriesDaily[item.timeSeriesDaily.length - 1].value - item.timeSeriesDaily[item.timeSeriesDaily.length - 7].value;
|
||
|
- const lastMonth = item.timeSeriesMonthly[item.timeSeriesMonthly.length - 1].delta || 0;
|
||
|
tr(id="row-" + item.stateSafeName data-name=item.state data-total=item.total data-yesterday=yesterday data-week=lastWeek data-month=lastMonth)
|
||
|
td.sort-order= i + 1
|
||
|
td
|
||
|
if item.state
|
||
|
= item.state
|
||
|
else
|
||
|
em.text-muted All #{item.country}
|
||
|
td.text-right: +formatNumber(item.total)
|
||
|
td.text-right: +formatNumber(yesterday)
|
||
|
td.text-right: +formatNumber(lastWeek)
|
||
|
td.text-right: +formatNumber(lastMonth)
|
||
|
td
|
||
|
canvas(id="sparkline-" + i width="200" height="50")
|
||
|
script.
|
||
|
(function() {
|
||
|
const canvas = document.getElementById('sparkline-#{i}');
|
||
|
const chart = new Chart(canvas.getContext('2d'), {
|
||
|
type: 'line',
|
||
|
data: {
|
||
|
labels: [ 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14 ],
|
||
|
datasets: [
|
||
|
{
|
||
|
data: #{JSON.stringify(item.timeSeriesDaily.slice(-14).map(x => x.value))},
|
||
|
}
|
||
|
],
|
||
|
},
|
||
|
options: {
|
||
|
responsive: false,
|
||
|
legend: {
|
||
|
display: false,
|
||
|
},
|
||
|
elements: {
|
||
|
line: {
|
||
|
borderColor: '#000000',
|
||
|
borderWidth: 1,
|
||
|
},
|
||
|
point: {
|
||
|
radius: 0,
|
||
|
},
|
||
|
},
|
||
|
tooltips: {
|
||
|
enabled: false,
|
||
|
},
|
||
|
scales: {
|
||
|
yAxes: [
|
||
|
{
|
||
|
display: false,
|
||
|
ticks: {
|
||
|
precision: 0,
|
||
|
beginAtZero: true,
|
||
|
}
|
||
|
},
|
||
|
],
|
||
|
xAxes: [
|
||
|
{
|
||
|
display: false,
|
||
|
},
|
||
|
],
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}());
|
||
|
|
||
|
script.
|
||
|
(function() {
|
||
|
const tbody = document.getElementById('table').querySelector('tbody');
|
||
|
const allRows = [].slice.call(tbody.querySelectorAll('tbody tr'));
|
||
|
|
||
|
const resortTable = () => {
|
||
|
let nextChild = null;
|
||
|
for (let i = allRows.length - 1; i >= 0; i--) {
|
||
|
const row = allRows[i];
|
||
|
if (!row) {
|
||
|
continue;
|
||
|
}
|
||
|
if (row === nextChild) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
tbody.insertBefore(row, nextChild);
|
||
|
row.querySelector('.sort-order').textContent = (i + 1).toString();
|
||
|
nextChild = row;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const handleSort = (value, dir) => {
|
||
|
const newSortDir = dir === 'desc' ? 'desc' : 'asc';
|
||
|
const sortByNumberThenName = (attr) => {
|
||
|
allRows.sort((a, b) => {
|
||
|
const aValue = Number(a.getAttribute('data-' + attr));
|
||
|
const bValue = Number(b.getAttribute('data-' + attr));
|
||
|
if (aValue === bValue) {
|
||
|
const aName = a.getAttribute('data-name');
|
||
|
const bName = b.getAttribute('data-name');
|
||
|
return aName.localeCompare(bName);
|
||
|
}
|
||
|
|
||
|
return aValue < bValue ?
|
||
|
(newSortDir === 'asc' ? -1 : 1) :
|
||
|
(newSortDir === 'asc' ? 1 : -1);
|
||
|
});
|
||
|
resortTable();
|
||
|
};
|
||
|
switch (value) {
|
||
|
case 'name':
|
||
|
allRows.sort((a, b) => {
|
||
|
const aName = a.getAttribute('data-name');
|
||
|
const bName = b.getAttribute('data-name');
|
||
|
if (newSortDir === 'asc') {
|
||
|
return aName.localeCompare(bName);
|
||
|
}
|
||
|
|
||
|
return bName.localeCompare(aName);
|
||
|
});
|
||
|
resortTable();
|
||
|
break;
|
||
|
case 'total':
|
||
|
sortByNumberThenName('total');
|
||
|
break;
|
||
|
case 'yesterday':
|
||
|
sortByNumberThenName('yesterday');
|
||
|
break;
|
||
|
case 'week':
|
||
|
sortByNumberThenName('week');
|
||
|
break;
|
||
|
case 'month':
|
||
|
sortByNumberThenName('month');
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const handleHash = (hash) => {
|
||
|
const sortValue = hash.replace(/^#sort:/, '').split(':');
|
||
|
handleSort(sortValue[0], sortValue[1]);
|
||
|
};
|
||
|
|
||
|
window.addEventListener('hashchange', () => {
|
||
|
handleHash(window.location.hash);
|
||
|
});
|
||
|
|
||
|
handleHash(window.location.hash);
|
||
|
}());
|