mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
feat: show placeholders when no data is available (resolve #42)
feat: add jsx as custom language by default (resolve #50)
This commit is contained in:
parent
cecb5e113c
commit
97cb29ee4d
@ -11,4 +11,5 @@ cleanup = false
|
|||||||
max_connections = 2
|
max_connections = 2
|
||||||
|
|
||||||
[languages]
|
[languages]
|
||||||
vue = Vue
|
vue = Vue
|
||||||
|
jsx = JSX
|
@ -1,5 +1,5 @@
|
|||||||
const SHOW_TOP_N = 10
|
const SHOW_TOP_N = 10
|
||||||
const CHART_TARGET_SIZE = 170
|
const CHART_TARGET_SIZE = 200
|
||||||
|
|
||||||
const projectsCanvas = document.getElementById('chart-projects')
|
const projectsCanvas = document.getElementById('chart-projects')
|
||||||
const osCanvas = document.getElementById('chart-os')
|
const osCanvas = document.getElementById('chart-os')
|
||||||
@ -7,6 +7,16 @@ const editorsCanvas = document.getElementById('chart-editor')
|
|||||||
const languagesCanvas = document.getElementById('chart-language')
|
const languagesCanvas = document.getElementById('chart-language')
|
||||||
const machinesCanvas = document.getElementById('chart-machine')
|
const machinesCanvas = document.getElementById('chart-machine')
|
||||||
|
|
||||||
|
const projectContainer = document.getElementById('project-container')
|
||||||
|
const osContainer = document.getElementById('os-container')
|
||||||
|
const editorContainer = document.getElementById('editor-container')
|
||||||
|
const languageContainer = document.getElementById('language-container')
|
||||||
|
const machineContainer = document.getElementById('machine-container')
|
||||||
|
|
||||||
|
const containers = [projectContainer, osContainer, editorContainer, languageContainer, machineContainer]
|
||||||
|
const canvases = [projectsCanvas, osCanvas, editorsCanvas, languagesCanvas, machinesCanvas]
|
||||||
|
const data = [wakapiData.projects, wakapiData.operatingSystems, wakapiData.editors, wakapiData.languages, wakapiData.machines]
|
||||||
|
|
||||||
let charts = []
|
let charts = []
|
||||||
let resizeCount = 0
|
let resizeCount = 0
|
||||||
|
|
||||||
@ -29,11 +39,6 @@ String.prototype.toHHMMSS = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
let titleOptions = {
|
|
||||||
display: true,
|
|
||||||
fontSize: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTooltipOptions(key, type) {
|
function getTooltipOptions(key, type) {
|
||||||
return {
|
return {
|
||||||
mode: 'single',
|
mode: 'single',
|
||||||
@ -49,131 +54,158 @@ function draw() {
|
|||||||
|
|
||||||
charts.forEach(c => c.destroy())
|
charts.forEach(c => c.destroy())
|
||||||
|
|
||||||
let projectChart = new Chart(projectsCanvas.getContext('2d'), {
|
let projectChart = !projectsCanvas.classList.contains('hidden')
|
||||||
type: 'horizontalBar',
|
? new Chart(projectsCanvas.getContext('2d'), {
|
||||||
data: {
|
type: 'horizontalBar',
|
||||||
datasets: wakapiData.projects
|
data: {
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.projects.length))
|
datasets: wakapiData.projects
|
||||||
.map(p => {
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.projects.length))
|
||||||
return {
|
.map(p => {
|
||||||
label: p.key,
|
return {
|
||||||
data: [parseInt(p.total) / 60],
|
label: p.key,
|
||||||
backgroundColor: getRandomColor(p.key)
|
data: [parseInt(p.total) / 60],
|
||||||
}
|
backgroundColor: getRandomColor(p.key)
|
||||||
})
|
}
|
||||||
},
|
})
|
||||||
options: {
|
|
||||||
title: Object.assign(titleOptions, {text: `Projects (top ${SHOW_TOP_N})`}),
|
|
||||||
tooltips: getTooltipOptions('projects', 'bar'),
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
},
|
},
|
||||||
scales: {
|
options: {
|
||||||
xAxes: [{
|
tooltips: getTooltipOptions('projects', 'bar'),
|
||||||
scaleLabel: {
|
legend: {
|
||||||
display: true,
|
display: false
|
||||||
labelString: 'Minutes'
|
},
|
||||||
}
|
scales: {
|
||||||
}]
|
xAxes: [{
|
||||||
},
|
scaleLabel: {
|
||||||
maintainAspectRatio: false,
|
display: true,
|
||||||
onResize: onChartResize
|
labelString: 'Minutes'
|
||||||
}
|
}
|
||||||
})
|
}]
|
||||||
|
},
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
onResize: onChartResize
|
||||||
|
}
|
||||||
|
})
|
||||||
|
: null
|
||||||
|
|
||||||
let osChart = new Chart(osCanvas.getContext('2d'), {
|
let osChart = !osCanvas.classList.contains('hidden')
|
||||||
type: 'pie',
|
? new Chart(osCanvas.getContext('2d'), {
|
||||||
data: {
|
type: 'pie',
|
||||||
datasets: [{
|
data: {
|
||||||
data: wakapiData.operatingSystems
|
datasets: [{
|
||||||
|
data: wakapiData.operatingSystems
|
||||||
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.operatingSystems.length))
|
||||||
|
.map(p => parseInt(p.total)),
|
||||||
|
backgroundColor: wakapiData.operatingSystems.map(p => getRandomColor(p.key))
|
||||||
|
}],
|
||||||
|
labels: wakapiData.operatingSystems
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.operatingSystems.length))
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.operatingSystems.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => p.key)
|
||||||
backgroundColor: wakapiData.operatingSystems.map(p => getRandomColor(p.key))
|
},
|
||||||
}],
|
options: {
|
||||||
labels: wakapiData.operatingSystems
|
tooltips: getTooltipOptions('operatingSystems', 'pie'),
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.operatingSystems.length))
|
maintainAspectRatio: false,
|
||||||
.map(p => p.key)
|
onResize: onChartResize
|
||||||
},
|
}
|
||||||
options: {
|
})
|
||||||
title: Object.assign(titleOptions, {text: `Operating Systems (top ${SHOW_TOP_N})`}),
|
: null
|
||||||
tooltips: getTooltipOptions('operatingSystems', 'pie'),
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
onResize: onChartResize
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let editorChart = new Chart(editorsCanvas.getContext('2d'), {
|
let editorChart = !editorsCanvas.classList.contains('hidden')
|
||||||
type: 'pie',
|
? new Chart(editorsCanvas.getContext('2d'), {
|
||||||
data: {
|
type: 'pie',
|
||||||
datasets: [{
|
data: {
|
||||||
data: wakapiData.editors
|
datasets: [{
|
||||||
|
data: wakapiData.editors
|
||||||
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.editors.length))
|
||||||
|
.map(p => parseInt(p.total)),
|
||||||
|
backgroundColor: wakapiData.editors.map(p => getRandomColor(p.key))
|
||||||
|
}],
|
||||||
|
labels: wakapiData.editors
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.editors.length))
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.editors.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => p.key)
|
||||||
backgroundColor: wakapiData.editors.map(p => getRandomColor(p.key))
|
},
|
||||||
}],
|
options: {
|
||||||
labels: wakapiData.editors
|
tooltips: getTooltipOptions('editors', 'pie'),
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.editors.length))
|
maintainAspectRatio: false,
|
||||||
.map(p => p.key)
|
onResize: onChartResize
|
||||||
},
|
}
|
||||||
options: {
|
})
|
||||||
title: Object.assign(titleOptions, {text: `Editors (top ${SHOW_TOP_N})`}),
|
: null
|
||||||
tooltips: getTooltipOptions('editors', 'pie'),
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
onResize: onChartResize
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let languageChart = new Chart(languagesCanvas.getContext('2d'), {
|
let languageChart = !languagesCanvas.classList.contains('hidden')
|
||||||
type: 'pie',
|
? new Chart(languagesCanvas.getContext('2d'), {
|
||||||
data: {
|
type: 'pie',
|
||||||
datasets: [{
|
data: {
|
||||||
data: wakapiData.languages
|
datasets: [{
|
||||||
|
data: wakapiData.languages
|
||||||
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.languages.length))
|
||||||
|
.map(p => parseInt(p.total)),
|
||||||
|
backgroundColor: wakapiData.languages.map(p => languageColors[p.key.toLowerCase()] || getRandomColor(p.key))
|
||||||
|
}],
|
||||||
|
labels: wakapiData.languages
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.languages.length))
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.languages.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => p.key)
|
||||||
backgroundColor: wakapiData.languages.map(p => languageColors[p.key.toLowerCase()] || getRandomColor(p.key))
|
},
|
||||||
}],
|
options: {
|
||||||
labels: wakapiData.languages
|
tooltips: getTooltipOptions('languages', 'pie'),
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.languages.length))
|
maintainAspectRatio: false,
|
||||||
.map(p => p.key)
|
onResize: onChartResize
|
||||||
},
|
}
|
||||||
options: {
|
})
|
||||||
title: Object.assign(titleOptions, {text: `Languages (top ${SHOW_TOP_N})`}),
|
: null
|
||||||
tooltips: getTooltipOptions('languages', 'pie'),
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
onResize: onChartResize
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let machineChart = new Chart(machinesCanvas.getContext('2d'), {
|
let machineChart = !machinesCanvas.classList.contains('hidden')
|
||||||
type: 'pie',
|
? new Chart(machinesCanvas.getContext('2d'), {
|
||||||
data: {
|
type: 'pie',
|
||||||
datasets: [{
|
data: {
|
||||||
data: wakapiData.machines
|
datasets: [{
|
||||||
|
data: wakapiData.machines
|
||||||
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.machines.length))
|
||||||
|
.map(p => parseInt(p.total)),
|
||||||
|
backgroundColor: wakapiData.machines.map(p => getRandomColor(p.key))
|
||||||
|
}],
|
||||||
|
labels: wakapiData.machines
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.machines.length))
|
.slice(0, Math.min(SHOW_TOP_N, wakapiData.machines.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => p.key)
|
||||||
backgroundColor: wakapiData.machines.map(p => getRandomColor(p.key))
|
},
|
||||||
}],
|
options: {
|
||||||
labels: wakapiData.machines
|
tooltips: getTooltipOptions('machines', 'pie'),
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.machines.length))
|
maintainAspectRatio: false,
|
||||||
.map(p => p.key)
|
onResize: onChartResize
|
||||||
},
|
}
|
||||||
options: {
|
})
|
||||||
title: Object.assign(titleOptions, {text: `Machines (top ${SHOW_TOP_N})`}),
|
: null
|
||||||
tooltips: getTooltipOptions('machines', 'pie'),
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
onResize: onChartResize
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
getTotal(wakapiData.operatingSystems)
|
getTotal(wakapiData.operatingSystems)
|
||||||
document.getElementById('grid-container').style.visibility = 'visible'
|
|
||||||
|
|
||||||
charts = [projectChart, osChart, editorChart, languageChart, machineChart]
|
charts = [projectChart, osChart, editorChart, languageChart, machineChart].filter(c => !!c)
|
||||||
|
|
||||||
charts.forEach(c => c.options.onResize(c.chart))
|
charts.forEach(c => c.options.onResize(c.chart))
|
||||||
equalizeHeights()
|
equalizeHeights()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setTopLabels() {
|
||||||
|
[...document.getElementsByClassName('top-label')]
|
||||||
|
.forEach(e => e.innerText = `(top ${SHOW_TOP_N})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePlaceholders(mask) {
|
||||||
|
const placeholderElements = containers.map(c => c.querySelector('.placeholder-container'))
|
||||||
|
|
||||||
|
for (let i = 0; i < mask.length; i++) {
|
||||||
|
if (!mask[i]) {
|
||||||
|
canvases[i].classList.add('hidden')
|
||||||
|
placeholderElements[i].classList.remove('hidden')
|
||||||
|
} else {
|
||||||
|
canvases[i].classList.remove('hidden')
|
||||||
|
placeholderElements[i].classList.add('hidden')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPresentDataMask() {
|
||||||
|
return data.map(list => list.reduce((acc, e) => acc + e.total, 0) > 0)
|
||||||
|
}
|
||||||
|
|
||||||
function getContainer(chart) {
|
function getContainer(chart) {
|
||||||
return chart.canvas.parentNode
|
return chart.canvas.parentNode
|
||||||
}
|
}
|
||||||
@ -257,7 +289,7 @@ if (favicon) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Click outside
|
// Click outside
|
||||||
window.addEventListener('click', function(event) {
|
window.addEventListener('click', function (event) {
|
||||||
if (event.target.classList.contains('popup')) {
|
if (event.target.classList.contains('popup')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -268,5 +300,7 @@ window.addEventListener('click', function(event) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
|
setTopLabels()
|
||||||
|
togglePlaceholders(getPresentDataMask())
|
||||||
draw()
|
draw()
|
||||||
})
|
})
|
1
static/assets/images/no_data.svg
Normal file
1
static/assets/images/no_data.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.9 KiB |
@ -1 +1 @@
|
|||||||
1.8.3
|
1.8.4
|
@ -59,28 +59,68 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 bg-white rounded shadow m-2" id="projects-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="project-container" style="height: 300px">
|
||||||
|
<div class="self-center flex">
|
||||||
|
<span class="font-semibold mr-1">Projects</span>
|
||||||
|
<span id="project-top-label" class="top-label"></span>
|
||||||
|
</div>
|
||||||
<canvas id="chart-projects"></canvas>
|
<canvas id="chart-projects"></canvas>
|
||||||
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
|
<img src="assets/images/no_data.svg" class="w-20"/>
|
||||||
|
<span class="text-sm mt-4">No data available ...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 bg-white rounded shadow m-2" id="os-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="os-container" style="height: 300px">
|
||||||
|
<div class="self-center flex">
|
||||||
|
<span class="font-semibold mr-1">Operating Systems</span>
|
||||||
|
<span id="os-top-label" class="top-label"></span>
|
||||||
|
</div>
|
||||||
<canvas id="chart-os"></canvas>
|
<canvas id="chart-os"></canvas>
|
||||||
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
|
<img src="assets/images/no_data.svg" class="w-20"/>
|
||||||
|
<span class="text-sm mt-4">No data available ...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 bg-white rounded shadow m-2 relative" id="language-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col relative" id="language-container" style="height: 300px">
|
||||||
|
<div class="self-center flex">
|
||||||
|
<span class="font-semibold mr-1">Languages</span>
|
||||||
|
<span id="language-top-label" class="top-label"></span>
|
||||||
|
</div>
|
||||||
<canvas id="chart-language"></canvas>
|
<canvas id="chart-language"></canvas>
|
||||||
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
|
<img src="assets/images/no_data.svg" class="w-20"/>
|
||||||
|
<span class="text-sm mt-4">No data available ...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 bg-white rounded shadow m-2" id="editor-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="editor-container" style="height: 300px">
|
||||||
|
<div class="self-center flex">
|
||||||
|
<span class="font-semibold mr-1">Editors</span>
|
||||||
|
<span id="editor-top-label" class="top-label"></span>
|
||||||
|
</div>
|
||||||
<canvas id="chart-editor"></canvas>
|
<canvas id="chart-editor"></canvas>
|
||||||
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
|
<img src="assets/images/no_data.svg" class="w-20"/>
|
||||||
|
<span class="text-sm mt-4">No data available ...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 bg-white rounded shadow m-2" id="machine-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="machine-container" style="height: 300px">
|
||||||
|
<div class="self-center flex">
|
||||||
|
<span class="font-semibold mr-1">Machines</span>
|
||||||
|
<span id="machine-top-label" class="top-label"></span>
|
||||||
|
</div>
|
||||||
<canvas id="chart-machine"></canvas>
|
<canvas id="chart-machine"></canvas>
|
||||||
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
|
<img src="assets/images/no_data.svg" class="w-20"/>
|
||||||
|
<span class="text-sm mt-4">No data available ...</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user