mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-28 19:03:24 +01:00
Add tooltip
This commit is contained in:
parent
668ed3b1a2
commit
67a3e4e330
@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<Services :serviceStatuses="serviceStatuses" :maximumNumberOfResults="20" :showStatusOnHover="true" />
|
<Services :serviceStatuses="serviceStatuses" :showStatusOnHover="true" @showTooltip="showTooltip"/>
|
||||||
<Social />
|
<Tooltip :result="tooltip.result" :event="tooltip.event"/>
|
||||||
<Settings @refreshStatuses="fetchStatuses" />
|
<Social/>
|
||||||
|
<Settings @refreshStatuses="fetchStatuses"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@ -9,13 +10,15 @@
|
|||||||
import Social from './components/Social.vue'
|
import Social from './components/Social.vue'
|
||||||
import Settings from './components/Settings.vue'
|
import Settings from './components/Settings.vue'
|
||||||
import Services from './components/Services.vue';
|
import Services from './components/Services.vue';
|
||||||
|
import Tooltip from './components/Tooltip.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
Services,
|
Services,
|
||||||
Social,
|
Social,
|
||||||
Settings
|
Settings,
|
||||||
|
Tooltip
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchStatuses() {
|
fetchStatuses() {
|
||||||
@ -28,11 +31,15 @@ export default {
|
|||||||
this.serviceStatuses = data;
|
this.serviceStatuses = data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
showTooltip(result, event) {
|
||||||
|
this.tooltip = {result: result, event: event};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
serviceStatuses: {}
|
serviceStatuses: {},
|
||||||
|
tooltip: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -43,10 +50,11 @@ export default {
|
|||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
background-color: #f7f9fb;
|
background-color: #f7f9fb;
|
||||||
}
|
}
|
||||||
html {
|
|
||||||
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -12,16 +12,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class='status-over-time flex flex-row'>
|
<div class='status-over-time flex flex-row'>
|
||||||
<slot v-for="filler in 20 - data.results.length" :key="filler">
|
<slot v-for="filler in maximumNumberOfResults - data.results.length" :key="filler">
|
||||||
<span class="status rounded border border-dashed"> </span>
|
<span class="status rounded border border-dashed"> </span>
|
||||||
</slot>
|
</slot>
|
||||||
<slot v-for="result in data.results" :key="result">
|
<slot v-for="result in data.results" :key="result">
|
||||||
<span v-if="result.success" class="status rounded bg-success">✓</span>
|
<span v-if="result.success" class="status rounded bg-success" @mouseenter="showTooltip(result, $event)" @mouseleave="showTooltip(null, $event)">✓</span>
|
||||||
<span v-else class="status rounded bg-red-600">X</span>
|
<span v-else class="status rounded bg-red-600" @mouseenter="showTooltip(result, $event)" @mouseleave="showTooltip(null, $event)">X</span>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='flex flex-wrap status-time-ago'>
|
<div class='flex flex-wrap status-time-ago'>
|
||||||
|
<!-- Show "Last update at" instead? -->
|
||||||
<div class='w-1/2'>
|
<div class='w-1/2'>
|
||||||
{{ generatePrettyTimeAgo(data.results[0].timestamp) }}
|
{{ generatePrettyTimeAgo(data.results[0].timestamp) }}
|
||||||
</div>
|
</div>
|
||||||
@ -37,7 +38,8 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'Service',
|
name: 'Service',
|
||||||
props: {
|
props: {
|
||||||
data: Object
|
maximumNumberOfResults: Number,
|
||||||
|
data: Object,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateMinAndMaxResponseTimes() {
|
updateMinAndMaxResponseTimes() {
|
||||||
@ -70,6 +72,9 @@ export default {
|
|||||||
return minutes + " minute" + (minutes !== "1" ? "s" : "") + " ago";
|
return minutes + " minute" + (minutes !== "1" ? "s" : "") + " ago";
|
||||||
}
|
}
|
||||||
return (differenceInMs/1000).toFixed(0) + " seconds ago";
|
return (differenceInMs/1000).toFixed(0) + " seconds ago";
|
||||||
|
},
|
||||||
|
showTooltip(result, event) {
|
||||||
|
this.$emit('showTooltip', result, event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</slot>
|
</slot>
|
||||||
<div v-if="!collapsed" :class="name === 'undefined' ? '' : 'service-group-content'">
|
<div v-if="!collapsed" :class="name === 'undefined' ? '' : 'service-group-content'">
|
||||||
<slot v-for="service in services" :key="service">
|
<slot v-for="service in services" :key="service">
|
||||||
<Service :data="service"/>
|
<Service :data="service" @showTooltip="showTooltip" :maximumNumberOfResults="50" />
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -55,6 +55,9 @@ export default {
|
|||||||
},
|
},
|
||||||
toggleGroup() {
|
toggleGroup() {
|
||||||
this.collapsed = !this.collapsed;
|
this.collapsed = !this.collapsed;
|
||||||
|
},
|
||||||
|
showTooltip(result, event) {
|
||||||
|
this.$emit('showTooltip', result, event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="results">
|
<div id="results">
|
||||||
<slot v-for="serviceGroup in serviceGroups" :key="serviceGroup">
|
<slot v-for="serviceGroup in serviceGroups" :key="serviceGroup">
|
||||||
<ServiceGroup :services="serviceGroup.services" :name="serviceGroup.name" />
|
<ServiceGroup :services="serviceGroup.services" :name="serviceGroup.name" @showTooltip="showTooltip" />
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +28,6 @@ export default {
|
|||||||
ServiceGroup
|
ServiceGroup
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
maximumNumberOfResults: Number,
|
|
||||||
showStatusOnHover: Boolean,
|
showStatusOnHover: Boolean,
|
||||||
serviceStatuses: Object
|
serviceStatuses: Object
|
||||||
},
|
},
|
||||||
@ -54,6 +53,9 @@ export default {
|
|||||||
serviceGroups.push({name: 'undefined', services: outputByGroup['undefined']})
|
serviceGroups.push({name: 'undefined', services: outputByGroup['undefined']})
|
||||||
}
|
}
|
||||||
this.serviceGroups = serviceGroups;
|
this.serviceGroups = serviceGroups;
|
||||||
|
},
|
||||||
|
showTooltip(result, event) {
|
||||||
|
this.$emit('showTooltip', result, event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -29,7 +29,7 @@ export default {
|
|||||||
}, seconds * 1000);
|
}, seconds * 1000);
|
||||||
},
|
},
|
||||||
refreshStatuses() {
|
refreshStatuses() {
|
||||||
this.$emit('refreshStatuses')
|
this.$emit('refreshStatuses');
|
||||||
},
|
},
|
||||||
handleChangeRefreshInterval() {
|
handleChangeRefreshInterval() {
|
||||||
this.refreshStatuses();
|
this.refreshStatuses();
|
||||||
|
135
web/app/src/components/Tooltip.vue
Normal file
135
web/app/src/components/Tooltip.vue
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<div id="tooltip" ref="tooltip" :class="hidden ? 'invisible' : ''" :style="'top:' + top + 'px; left:' + left + 'px'">
|
||||||
|
<slot v-if="result">
|
||||||
|
<div class="tooltip-title">Timestamp:</div>
|
||||||
|
<code id="tooltip-timestamp">{{ prettifyTimestamp(result.timestamp) }}</code>
|
||||||
|
<div class="tooltip-title">Response time:</div>
|
||||||
|
<code id="tooltip-response-time">{{ (result.duration / 1000000).toFixed(0) }}ms</code>
|
||||||
|
<div class="tooltip-title">Conditions:</div>
|
||||||
|
<code id="tooltip-conditions">
|
||||||
|
<slot v-for="conditionResult in result.conditionResults" :key="conditionResult">
|
||||||
|
{{ conditionResult.success ? "✓" : "X" }} ~ {{ conditionResult.condition }}<br/>
|
||||||
|
</slot>
|
||||||
|
</code>
|
||||||
|
<div id="tooltip-errors-container" v-if="result.errors && result.errors.length">
|
||||||
|
<div class="tooltip-title">Errors:</div>
|
||||||
|
<code id="tooltip-errors">
|
||||||
|
<slot v-for="error in result.errors" :key="error">
|
||||||
|
- {{ error }}<br/>
|
||||||
|
</slot>
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Services',
|
||||||
|
props: {
|
||||||
|
event: Event,
|
||||||
|
result: Object
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
prettifyTimestamp(timestamp) {
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
let YYYY = date.getFullYear();
|
||||||
|
let MM = ((date.getMonth() + 1) < 10 ? "0" : "") + "" + (date.getMonth() + 1);
|
||||||
|
let DD = ((date.getDate()) < 10 ? "0" : "") + "" + (date.getDate());
|
||||||
|
let hh = ((date.getHours()) < 10 ? "0" : "") + "" + (date.getHours());
|
||||||
|
let mm = ((date.getMinutes()) < 10 ? "0" : "") + "" + (date.getMinutes());
|
||||||
|
let ss = ((date.getSeconds()) < 10 ? "0" : "") + "" + (date.getSeconds());
|
||||||
|
return YYYY + "-" + MM + "-" + DD + " " + hh + ":" + mm + ":" + ss;
|
||||||
|
},
|
||||||
|
htmlEntities(s) {
|
||||||
|
return String(s)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
},
|
||||||
|
reposition() {
|
||||||
|
if (this.event && this.event.type) {
|
||||||
|
if (this.event.type === 'mouseenter') {
|
||||||
|
let targetTopPosition = this.event.target.getBoundingClientRect().y + 30;
|
||||||
|
let targetLeftPosition = this.event.target.getBoundingClientRect().x;
|
||||||
|
let tooltipBoundingClientRect = this.$refs.tooltip.getBoundingClientRect();
|
||||||
|
if (targetLeftPosition + window.scrollX + tooltipBoundingClientRect.width + 50 > document.body.getBoundingClientRect().width) {
|
||||||
|
targetLeftPosition = this.event.target.getBoundingClientRect().x - tooltipBoundingClientRect.width + this.event.target.getBoundingClientRect().width;
|
||||||
|
if (targetLeftPosition < 0) {
|
||||||
|
targetLeftPosition += -targetLeftPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetTopPosition + window.scrollY + tooltipBoundingClientRect.height + 50 > document.body.getBoundingClientRect().height && targetTopPosition >= 0) {
|
||||||
|
targetTopPosition = this.event.target.getBoundingClientRect().y - (tooltipBoundingClientRect.height + 10);
|
||||||
|
if (targetTopPosition < 0) {
|
||||||
|
targetTopPosition = this.event.target.getBoundingClientRect().y + 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.top = targetTopPosition;
|
||||||
|
this.left = targetLeftPosition;
|
||||||
|
} else if (this.event.type === 'mouseleave') {
|
||||||
|
this.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
event: function (value) {
|
||||||
|
if (value && value.type) {
|
||||||
|
if (value.type === 'mouseenter') {
|
||||||
|
this.hidden = false;
|
||||||
|
} else if (value.type === 'mouseleave') {
|
||||||
|
this.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
this.reposition();
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.reposition();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hidden: false,
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#tooltip {
|
||||||
|
position: fixed;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip code {
|
||||||
|
color: #212529;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip .tooltip-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip .tooltip-title {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip > .tooltip-title:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user