refactor(webui): use components to split Home concerns
This commit is contained in:
parent
28500989bc
commit
7c852fbf33
6 changed files with 184 additions and 138 deletions
|
@ -18,6 +18,7 @@
|
|||
"vuex": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.9.0",
|
||||
"@vue/cli-plugin-babel": "^3.9.0",
|
||||
"@vue/cli-plugin-eslint": "^3.9.0",
|
||||
"@vue/cli-plugin-unit-jest": "^3.9.0",
|
||||
|
|
79
webui/src/components/EntityStateDoughnut.vue
Normal file
79
webui/src/components/EntityStateDoughnut.vue
Normal file
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<canvas />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Chart from "chart.js";
|
||||
|
||||
Chart.plugins.register({
|
||||
afterDraw: function(chart) {
|
||||
if (chart.data.datasets[0].data.reduce((acc, it) => acc + it, 0) === 0) {
|
||||
var ctx = chart.chart.ctx;
|
||||
var width = chart.chart.width;
|
||||
var height = chart.chart.height;
|
||||
chart.clear();
|
||||
|
||||
ctx.save();
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.font = "16px normal 'Helvetica Nueue'";
|
||||
ctx.fillText(`No ${chart.options.title.text}`, width / 2, height / 2);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "EntityStateDoughnut",
|
||||
props: {
|
||||
entity: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
errors: 0,
|
||||
warnings: 0,
|
||||
total: 0
|
||||
})
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
data() {
|
||||
return [
|
||||
this.entity.errors,
|
||||
this.entity.warnings,
|
||||
this.entity.total - (this.entity.errors + this.entity.warnings)
|
||||
];
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
new Chart(this.$el, {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: this.data,
|
||||
backgroundColor: [
|
||||
"hsl(348, 100%, 61%)",
|
||||
"hsl(48, 100%, 67%)",
|
||||
"hsl(141, 71%, 48%)"
|
||||
]
|
||||
}
|
||||
],
|
||||
labels: ["errors", "warnings", "success"]
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: true,
|
||||
text: this.title
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
22
webui/src/components/Tile.vue
Normal file
22
webui/src/components/Tile.vue
Normal file
|
@ -0,0 +1,22 @@
|
|||
<template>
|
||||
<div class="tile is-parent">
|
||||
<div class="tile is-child notification" :class="modifier">
|
||||
<p class="title">{{ title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modifier: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -4,6 +4,8 @@ import router from "./router";
|
|||
import store from "./store";
|
||||
|
||||
import "bulma/css/bulma.min.css";
|
||||
import "@fortawesome/fontawesome-free";
|
||||
import "@fortawesome/fontawesome-free/css/all.css";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
|
|
|
@ -1,27 +1,35 @@
|
|||
<template>
|
||||
<main class="home section">
|
||||
<section class="container panel">
|
||||
<p class="panel-heading ">🚧 Work in progress...</p>
|
||||
<div class="panel-block">
|
||||
<div>
|
||||
<section class="container hero is-info">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h1 class="title">
|
||||
🚧 Work in progress...
|
||||
</h1>
|
||||
<h2 class="subtitle">
|
||||
<p>
|
||||
In the meantime, you can review your current configuration by using
|
||||
the <a href="/api/rawdata">/api/rawdata</a> endpoint.
|
||||
In the meantime, you can review your current configuration by
|
||||
using the
|
||||
<a href="/api/rawdata"
|
||||
>/api/rawdata <i class="fas fa-external-link-alt"
|
||||
/></a>
|
||||
endpoint.
|
||||
</p>
|
||||
<p>
|
||||
Also, please keep your <i class="fa fa-eye" /> on our
|
||||
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
|
||||
>documentation</a
|
||||
>
|
||||
>documentation<i class="fas fa-external-link-alt"
|
||||
/></a>
|
||||
to stay informed
|
||||
</p>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container panel" v-if="entrypoints.length">
|
||||
<p class="panel-heading ">Entrypoints</p>
|
||||
<div class="panel-block">
|
||||
<section class="container" v-if="entrypoints.length">
|
||||
<p class="title is-4">Entrypoints</p>
|
||||
<div class="tile is-child box columns">
|
||||
<nav class="level" :style="{ flex: '1 1' }">
|
||||
<div
|
||||
class="level-item has-text-centered"
|
||||
|
@ -39,47 +47,67 @@
|
|||
|
||||
<section class="container" v-if="overview.http">
|
||||
<p class="title is-4">HTTP</p>
|
||||
<div class="tile is-child box columns is-height-limited">
|
||||
<div class="tile is-child box columns">
|
||||
<div class="column is-4">
|
||||
<canvas id="http-routers" />
|
||||
<EntityStateDoughnut
|
||||
:entity="overview.http.routers"
|
||||
title="Routers"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<canvas id="http-middlewares" />
|
||||
<EntityStateDoughnut
|
||||
:entity="overview.http.middlewares"
|
||||
title="Middlewares"
|
||||
/>
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<canvas id="http-services" />
|
||||
<EntityStateDoughnut
|
||||
:entity="overview.http.services"
|
||||
title="Services"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container" v-if="overview.tcp">
|
||||
<p class="title is-4">TCP</p>
|
||||
<div class="tile is-child box columns is-height-limited">
|
||||
<div class="tile is-child box columns">
|
||||
<div class="column is-4">
|
||||
<canvas id="tcp-routers" />
|
||||
<EntityStateDoughnut :entity="overview.tcp.routers" title="Routers" />
|
||||
</div>
|
||||
<div class="column is-4">
|
||||
<canvas id="tcp-services" />
|
||||
<EntityStateDoughnut
|
||||
:entity="overview.tcp.services"
|
||||
title="Services"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container panel">
|
||||
<p class="panel-heading">Features</p>
|
||||
<div class="panel-block">
|
||||
<section class="container">
|
||||
<p class="title is-4">Features</p>
|
||||
<div class="tile is-child box columns">
|
||||
<div class="tile is-ancestor">
|
||||
<div
|
||||
class="tile is-parent"
|
||||
<Tile
|
||||
v-for="(feature, key) of overview.features"
|
||||
:key="key"
|
||||
>
|
||||
<div
|
||||
class="tile is-child notification"
|
||||
:class="{ 'is-success': feature, 'is-danger': !feature }"
|
||||
>
|
||||
<p class="title">{{ key }}</p>
|
||||
:title="key"
|
||||
:modifier="{ 'is-success': feature, 'is-danger': !feature }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container">
|
||||
<p class="title is-4">Providers</p>
|
||||
<div class="tile is-child box columns">
|
||||
<div class="tile is-ancestor">
|
||||
<Tile
|
||||
v-for="provider of overview.providers"
|
||||
:key="provider"
|
||||
:title="provider"
|
||||
:modifier="{ 'is-info': provider, 'is-3': provider }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -87,119 +115,28 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Chart from "chart.js";
|
||||
|
||||
Chart.plugins.register({
|
||||
afterDraw: function(chart) {
|
||||
if (chart.data.datasets[0].data.reduce((acc, it) => acc + it, 0) === 0) {
|
||||
var ctx = chart.chart.ctx;
|
||||
var width = chart.chart.width;
|
||||
var height = chart.chart.height
|
||||
chart.clear();
|
||||
|
||||
ctx.save();
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.font = "16px normal 'Helvetica Nueue'";
|
||||
ctx.fillText(`No ${chart.options.title.text}`, width / 2, height / 2);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
});
|
||||
import Tile from "../components/Tile";
|
||||
import EntityStateDoughnut from "../components/EntityStateDoughnut";
|
||||
|
||||
export default {
|
||||
name: "home",
|
||||
components: {
|
||||
Tile,
|
||||
EntityStateDoughnut
|
||||
},
|
||||
data: () => ({
|
||||
entrypoints: [],
|
||||
overview: {
|
||||
features: []
|
||||
},
|
||||
charts: {
|
||||
http: {
|
||||
routers: null,
|
||||
middlewares: null,
|
||||
services: null
|
||||
},
|
||||
tcp: {
|
||||
routers: null,
|
||||
services: null
|
||||
}
|
||||
features: [],
|
||||
providers: []
|
||||
},
|
||||
interval: null
|
||||
}),
|
||||
methods: {
|
||||
buildDoughnutChart(
|
||||
selector,
|
||||
entity = { errors: 2, warnings: 2, total: 6 },
|
||||
name
|
||||
) {
|
||||
return new Chart(this.$el.querySelector(selector), {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
entity.errors,
|
||||
entity.warnings,
|
||||
entity.total - (entity.errors + entity.warnings)
|
||||
],
|
||||
backgroundColor: [
|
||||
"hsl(348, 100%, 61%)",
|
||||
"hsl(48, 100%, 67%)",
|
||||
"hsl(141, 71%, 48%)"
|
||||
]
|
||||
}
|
||||
],
|
||||
labels: ["errors", "warnings", "success"]
|
||||
},
|
||||
options: {
|
||||
title: {
|
||||
display: true,
|
||||
text: name
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchOverview() {
|
||||
return fetch("/api/overview")
|
||||
.then(response => response.json())
|
||||
.then(response => (this.overview = response))
|
||||
.then(() => {
|
||||
this.charts = {
|
||||
http: {
|
||||
routers: this.buildDoughnutChart(
|
||||
"#http-routers",
|
||||
this.overview.http.routers,
|
||||
"Routers"
|
||||
),
|
||||
middlewares: this.buildDoughnutChart(
|
||||
"#http-middlewares",
|
||||
this.overview.http.middlewares,
|
||||
"Middlewares"
|
||||
),
|
||||
services: this.buildDoughnutChart(
|
||||
"#http-services",
|
||||
this.overview.http.services,
|
||||
"Services"
|
||||
)
|
||||
},
|
||||
tcp: {
|
||||
routers: this.buildDoughnutChart(
|
||||
"#tcp-routers",
|
||||
this.overview.tcp.routers,
|
||||
"Routers"
|
||||
),
|
||||
services: this.buildDoughnutChart(
|
||||
"#tcp-services",
|
||||
this.overview.tcp.services,
|
||||
"Services"
|
||||
)
|
||||
}
|
||||
};
|
||||
});
|
||||
.then(response => (this.overview = response));
|
||||
},
|
||||
fetchEntrypoints() {
|
||||
return fetch("/api/entrypoints")
|
||||
|
|
|
@ -670,6 +670,11 @@
|
|||
lodash "^4.17.11"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@fortawesome/fontawesome-free@^5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.9.0.tgz#1aa5c59efb1b8c6eb6277d1e3e8c8f31998b8c8e"
|
||||
integrity sha512-g795BBEzM/Hq2SYNPm/NQTIp3IWd4eXSH0ds87Na2jnrAUFX3wkyZAI4Gwj9DOaWMuz2/01i8oWI7P7T/XLkhg==
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
|
||||
|
|
Loading…
Reference in a new issue