Fix metrics not displaying. Closes #2737
This commit is contained in:
@@ -13,9 +13,10 @@ namespace CachetHQ\Cachet\Presenters;
|
|||||||
|
|
||||||
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
|
use CachetHQ\Cachet\Presenters\Traits\TimestampsTrait;
|
||||||
use Illuminate\Contracts\Support\Arrayable;
|
use Illuminate\Contracts\Support\Arrayable;
|
||||||
|
use Illuminate\Contracts\Support\Jsonable;
|
||||||
use McCool\LaravelAutoPresenter\BasePresenter;
|
use McCool\LaravelAutoPresenter\BasePresenter;
|
||||||
|
|
||||||
class MetricPresenter extends BasePresenter implements Arrayable
|
class MetricPresenter extends BasePresenter implements Arrayable, Jsonable
|
||||||
{
|
{
|
||||||
use TimestampsTrait;
|
use TimestampsTrait;
|
||||||
|
|
||||||
@@ -72,4 +73,17 @@ class MetricPresenter extends BasePresenter implements Arrayable
|
|||||||
'default_view_name' => $this->default_view_name(),
|
'default_view_name' => $this->default_view_name(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the object to its JSON representation.
|
||||||
|
*
|
||||||
|
* @param int $options
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function toJson($options = 0)
|
||||||
|
{
|
||||||
|
$json = json_encode($this->toArray(), $options);
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,11 @@ window.axios.defaults.headers.common = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
'setup': require('./components/Setup.js'),
|
'setup': require('./components/Setup'),
|
||||||
'dashboard': require('./components/dashboard/Dashboard.js'),
|
'dashboard': require('./components/dashboard/Dashboard'),
|
||||||
'report-incident': require('./components/dashboard/ReportIncident.js'),
|
'report-incident': require('./components/dashboard/ReportIncident'),
|
||||||
'invite-team': require('./components/dashboard/InviteTeam.js'),
|
'invite-team': require('./components/dashboard/InviteTeam'),
|
||||||
|
'metric-chart': require('./components/status-page/Metric'),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<script>
|
||||||
module.exports = {
|
module.exports = {
|
||||||
props: [],
|
props: [],
|
||||||
data () {
|
data () {
|
||||||
@@ -47,3 +48,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<script>
|
||||||
module.exports = {
|
module.exports = {
|
||||||
props: ['welcome-user'],
|
props: ['welcome-user'],
|
||||||
mounted () {
|
mounted () {
|
||||||
@@ -14,3 +15,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<script>
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -27,3 +28,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<script>
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -41,3 +42,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
179
resources/assets/js/components/status-page/Metric.vue
Normal file
179
resources/assets/js/components/status-page/Metric.vue
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<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="160" width="600"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const Chart = require('chart.js')
|
||||||
|
const _ = require('lodash')
|
||||||
|
|
||||||
|
// Configure Chart.js
|
||||||
|
Chart.defaults.global.elements.point.hitRadius = 10
|
||||||
|
Chart.defaults.global.responsiveAnimationDuration = 1000
|
||||||
|
Chart.defaults.global.legend.display = false
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
props: [
|
||||||
|
'metric',
|
||||||
|
],
|
||||||
|
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 = '44px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"'
|
||||||
|
|
||||||
|
const textString = "Loading data",
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.chart = new Chart(this.context, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: _.keys(this.data),
|
||||||
|
datasets: [{
|
||||||
|
data: _.values(this.data),
|
||||||
|
// backgroundColor: "{{ $theme_metrics }}",
|
||||||
|
// borderColor: "{{ color_darken($theme_metrics, -0.1) }}",
|
||||||
|
// pointBackgroundColor: "{{ color_darken($theme_metrics, -0.1) }}",
|
||||||
|
// pointBorderColor: "{{ color_darken($theme_metrics, -0.1) }}",
|
||||||
|
// pointHoverBackgroundColor: "{{ color_darken($theme_metrics, -0.2) }}",
|
||||||
|
// pointHoverBorderColor: "{{ color_darken($theme_metrics, -0.2) }}"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
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 + ' ' + result.data.metric.suffix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -4,140 +4,10 @@
|
|||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
@foreach($metrics as $metric)
|
@foreach($metrics as $metric)
|
||||||
<li class="list-group-item metric" data-metric-id="{{ $metric->id }}">
|
<li class="list-group-item metric" data-metric-id="{{ $metric->id }}">
|
||||||
<div class="row">
|
<metric-chart :metric="{{ $metric->toJson() }}"></metric-chart>
|
||||||
<div class="col-xs-10">
|
|
||||||
<strong>
|
|
||||||
{{ $metric->name }}
|
|
||||||
@if($metric->description)
|
|
||||||
<i class="ion ion-ios-help-outline" data-toggle="tooltip" data-title="{{ $metric->description }}"></i>
|
|
||||||
@endif
|
|
||||||
</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'>{{ trans('cachet.metrics.filter.'.$metric->trans_string_name) }}</span> <span class="caret"></span></a>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-right">
|
|
||||||
<li><a href="#" data-filter-type="last_hour">{{ trans('cachet.metrics.filter.last_hour') }}</a></li>
|
|
||||||
<li><a href="#" data-filter-type="today">{{ trans('cachet.metrics.filter.hourly') }}</a></li>
|
|
||||||
<li><a href="#" data-filter-type="week">{{ trans('cachet.metrics.filter.weekly') }}</a></li>
|
|
||||||
<li><a href="#" data-filter-type="month">{{ trans('cachet.metrics.filter.monthly') }}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<canvas id="metric-{{ $metric->id }}" data-metric-name="{{ $metric->name }}" data-metric-suffix="{{ $metric->suffix }}" data-metric-id="{{ $metric->id }}" data-metric-group="{{ $metric->view_name }}" data-metric-precision="{{ $metric->places }}" height="160" width="600"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
@endforeach
|
@endforeach
|
||||||
</ul>
|
</ul>
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
Chart.defaults.global.elements.point.hitRadius = 10;
|
|
||||||
Chart.defaults.global.responsiveAnimationDuration = 1000;
|
|
||||||
Chart.defaults.global.legend.display = false;
|
|
||||||
|
|
||||||
var charts = {};
|
|
||||||
|
|
||||||
$('a[data-filter-type]').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
var $this = $(this), $li, $canvas;
|
|
||||||
|
|
||||||
$li = $this.parents('li');
|
|
||||||
$li.find('a[data-toggle=dropdown] span.filter').text($this.text());
|
|
||||||
$canvas = $li.find('canvas');
|
|
||||||
|
|
||||||
$canvas.data('metric-group', $this.data('filter-type'));
|
|
||||||
drawChart($canvas);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('canvas[data-metric-id]').each(function() {
|
|
||||||
drawChart($(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
function drawChart($el) {
|
|
||||||
var metricId = $el.data('metric-id');
|
|
||||||
var metricGroup = $el.data('metric-group');
|
|
||||||
|
|
||||||
if (typeof charts[metricId] === 'undefined') {
|
|
||||||
charts[metricId] = {
|
|
||||||
context: document.getElementById("metric-"+metricId).getContext("2d"),
|
|
||||||
chart: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var chart = charts[metricId];
|
|
||||||
|
|
||||||
$.getJSON('/metrics/'+metricId, { filter: metricGroup }).done(function (result) {
|
|
||||||
var data = result.data.items;
|
|
||||||
|
|
||||||
if (chart.chart !== null) {
|
|
||||||
chart.chart.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
chart.chart = new Chart(chart.context, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
labels: _.keys(data),
|
|
||||||
datasets: [{
|
|
||||||
data: _.values(data),
|
|
||||||
backgroundColor: "{{ $theme_metrics }}",
|
|
||||||
borderColor: "{{ color_darken($theme_metrics, -0.1) }}",
|
|
||||||
pointBackgroundColor: "{{ color_darken($theme_metrics, -0.1) }}",
|
|
||||||
pointBorderColor: "{{ color_darken($theme_metrics, -0.1) }}",
|
|
||||||
pointHoverBackgroundColor: "{{ color_darken($theme_metrics, -0.2) }}",
|
|
||||||
pointHoverBorderColor: "{{ color_darken($theme_metrics, -0.2) }}"
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
scales: {
|
|
||||||
yAxes: [{
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true,
|
|
||||||
suggestedMax: 0.1,
|
|
||||||
// fixedStepSize: result.data.metric.places,
|
|
||||||
callback: function(tickValue, index, ticks) {
|
|
||||||
var 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var logDelta = Chart.helpers.log10(Math.abs(delta));
|
|
||||||
var tickString = '';
|
|
||||||
|
|
||||||
if (tickValue !== 0) {
|
|
||||||
var numDecimal = -1 * Math.floor(logDelta);
|
|
||||||
numDecimal = Math.max(Math.min(numDecimal, {{ $metric->places }}), 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 + ' ' + result.data.metric.suffix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
</script>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ mix
|
|||||||
.options({
|
.options({
|
||||||
processCssUrls: false
|
processCssUrls: false
|
||||||
})
|
})
|
||||||
.js('resources/assets/js/app.js', 'public/dist/js').extract(['vue'])
|
.js('resources/assets/js/app.js', 'public/dist/js').extract(['vue', 'chart.js'])
|
||||||
.scripts([
|
.scripts([
|
||||||
'public/dist/js/app.js',
|
'public/dist/js/app.js',
|
||||||
'node_modules/es5-shim/es5-shim.js',
|
'node_modules/es5-shim/es5-shim.js',
|
||||||
@@ -40,7 +40,6 @@ mix
|
|||||||
'node_modules/messenger/build/js/messenger.min.js',
|
'node_modules/messenger/build/js/messenger.min.js',
|
||||||
'node_modules/sortablejs/Sortable.min.js',
|
'node_modules/sortablejs/Sortable.min.js',
|
||||||
'node_modules/jquery-minicolors/jquery.minicolors.min.js',
|
'node_modules/jquery-minicolors/jquery.minicolors.min.js',
|
||||||
'node_modules/chart.js/dist/Chart.min.js',
|
|
||||||
'node_modules/jquery-sparkline/jquery.sparkline.min.js',
|
'node_modules/jquery-sparkline/jquery.sparkline.min.js',
|
||||||
'node_modules/sweetalert2/dist/sweetalert2.min.js',
|
'node_modules/sweetalert2/dist/sweetalert2.min.js',
|
||||||
'node_modules/livestamp/livestamp.js',
|
'node_modules/livestamp/livestamp.js',
|
||||||
|
|||||||
Reference in New Issue
Block a user