mirror of
https://github.com/BluemediaGER/fancy-gatus.git
synced 2024-11-22 22:55:29 +01:00
Initial build
This commit is contained in:
parent
ba484d3d9a
commit
20686e39b6
23
README.md
23
README.md
|
@ -1,3 +1,26 @@
|
|||
# fancy-gatus
|
||||
|
||||
Fancy-Gatus is an alternative frontend for [Gatus](https://github.com/TwiN/gatus). The goal is to provide a simplified and modern status page based on data from a Gatus instance, which only displays the most important information in a way that is understandable for end users.
|
||||
|
||||
## Project setup
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"bootstrap-icons-vue": "^0.8.0",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^3.0.0"
|
||||
},
|
||||
|
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -5,11 +5,11 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<title>Fancy Gatus</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
<strong>We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
|
|
142
src/App.vue
142
src/App.vue
|
@ -1,21 +1,157 @@
|
|||
<template>
|
||||
|
||||
<div class="main">
|
||||
<div v-if="$data.loading" class="loader-wrapper">
|
||||
<Loader />
|
||||
</div>
|
||||
<div v-if="!$data.loading" class="content-wrapper">
|
||||
<Header :title="this.config.title" />
|
||||
<OverallStatus class="overall-status" :failedEndpoints="failedEndpoints" />
|
||||
<EndpointGroup class="endpoint-group" v-for="(value, key) in groups" :key="key" :name="key" :endpoints="value" />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Loader from '@/components/Loader.vue';
|
||||
import Header from '@/components/Header.vue';
|
||||
import OverallStatus from '@/components/OverallStatus.vue';
|
||||
import EndpointGroup from '@/components/EndpointGroup.vue';
|
||||
import Footer from '@/components/Footer.vue';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
|
||||
Loader,
|
||||
Header,
|
||||
OverallStatus,
|
||||
EndpointGroup,
|
||||
Footer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
config: null,
|
||||
apiData: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// Group endpoints by their group name
|
||||
groups() {
|
||||
return this.apiData.reduce(function(rv, x) {
|
||||
(rv[x["group"]] = rv[x["group"]] || []).push(x);
|
||||
return rv;
|
||||
}, {});
|
||||
},
|
||||
// Get an array of all failed endpoints
|
||||
failedEndpoints() {
|
||||
return this.apiData.filter(endpoint => {
|
||||
return !endpoint.results[endpoint.results.length - 1].success;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Get frontend config and trigger initial data fetch
|
||||
getConfig() {
|
||||
axios.get('/config.json')
|
||||
.then(response => {
|
||||
this.config = response.data;
|
||||
// Set title if defined in config
|
||||
if (this.config.title) {
|
||||
document.title = this.config.title;
|
||||
}
|
||||
this.getApiData();
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
getApiData() {
|
||||
// Set base URL for API calls if defined in config
|
||||
if (this.config.gatusBaseUrl && axios.defaults.baseURL !== this.config.gatusBaseUrl) {
|
||||
axios.defaults.baseURL = this.config.gatusBaseUrl;
|
||||
}
|
||||
axios.get('/api/v1/endpoints/statuses')
|
||||
.then(response => {
|
||||
// Remove hidden endpoints if defined in config
|
||||
if (this.config.hiddenEndpoints) {
|
||||
this.apiData = response.data.filter(endpoint => {
|
||||
return !this.config.hiddenEndpoints.includes(endpoint.name);
|
||||
});
|
||||
} else {
|
||||
this.apiData = response.data;
|
||||
}
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getConfig();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.loader-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
.content-wrapper {
|
||||
min-width: 60%;
|
||||
max-width: 90%;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.overall-status {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.endpoint-group {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
|
||||
body,
|
||||
html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
--green: #00d560;
|
||||
--orange: #ff9100;
|
||||
}
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
color: #2F3545;
|
||||
padding: 0;
|
||||
}
|
||||
.shadow-box {
|
||||
box-shadow: 0 15px 70px rgb(0 0 0 / 10%);
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.green {
|
||||
color: var(--green);
|
||||
}
|
||||
.orange {
|
||||
color: var(--orange);
|
||||
}
|
||||
</style>
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
61
src/components/Endpoint.vue
Normal file
61
src/components/Endpoint.vue
Normal file
|
@ -0,0 +1,61 @@
|
|||
<template>
|
||||
<div class="endpoint">
|
||||
<BIconCheckCircleFill class="green icon" v-if="isSuccessfull" />
|
||||
<BIconExclamationCircleFill class="orange icon" v-if="!isSuccessfull" />
|
||||
<span>{{ endpoint.name }}</span>
|
||||
<span class="hostname" v-if="hostname">| {{ hostname }}</span>
|
||||
<span class="spacer"></span>
|
||||
<EndpointHistory :results="endpoint.results" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BIconExclamationCircleFill, BIconCheckCircleFill } from 'bootstrap-icons-vue';
|
||||
import EndpointHistory from '@/components/EndpointHistory.vue';
|
||||
|
||||
export default {
|
||||
name: 'Endpoint',
|
||||
components: {
|
||||
BIconExclamationCircleFill,
|
||||
BIconCheckCircleFill,
|
||||
EndpointHistory
|
||||
},
|
||||
props: {
|
||||
endpoint: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSuccessfull() {
|
||||
return this.endpoint.results[this.endpoint.results.length - 1].success;
|
||||
},
|
||||
hostname() {
|
||||
return this.endpoint.results[0].hostname;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.endpoint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
}
|
||||
span {
|
||||
margin: 0;
|
||||
margin-left: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.hostname {
|
||||
margin-left: 5px;
|
||||
color: rgb(143, 143, 143);
|
||||
}
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
41
src/components/EndpointGroup.vue
Normal file
41
src/components/EndpointGroup.vue
Normal file
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div class="endpoint-group">
|
||||
<h3>{{ name }}</h3>
|
||||
<div class="shadow-box list">
|
||||
<Endpoint class="endpoint" v-for="endpoint in endpoints" :key="endpoint.name" :endpoint="endpoint" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Endpoint from '@/components/Endpoint.vue';
|
||||
|
||||
export default {
|
||||
name: 'EndpointGroup',
|
||||
components: {
|
||||
Endpoint
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
endpoints: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: space-between;
|
||||
}
|
||||
.endpoint {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
54
src/components/EndpointHistory.vue
Normal file
54
src/components/EndpointHistory.vue
Normal file
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<div class="history">
|
||||
<div v-for="(result, index) in preparedResults" :key="index" :title="result.timestamp" :class="{blob: true, green: result.success === true, orange: result.success === false, grey: !result.timestamp}"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'EndpointHistory',
|
||||
props: {
|
||||
results: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
preparedResults() {
|
||||
let tmp = [...this.results];
|
||||
while (tmp.length < 20) {
|
||||
tmp.unshift({});
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.history {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 300px;
|
||||
}
|
||||
.blob {
|
||||
width: 7px;
|
||||
height: 20px;
|
||||
margin: 0;
|
||||
border-radius: 50rem;
|
||||
--hover-scale: 1.5;
|
||||
}
|
||||
.blob:hover {
|
||||
transform: scale(var(--hover-scale));
|
||||
}
|
||||
.green {
|
||||
background-color: var(--green);
|
||||
}
|
||||
.orange {
|
||||
background-color: var(--orange);
|
||||
}
|
||||
.grey {
|
||||
background-color: grey;
|
||||
}
|
||||
</style>
|
25
src/components/Footer.vue
Normal file
25
src/components/Footer.vue
Normal file
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<footer>Powered by
|
||||
<a target="_blank" href="https://github.com/TwiN/gatus">Gatus</a>
|
||||
and
|
||||
<a target="_blank" href="https://github.com/BluemediaGER/fancy-gatus">Fancy Gatus</a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Footer'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
footer {
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
a {
|
||||
color: #111;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
33
src/components/Header.vue
Normal file
33
src/components/Header.vue
Normal file
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<img src="@/assets/logo.png" alt="Logo" class="logo">
|
||||
<h2>{{ this.title }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Header',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Infrastructure Status'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.logo {
|
||||
width: 60px;
|
||||
height: auto;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
256
src/components/Loader.vue
Normal file
256
src/components/Loader.vue
Normal file
|
@ -0,0 +1,256 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="loader">
|
||||
<svg viewBox="0 0 80 80">
|
||||
<circle id="test" cx="40" cy="40" r="32"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="loader triangle">
|
||||
<svg viewBox="0 0 86 80">
|
||||
<polygon points="43 8 79 72 7 72"></polygon>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="loader">
|
||||
<svg viewBox="0 0 80 80">
|
||||
<rect x="8" y="8" width="64" height="64"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Loader"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.loader {
|
||||
--path: #2F3545;
|
||||
--dot: #00d560;
|
||||
--duration: 3s;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
position: relative;
|
||||
}
|
||||
.loader:before {
|
||||
content: "";
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
display: block;
|
||||
background: var(--dot);
|
||||
top: 37px;
|
||||
left: 19px;
|
||||
transform: translate(-18px, -18px);
|
||||
-webkit-animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
}
|
||||
.loader svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.loader svg rect,
|
||||
.loader svg polygon,
|
||||
.loader svg circle {
|
||||
fill: none;
|
||||
stroke: var(--path);
|
||||
stroke-width: 10px;
|
||||
stroke-linejoin: round;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
.loader svg polygon {
|
||||
stroke-dasharray: 145 76 145 76;
|
||||
stroke-dashoffset: 0;
|
||||
-webkit-animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
}
|
||||
.loader svg rect {
|
||||
stroke-dasharray: 192 64 192 64;
|
||||
stroke-dashoffset: 0;
|
||||
-webkit-animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
}
|
||||
.loader svg circle {
|
||||
stroke-dasharray: 150 50 150 50;
|
||||
stroke-dashoffset: 75;
|
||||
-webkit-animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
}
|
||||
.loader.triangle {
|
||||
width: 48px;
|
||||
}
|
||||
.loader.triangle:before {
|
||||
left: 21px;
|
||||
transform: translate(-10px, -18px);
|
||||
-webkit-animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes pathTriangle {
|
||||
33% {
|
||||
stroke-dashoffset: 74;
|
||||
}
|
||||
66% {
|
||||
stroke-dashoffset: 147;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 221;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pathTriangle {
|
||||
33% {
|
||||
stroke-dashoffset: 74;
|
||||
}
|
||||
66% {
|
||||
stroke-dashoffset: 147;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 221;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes dotTriangle {
|
||||
33% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
66% {
|
||||
transform: translate(10px, -18px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-10px, -18px);
|
||||
}
|
||||
}
|
||||
@keyframes dotTriangle {
|
||||
33% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
66% {
|
||||
transform: translate(10px, -18px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-10px, -18px);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes pathRect {
|
||||
25% {
|
||||
stroke-dashoffset: 64;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 128;
|
||||
}
|
||||
75% {
|
||||
stroke-dashoffset: 192;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 256;
|
||||
}
|
||||
}
|
||||
@keyframes pathRect {
|
||||
25% {
|
||||
stroke-dashoffset: 64;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 128;
|
||||
}
|
||||
75% {
|
||||
stroke-dashoffset: 192;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 256;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes dotRect {
|
||||
25% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
50% {
|
||||
transform: translate(18px, -18px);
|
||||
}
|
||||
75% {
|
||||
transform: translate(0, -36px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-18px, -18px);
|
||||
}
|
||||
}
|
||||
@keyframes dotRect {
|
||||
25% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
50% {
|
||||
transform: translate(18px, -18px);
|
||||
}
|
||||
75% {
|
||||
transform: translate(0, -36px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-18px, -18px);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes pathCircle {
|
||||
25% {
|
||||
stroke-dashoffset: 125;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 175;
|
||||
}
|
||||
75% {
|
||||
stroke-dashoffset: 225;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 275;
|
||||
}
|
||||
}
|
||||
@keyframes pathCircle {
|
||||
25% {
|
||||
stroke-dashoffset: 125;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 175;
|
||||
}
|
||||
75% {
|
||||
stroke-dashoffset: 225;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 275;
|
||||
}
|
||||
}
|
||||
.loader {
|
||||
display: inline-block;
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
*:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
background: #F5F9FF;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
body .dribbble {
|
||||
position: fixed;
|
||||
display: block;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
}
|
||||
body .dribbble img {
|
||||
display: block;
|
||||
height: 28px;
|
||||
}
|
||||
</style>
|
60
src/components/OverallStatus.vue
Normal file
60
src/components/OverallStatus.vue
Normal file
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div class="shadow-box">
|
||||
<div class="status-wrapper" v-if="this.failedEndpoints.length === 0">
|
||||
<BIconCheckCircleFill class="icon green" />
|
||||
<span>All services healthy.</span>
|
||||
</div>
|
||||
<div class="status-wrapper" v-else>
|
||||
<BIconExclamationCircleFill class="icon orange" />
|
||||
<span>{{ failedEndpointCountText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BIconExclamationCircleFill, BIconCheckCircleFill } from 'bootstrap-icons-vue';
|
||||
|
||||
export default {
|
||||
name: 'OverallStatus',
|
||||
components: {
|
||||
BIconExclamationCircleFill,
|
||||
BIconCheckCircleFill
|
||||
},
|
||||
props: {
|
||||
failedEndpoints: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
failedEndpointCountText() {
|
||||
if (this.failedEndpoints.length === 2) {
|
||||
return 'Two services are experiencing issues.';
|
||||
} else if (this.failedEndpoints.length === 3) {
|
||||
return 'Three services are experiencing issues.';
|
||||
} else if (this.failedEndpoints.length > 3) {
|
||||
return 'Multiple services are experiencing issues.';
|
||||
} else {
|
||||
return 'One service is experiencing issues.';
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.status-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.3rem;
|
||||
margin-left: 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.icon {
|
||||
font-size: 30px;
|
||||
}
|
||||
span {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
</style>
|
17
yarn.lock
17
yarn.lock
|
@ -1941,6 +1941,13 @@ aws4@^1.8.0:
|
|||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||
|
||||
axios@^0.24.0:
|
||||
version "0.24.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
|
||||
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.4"
|
||||
|
||||
babel-eslint@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
|
||||
|
@ -2114,6 +2121,11 @@ boolbase@^1.0.0, boolbase@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
|
||||
|
||||
bootstrap-icons-vue@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap-icons-vue/-/bootstrap-icons-vue-0.8.0.tgz#922dfc368e62c85fe9cadad4f3568ce4c2b7bdba"
|
||||
integrity sha512-bFBpTGNN5RtUpSH7zTz3WkZs2UZA7WXSmkwAKsrA7qddjbIQrvoGsu/yNJiAl14eJgZ+rTqmjqSB+0gRlNZEfA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
|
@ -4026,6 +4038,11 @@ follow-redirects@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
|
||||
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
|
||||
|
||||
follow-redirects@^1.14.4:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
|
||||
for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
|
|
Loading…
Reference in a new issue