183 lines
5.3 KiB
Vue
183 lines
5.3 KiB
Vue
<template>
|
|
<div class='service px-3 py-3 border-l border-r border-t rounded-none hover:bg-gray-100 dark:hover:bg-gray-700 dark:border-gray-500' v-if="data">
|
|
<div class='flex flex-wrap mb-2'>
|
|
<div class='w-3/4'>
|
|
<router-link :to="generatePath()" class="font-bold hover:text-blue-800 hover:underline dark:hover:text-blue-400" title="View detailed service health">
|
|
{{ data.name }}
|
|
</router-link>
|
|
<span v-if="data.results && data.results.length" class='text-gray-500 font-light'> | {{ data.results[data.results.length - 1].hostname }}</span>
|
|
</div>
|
|
<div class='w-1/4 text-right'>
|
|
<span class='font-light overflow-x-hidden cursor-pointer select-none' v-if="data.results && data.results.length" @click="toggleShowAverageResponseTime" :title="showAverageResponseTime ? 'Average response time' : 'Minimum and maximum response time'">
|
|
<slot v-if="showAverageResponseTime">
|
|
~{{ averageResponseTime }}ms
|
|
</slot>
|
|
<slot v-else>
|
|
{{ (minResponseTime === maxResponseTime ? minResponseTime : (minResponseTime + '-' + maxResponseTime)) }}ms
|
|
</slot>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class='status-over-time flex flex-row'>
|
|
<slot v-if="data.results && data.results.length">
|
|
<slot v-if="data.results.length < maximumNumberOfResults">
|
|
<span v-for="filler in maximumNumberOfResults - data.results.length" :key="filler" class="status rounded border border-dashed border-gray-500"> </span>
|
|
</slot>
|
|
<slot v-for="result in data.results" :key="result">
|
|
<span v-if="result.success" class="status status-success rounded bg-success" @mouseenter="showTooltip(result, $event)" @mouseleave="showTooltip(null, $event)"></span>
|
|
<span v-else class="status status-failure rounded bg-red-600" @mouseenter="showTooltip(result, $event)" @mouseleave="showTooltip(null, $event)"></span>
|
|
</slot>
|
|
</slot>
|
|
<slot v-else>
|
|
<span v-for="filler in maximumNumberOfResults" :key="filler" class="status rounded border border-dashed"> </span>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
<div class='flex flex-wrap status-time-ago'>
|
|
<slot v-if="data.results && data.results.length">
|
|
<div class='w-1/2'>
|
|
{{ generatePrettyTimeAgo(data.results[0].timestamp) }}
|
|
</div>
|
|
<div class='w-1/2 text-right'>
|
|
{{ generatePrettyTimeAgo(data.results[data.results.length - 1].timestamp) }}
|
|
</div>
|
|
</slot>
|
|
<slot v-else>
|
|
<div class='w-1/2'>
|
|
|
|
</div>
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
<script>
|
|
import {helper} from "@/mixins/helper";
|
|
|
|
export default {
|
|
name: 'Service',
|
|
props: {
|
|
maximumNumberOfResults: Number,
|
|
data: Object,
|
|
showAverageResponseTime: Boolean
|
|
},
|
|
emits: ['showTooltip', 'toggleShowAverageResponseTime'],
|
|
mixins: [helper],
|
|
methods: {
|
|
updateMinAndMaxResponseTimes() {
|
|
let minResponseTime = null;
|
|
let maxResponseTime = null;
|
|
let totalResponseTime = 0;
|
|
for (let i in this.data.results) {
|
|
const responseTime = parseInt((this.data.results[i].duration/1000000).toFixed(0));
|
|
totalResponseTime += responseTime;
|
|
if (minResponseTime == null || minResponseTime > responseTime) {
|
|
minResponseTime = responseTime;
|
|
}
|
|
if (maxResponseTime == null || maxResponseTime < responseTime) {
|
|
maxResponseTime = responseTime;
|
|
}
|
|
}
|
|
if (this.minResponseTime !== minResponseTime) {
|
|
this.minResponseTime = minResponseTime;
|
|
}
|
|
if (this.maxResponseTime !== maxResponseTime) {
|
|
this.maxResponseTime = maxResponseTime;
|
|
}
|
|
if (this.data.results && this.data.results.length) {
|
|
this.averageResponseTime = (totalResponseTime/this.data.results.length).toFixed(0);
|
|
}
|
|
},
|
|
generatePath() {
|
|
if (!this.data) {
|
|
return '/';
|
|
}
|
|
return `/services/${this.data.key}`;
|
|
},
|
|
showTooltip(result, event) {
|
|
this.$emit('showTooltip', result, event);
|
|
},
|
|
toggleShowAverageResponseTime() {
|
|
this.$emit('toggleShowAverageResponseTime');
|
|
}
|
|
},
|
|
watch: {
|
|
data: function () {
|
|
this.updateMinAndMaxResponseTimes();
|
|
}
|
|
},
|
|
created() {
|
|
this.updateMinAndMaxResponseTimes()
|
|
},
|
|
data() {
|
|
return {
|
|
minResponseTime: 0,
|
|
maxResponseTime: 0,
|
|
averageResponseTime: 0
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
|
|
<style>
|
|
.service:first-child {
|
|
border-top-left-radius: 3px;
|
|
border-top-right-radius: 3px;
|
|
}
|
|
|
|
.service:last-child {
|
|
border-bottom-left-radius: 3px;
|
|
border-bottom-right-radius: 3px;
|
|
border-bottom-width: 3px;
|
|
}
|
|
|
|
.status-over-time {
|
|
overflow: auto;
|
|
}
|
|
|
|
.status-over-time > span:not(:first-child) {
|
|
margin-left: 2px;
|
|
}
|
|
|
|
.status {
|
|
cursor: pointer;
|
|
transition: all 500ms ease-in-out;
|
|
overflow-x: hidden;
|
|
color: white;
|
|
width: 5%;
|
|
font-size: 75%;
|
|
font-weight: 700;
|
|
text-align: center;
|
|
}
|
|
|
|
.status:hover {
|
|
opacity: 0.7;
|
|
transition: opacity 100ms ease-in-out;
|
|
color: black;
|
|
}
|
|
|
|
.status-time-ago {
|
|
color: #6a737d;
|
|
opacity: 0.5;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.status.status-success::after {
|
|
content: "✓";
|
|
}
|
|
|
|
.status.status-failure::after {
|
|
content: "X";
|
|
}
|
|
|
|
@media screen and (max-width: 600px) {
|
|
.status.status-success::after,
|
|
.status.status-failure::after {
|
|
content: " ";
|
|
white-space: pre;
|
|
}
|
|
}
|
|
</style> |