2021-08-10 07:34:47 -04:00
< template >
2021-10-18 07:00:39 -04:00
< div >
< div class = "period-options" >
2021-10-23 13:26:56 -04:00
< button type = "button" class = "btn btn-light dropdown-toggle btn-period-toggle" data -bs -toggle = " dropdown " aria -expanded = " false " >
{ { chartPeriodOptions [ chartPeriodHrs ] } } & nbsp ;
< / button >
< ul class = "dropdown-menu dropdown-menu-end" >
< li v-for ="(item, key) in chartPeriodOptions" :key ="key" >
< a class = "dropdown-item" : class = "{ active: chartPeriodHrs == key }" href = "#" @ click = "chartPeriodHrs = key" > { { item } } < / a >
< / li >
< / ul >
2021-10-18 07:00:39 -04:00
< / div >
2021-10-26 00:48:21 -04:00
< div class = "chart-wrapper" : class = "{ loading : loading}" >
2023-03-01 15:47:51 -05:00
< Line :data ="chartData" :options ="chartOptions" / >
2021-10-18 07:00:39 -04:00
< / div >
< / div >
2021-08-10 07:34:47 -04:00
< / template >
2022-09-27 12:20:17 -04:00
< script lang = "js" >
2021-08-11 12:31:21 -04:00
import { BarController , BarElement , Chart , Filler , LinearScale , LineController , LineElement , PointElement , TimeScale , Tooltip } from "chart.js" ;
2023-03-01 15:47:51 -05:00
import "chartjs-adapter-dayjs-4" ;
2021-08-10 07:34:47 -04:00
import dayjs from "dayjs" ;
2023-03-01 15:47:51 -05:00
import { Line } from "vue-chartjs" ;
2021-10-18 07:00:39 -04:00
import { useToast } from "vue-toastification" ;
2022-05-01 06:40:34 -04:00
import { DOWN , PENDING , MAINTENANCE , log } from "../util.ts" ;
2021-10-18 07:00:39 -04:00
const toast = useToast ( ) ;
2021-08-10 07:34:47 -04:00
2021-08-11 12:31:21 -04:00
Chart . register ( LineController , BarController , LineElement , PointElement , TimeScale , BarElement , LinearScale , Tooltip , Filler ) ;
2021-08-10 07:34:47 -04:00
export default {
2023-03-01 15:47:51 -05:00
components : { Line } ,
2021-08-10 07:34:47 -04:00
props : {
2022-06-01 19:32:05 -04:00
/** ID of monitor */
2021-08-10 07:34:47 -04:00
monitorId : {
type : Number ,
required : true ,
} ,
} ,
data ( ) {
return {
2021-10-26 00:48:21 -04:00
loading : false ,
2021-08-24 12:54:52 -04:00
// Configurable filtering on top of the returned data
2021-10-18 07:00:39 -04:00
chartPeriodHrs : 0 ,
2021-10-23 13:26:56 -04:00
chartPeriodOptions : {
0 : this . $t ( "recent" ) ,
3 : "3h" ,
6 : "6h" ,
24 : "24h" ,
168 : "1w" ,
} ,
2021-10-18 07:00:39 -04:00
// A heartbeatList for 3h, 6h, 24h, 1w
2021-10-22 06:44:11 -04:00
// Uses the $root.heartbeatList when value is null
2021-10-18 07:00:39 -04:00
heartbeatList : null
2021-08-10 07:34:47 -04:00
} ;
} ,
computed : {
chartOptions ( ) {
return {
responsive : true ,
2021-08-18 00:21:16 -04:00
maintainAspectRatio : false ,
onResize : ( chart ) => {
chart . canvas . parentNode . style . position = "relative" ;
if ( screen . width < 576 ) {
chart . canvas . parentNode . style . height = "275px" ;
} else if ( screen . width < 768 ) {
chart . canvas . parentNode . style . height = "320px" ;
} else if ( screen . width < 992 ) {
chart . canvas . parentNode . style . height = "300px" ;
} else {
chart . canvas . parentNode . style . height = "250px" ;
}
} ,
2021-08-10 07:34:47 -04:00
layout : {
padding : {
left : 10 ,
right : 30 ,
top : 30 ,
bottom : 10 ,
} ,
} ,
2021-08-11 09:00:33 -04:00
elements : {
point : {
2021-08-24 12:54:52 -04:00
// Hide points on chart unless mouse-over
2021-08-11 09:00:33 -04:00
radius : 0 ,
2021-08-24 12:54:52 -04:00
hitRadius : 100 ,
2021-08-11 09:00:33 -04:00
} ,
} ,
2021-08-10 07:34:47 -04:00
scales : {
x : {
type : "time" ,
time : {
2021-08-16 11:58:02 -04:00
minUnit : "minute" ,
round : "second" ,
tooltipFormat : "YYYY-MM-DD HH:mm:ss" ,
displayFormats : {
minute : "HH:mm" ,
hour : "MM-DD HH:mm" ,
}
2021-08-11 09:00:33 -04:00
} ,
ticks : {
2023-03-01 15:47:51 -05:00
sampleSize : 3 ,
2021-08-11 09:00:33 -04:00
maxRotation : 0 ,
2021-08-16 11:58:02 -04:00
autoSkipPadding : 30 ,
2023-03-01 15:47:51 -05:00
padding : 3 ,
2021-08-11 09:00:33 -04:00
} ,
grid : {
color : this . $root . theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)" ,
2021-08-24 11:34:48 -04:00
offset : false ,
2021-08-10 07:34:47 -04:00
} ,
} ,
y : {
title : {
display : true ,
2021-09-01 15:17:50 -04:00
text : this . $t ( "respTime" ) ,
2021-08-10 07:34:47 -04:00
} ,
2021-08-11 09:00:33 -04:00
offset : false ,
grid : {
color : this . $root . theme === "light" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.1)" ,
} ,
} ,
y1 : {
display : false ,
position : "right" ,
grid : {
drawOnChartArea : false ,
} ,
min : 0 ,
max : 1 ,
offset : false ,
} ,
2021-08-10 07:34:47 -04:00
} ,
bounds : "ticks" ,
plugins : {
2021-08-11 09:00:33 -04:00
tooltip : {
2021-08-11 12:31:21 -04:00
mode : "nearest" ,
intersect : false ,
padding : 10 ,
2021-08-24 12:54:52 -04:00
backgroundColor : this . $root . theme === "light" ? "rgba(212,232,222,1.0)" : "rgba(32,42,38,1.0)" ,
bodyColor : this . $root . theme === "light" ? "rgba(12,12,18,1.0)" : "rgba(220,220,220,1.0)" ,
titleColor : this . $root . theme === "light" ? "rgba(12,12,18,1.0)" : "rgba(220,220,220,1.0)" ,
2021-08-11 09:00:33 -04:00
filter : function ( tooltipItem ) {
2021-08-24 12:54:52 -04:00
return tooltipItem . datasetIndex === 0 ; // Hide tooltip on Bar Chart
2021-08-11 09:00:33 -04:00
} ,
2021-08-11 12:31:21 -04:00
callbacks : {
label : ( context ) => {
2021-10-18 07:00:39 -04:00
return ` ${ new Intl . NumberFormat ( ) . format ( context . parsed . y ) } ms ` ;
2021-08-11 12:31:21 -04:00
} ,
}
2021-08-11 09:00:33 -04:00
} ,
2021-08-10 07:34:47 -04:00
legend : {
display : false ,
} ,
} ,
2021-10-18 07:00:39 -04:00
} ;
2021-08-10 07:34:47 -04:00
} ,
chartData ( ) {
2021-08-24 12:54:52 -04:00
let pingData = [ ] ; // Ping Data for Line Chart, y-axis contains ping time
2022-01-23 09:22:00 -05:00
let downData = [ ] ; // Down Data for Bar Chart, y-axis is 1 if target is down (red color), under maintenance (blue color) or pending (orange color), 0 if target is up
let colorData = [ ] ; // Color Data for Bar Chart
2021-10-22 06:38:41 -04:00
let heartbeatList = this . heartbeatList ||
( this . monitorId in this . $root . heartbeatList && this . $root . heartbeatList [ this . monitorId ] ) ||
[ ] ;
heartbeatList
. filter (
// Filtering as data gets appended
// not the most efficient, but works for now
2021-10-22 06:44:11 -04:00
( beat ) => dayjs . utc ( beat . time ) . tz ( this . $root . timezone ) . isAfter (
dayjs ( ) . subtract ( Math . max ( this . chartPeriodHrs , 6 ) , "hours" )
)
)
2021-10-22 06:38:41 -04:00
. map ( ( beat ) => {
const x = this . $root . datetime ( beat . time ) ;
pingData . push ( {
x ,
y : beat . ping ,
2021-08-11 09:00:33 -04:00
} ) ;
2021-10-22 06:38:41 -04:00
downData . push ( {
x ,
2022-01-23 09:22:00 -05:00
y : ( beat . status === DOWN || beat . status === MAINTENANCE || beat . status === PENDING ) ? 1 : 0 ,
2021-10-22 06:38:41 -04:00
} ) ;
2022-04-30 08:33:54 -04:00
colorData . push ( ( beat . status === MAINTENANCE ) ? "rgba(23,71,245,0.41)" : ( ( beat . status === PENDING ) ? "rgba(245,182,23,0.41)" : "#DC354568" ) ) ;
2021-10-22 06:38:41 -04:00
} ) ;
2021-08-10 07:34:47 -04:00
return {
datasets : [
{
2021-08-24 12:54:52 -04:00
// Line Chart
data : pingData ,
2021-08-10 07:34:47 -04:00
fill : "origin" ,
tension : 0.2 ,
borderColor : "#5CDD8B" ,
backgroundColor : "#5CDD8B38" ,
2021-08-11 09:00:33 -04:00
yAxisID : "y" ,
2023-03-01 15:47:51 -05:00
label : "ping" ,
2021-08-11 09:00:33 -04:00
} ,
{
2021-08-24 12:54:52 -04:00
// Bar Chart
2021-08-11 09:00:33 -04:00
type : "bar" ,
2021-08-24 12:54:52 -04:00
data : downData ,
2021-08-11 09:00:33 -04:00
borderColor : "#00000000" ,
2022-01-23 09:22:00 -05:00
backgroundColor : colorData ,
2021-08-11 09:00:33 -04:00
yAxisID : "y1" ,
2021-08-24 11:34:48 -04:00
barThickness : "flex" ,
barPercentage : 1 ,
categoryPercentage : 1 ,
2023-03-01 15:47:51 -05:00
inflateAmount : 0.05 ,
label : "status" ,
2021-08-10 07:34:47 -04:00
} ,
] ,
} ;
} ,
} ,
2021-10-18 07:00:39 -04:00
watch : {
2021-10-22 06:44:11 -04:00
// Update chart data when the selected chart period changes
2021-10-18 07:00:39 -04:00
chartPeriodHrs : function ( newPeriod ) {
2022-05-01 05:56:42 -04:00
// eslint-disable-next-line eqeqeq
if ( newPeriod == "0" ) {
2021-10-22 06:38:41 -04:00
this . heartbeatList = null ;
2022-04-14 15:24:58 -04:00
this . $root . storage ( ) . removeItem ( ` chart-period- ${ this . monitorId } ` ) ;
2021-10-22 06:38:41 -04:00
} else {
2021-10-26 00:48:21 -04:00
this . loading = true ;
2021-10-22 06:38:41 -04:00
this . $root . getMonitorBeats ( this . monitorId , newPeriod , ( res ) => {
if ( ! res . ok ) {
toast . error ( res . msg ) ;
} else {
this . heartbeatList = res . data ;
2021-12-05 23:05:26 -05:00
this . $root . storage ( ) [ ` chart-period- ${ this . monitorId } ` ] = newPeriod ;
2021-10-22 06:38:41 -04:00
}
2021-10-26 00:48:21 -04:00
this . loading = false ;
2021-10-22 06:38:41 -04:00
} ) ;
2021-10-18 07:00:39 -04:00
}
}
} ,
2021-10-22 06:38:41 -04:00
created ( ) {
// Setup Watcher on the root heartbeatList,
// And mirror latest change to this.heartbeatList
2021-10-22 06:44:11 -04:00
this . $watch ( ( ) => this . $root . heartbeatList [ this . monitorId ] ,
( heartbeatList ) => {
2022-05-01 05:46:43 -04:00
log . debug ( "ping_chart" , ` this.chartPeriodHrs type ${ typeof this . chartPeriodHrs } , value: ${ this . chartPeriodHrs } ` ) ;
2022-05-01 05:56:42 -04:00
// eslint-disable-next-line eqeqeq
if ( this . chartPeriodHrs != "0" ) {
2021-10-22 07:07:11 -04:00
const newBeat = heartbeatList . at ( - 1 ) ;
if ( newBeat && dayjs . utc ( newBeat . time ) > dayjs . utc ( this . heartbeatList . at ( - 1 ) ? . time ) ) {
this . heartbeatList . push ( heartbeatList . at ( - 1 ) ) ;
}
2021-10-22 06:44:11 -04:00
}
} ,
{ deep : true }
) ;
2021-12-05 23:05:26 -05:00
// Load chart period from storage if saved
let period = this . $root . storage ( ) [ ` chart-period- ${ this . monitorId } ` ] ;
if ( period != null ) {
2022-04-14 15:24:58 -04:00
this . chartPeriodHrs = Math . min ( period , 6 ) ;
2021-12-05 23:05:26 -05:00
}
2021-10-22 06:38:41 -04:00
}
2021-08-10 07:34:47 -04:00
} ;
< / script >
2021-10-18 07:00:39 -04:00
< style lang = "scss" scoped >
@ import "../assets/vars.scss" ;
. form - select {
width : unset ;
display : inline - flex ;
}
. period - options {
2021-10-26 00:33:46 -04:00
padding : 0.1 em 1 em ;
margin - bottom : - 1.2 em ;
2021-10-18 07:00:39 -04:00
float : right ;
position : relative ;
z - index : 10 ;
2021-10-23 13:26:56 -04:00
. dropdown - menu {
padding : 0 ;
min - width : 50 px ;
font - size : 0.9 em ;
. dark & {
background : $dark - bg ;
}
. dropdown - item {
border - radius : 0.3 rem ;
2022-04-13 12:52:07 -04:00
padding : 2 px 16 px 4 px ;
2021-10-23 13:26:56 -04:00
. dark & {
background : $dark - bg ;
}
. dark & : hover {
background : $dark - font - color ;
2022-04-13 14:17:15 -04:00
color : $dark - font - color2 ;
2021-10-23 13:26:56 -04:00
}
}
. dark & . dropdown - item . active {
background : $primary ;
color : $dark - font - color2 ;
}
}
. btn - period - toggle {
padding : 2 px 15 px ;
background : transparent ;
border : 0 ;
color : $link - color ;
opacity : 0.7 ;
font - size : 0.9 em ;
& : : after {
vertical - align : 0.155 em ;
}
. dark & {
color : $dark - font - color ;
}
}
2021-10-18 07:00:39 -04:00
}
. chart - wrapper {
2021-10-23 13:26:56 -04:00
margin - bottom : 0.5 em ;
2021-10-26 00:48:21 -04:00
& . loading {
filter : blur ( 10 px ) ;
}
2021-10-18 07:00:39 -04:00
}
< / style >