199 lines
7.1 KiB
Vue
199 lines
7.1 KiB
Vue
<template>
|
|
<div>
|
|
<div class="row">
|
|
<div class="col-xs-10">
|
|
<strong>
|
|
{{metric.name}}
|
|
|
|
<i class="ion ion-ios-help-outline" data-toggle="tooltip" :data-title="metric.description" v-if="metric.description"></i>
|
|
</strong>
|
|
</div>
|
|
<div class="col-xs-2">
|
|
<div class="dropdown pull-right">
|
|
<a href='javascript: void(0)' class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span class='filter'>{{view.title || metric.default_view_name}}</span> <span class="caret"></span></a>
|
|
|
|
<ul class="dropdown-menu dropdown-menu-right">
|
|
<!-- TODO: Make these dynamic translations -->
|
|
<li><a @click="changeView('last_hour', 'Last Hour')">Last Hour</a></li>
|
|
<li><a @click="changeView('today', 'Last 12 Hours')">Last 12 Hours</a></li>
|
|
<li><a @click="changeView('week', 'Week')">Week</a></li>
|
|
<li><a @click="changeView('month', 'Month')">Month</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-xs-12">
|
|
<canvas :id="metricId" height="200" width="900"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
const Chart = require('chart.js')
|
|
|
|
export default {
|
|
props: {
|
|
metric: Object,
|
|
theme: String,
|
|
themeLight: String,
|
|
themeDark: String,
|
|
},
|
|
data () {
|
|
return {
|
|
canvas: null,
|
|
context: null,
|
|
chart: null,
|
|
data: null,
|
|
view: {
|
|
param: null,
|
|
title: null,
|
|
},
|
|
loading: false,
|
|
}
|
|
},
|
|
mounted () {
|
|
this.canvas = document.getElementById(this.metricId)
|
|
this.context = this.canvas.getContext('2d')
|
|
|
|
this.getData()
|
|
|
|
$('.dropdown-toggle').dropdown()
|
|
},
|
|
computed: {
|
|
metricId () {
|
|
return `metric-${this.metric.id}`
|
|
}
|
|
},
|
|
watch: {
|
|
loading (val) {
|
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
|
|
|
this.context.fillStyle = "#666"
|
|
this.context.font = '20px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'
|
|
|
|
const textString = "Loading metrics..."
|
|
const textWidth = this.context.measureText(textString).width
|
|
|
|
this.canvas.textBaseline = 'middle'
|
|
this.canvas.textAlign = "center"
|
|
|
|
this.context.fillText(textString, (this.canvas.width / 2) - (textWidth / 2), 100)
|
|
}
|
|
},
|
|
methods: {
|
|
getData () {
|
|
this.loading = true
|
|
|
|
return axios.get('/metrics/'+this.metric.id, {
|
|
params: {
|
|
filter: this.view.param
|
|
}
|
|
}).then(response => {
|
|
this.data = response.data.data.items
|
|
|
|
this.loading = false
|
|
|
|
this.updateChart()
|
|
})
|
|
},
|
|
changeView (param, title) {
|
|
// Don't reload the same view.
|
|
if (this.view.param === param) return
|
|
|
|
this.view = {
|
|
param: param,
|
|
title: title
|
|
}
|
|
|
|
this.getData().then(this.updateChart)
|
|
},
|
|
updateChart () {
|
|
if (this.chart !== null) {
|
|
this.chart.destroy()
|
|
}
|
|
//Used in tooltip callback where this.metric is not the same.
|
|
var metric = this.metric;
|
|
/*
|
|
* Datetimes are used as keys instead of just time in order to
|
|
* improve ordering of labels in "Last 12 hours", so we cut the
|
|
* labels.
|
|
* This cutting is done only if there is an hour in the string, so
|
|
* if the view by day is set it doesn't fail.
|
|
*/
|
|
var data_keys = _.keys(this.data);
|
|
if (0 < data_keys.length && data_keys[0].length > 10) {
|
|
for (var i = 0; i < data_keys.length; i++) {
|
|
data_keys[i] = data_keys[i].substr(11);
|
|
}
|
|
}
|
|
|
|
this.chart = new Chart(this.context, {
|
|
type: 'line',
|
|
data: {
|
|
labels: data_keys,
|
|
datasets: [{
|
|
data: _.values(this.data),
|
|
backgroundColor: this.themeLight,
|
|
borderColor: this.theme,
|
|
pointBackgroundColor: this.theme,
|
|
pointBorderColor: this.theme,
|
|
pointHoverBackgroundColor: this.themeDark,
|
|
pointHoverBorderColor: this.themeDark
|
|
}]
|
|
},
|
|
options: {
|
|
elements: {
|
|
point: {
|
|
hitRadius: 5
|
|
}
|
|
},
|
|
legend: {
|
|
display: false
|
|
},
|
|
scales: {
|
|
yAxes: [{
|
|
ticks: {
|
|
beginAtZero: true,
|
|
suggestedMax: 0.1,
|
|
// fixedStepSize: result.data.metric.places,
|
|
callback: function(tickValue, index, ticks) {
|
|
let delta = ticks[1] - ticks[0]
|
|
|
|
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
|
|
if (Math.abs(delta) > 1) {
|
|
if (tickValue !== Math.floor(tickValue)) {
|
|
delta = tickValue - Math.floor(tickValue)
|
|
}
|
|
}
|
|
|
|
const logDelta = Chart.helpers.log10(Math.abs(delta))
|
|
let tickString = ''
|
|
|
|
if (tickValue !== 0) {
|
|
let numDecimal = -1 * Math.floor(logDelta)
|
|
numDecimal = Math.max(Math.min(numDecimal, 2), 0) // Use as many places as the metric defines
|
|
tickString = tickValue.toFixed(numDecimal)
|
|
} else {
|
|
tickString = '0' // Never show decimal places for 0
|
|
}
|
|
|
|
return tickString
|
|
}
|
|
}
|
|
}]
|
|
},
|
|
tooltips: {
|
|
callbacks: {
|
|
label: function(tooltipItem, data) {
|
|
return tooltipItem.yLabel + ' ' + metric.suffix;
|
|
}
|
|
}
|
|
}
|
|
}})
|
|
}
|
|
}
|
|
}
|
|
</script>
|