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"
|
"vuex": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^5.9.0",
|
||||||
"@vue/cli-plugin-babel": "^3.9.0",
|
"@vue/cli-plugin-babel": "^3.9.0",
|
||||||
"@vue/cli-plugin-eslint": "^3.9.0",
|
"@vue/cli-plugin-eslint": "^3.9.0",
|
||||||
"@vue/cli-plugin-unit-jest": "^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 store from "./store";
|
||||||
|
|
||||||
import "bulma/css/bulma.min.css";
|
import "bulma/css/bulma.min.css";
|
||||||
|
import "@fortawesome/fontawesome-free";
|
||||||
|
import "@fortawesome/fontawesome-free/css/all.css";
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<main class="home section">
|
<main class="home section">
|
||||||
<section class="container panel">
|
<section class="container hero is-info">
|
||||||
<p class="panel-heading ">🚧 Work in progress...</p>
|
<div class="hero-body">
|
||||||
<div class="panel-block">
|
<div class="container">
|
||||||
<div>
|
<h1 class="title">
|
||||||
<p>
|
🚧 Work in progress...
|
||||||
In the meantime, you can review your current configuration by using
|
</h1>
|
||||||
the <a href="/api/rawdata">/api/rawdata</a> endpoint.
|
<h2 class="subtitle">
|
||||||
</p>
|
<p>
|
||||||
<p>
|
In the meantime, you can review your current configuration by
|
||||||
Also, please keep your <i class="fa fa-eye" /> on our
|
using the
|
||||||
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
|
<a href="/api/rawdata"
|
||||||
>documentation</a
|
>/api/rawdata <i class="fas fa-external-link-alt"
|
||||||
>
|
/></a>
|
||||||
to stay informed
|
endpoint.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
Also, please keep your <i class="fa fa-eye" /> on our
|
||||||
|
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
|
||||||
|
>documentation<i class="fas fa-external-link-alt"
|
||||||
|
/></a>
|
||||||
|
to stay informed
|
||||||
|
</p>
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="container panel" v-if="entrypoints.length">
|
<section class="container" v-if="entrypoints.length">
|
||||||
<p class="panel-heading ">Entrypoints</p>
|
<p class="title is-4">Entrypoints</p>
|
||||||
<div class="panel-block">
|
<div class="tile is-child box columns">
|
||||||
<nav class="level" :style="{ flex: '1 1' }">
|
<nav class="level" :style="{ flex: '1 1' }">
|
||||||
<div
|
<div
|
||||||
class="level-item has-text-centered"
|
class="level-item has-text-centered"
|
||||||
|
@ -39,47 +47,67 @@
|
||||||
|
|
||||||
<section class="container" v-if="overview.http">
|
<section class="container" v-if="overview.http">
|
||||||
<p class="title is-4">HTTP</p>
|
<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">
|
<div class="column is-4">
|
||||||
<canvas id="http-routers" />
|
<EntityStateDoughnut
|
||||||
|
:entity="overview.http.routers"
|
||||||
|
title="Routers"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<canvas id="http-middlewares" />
|
<EntityStateDoughnut
|
||||||
|
:entity="overview.http.middlewares"
|
||||||
|
title="Middlewares"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<canvas id="http-services" />
|
<EntityStateDoughnut
|
||||||
|
:entity="overview.http.services"
|
||||||
|
title="Services"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="container" v-if="overview.tcp">
|
<section class="container" v-if="overview.tcp">
|
||||||
<p class="title is-4">TCP</p>
|
<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">
|
<div class="column is-4">
|
||||||
<canvas id="tcp-routers" />
|
<EntityStateDoughnut :entity="overview.tcp.routers" title="Routers" />
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-4">
|
<div class="column is-4">
|
||||||
<canvas id="tcp-services" />
|
<EntityStateDoughnut
|
||||||
|
:entity="overview.tcp.services"
|
||||||
|
title="Services"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="container panel">
|
<section class="container">
|
||||||
<p class="panel-heading">Features</p>
|
<p class="title is-4">Features</p>
|
||||||
<div class="panel-block">
|
<div class="tile is-child box columns">
|
||||||
<div class="tile is-ancestor">
|
<div class="tile is-ancestor">
|
||||||
<div
|
<Tile
|
||||||
class="tile is-parent"
|
|
||||||
v-for="(feature, key) of overview.features"
|
v-for="(feature, key) of overview.features"
|
||||||
:key="key"
|
:key="key"
|
||||||
>
|
:title="key"
|
||||||
<div
|
:modifier="{ 'is-success': feature, 'is-danger': !feature }"
|
||||||
class="tile is-child notification"
|
/>
|
||||||
:class="{ 'is-success': feature, 'is-danger': !feature }"
|
</div>
|
||||||
>
|
</div>
|
||||||
<p class="title">{{ key }}</p>
|
</section>
|
||||||
</div>
|
|
||||||
</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -87,119 +115,28 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Chart from "chart.js";
|
import Tile from "../components/Tile";
|
||||||
|
import EntityStateDoughnut from "../components/EntityStateDoughnut";
|
||||||
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 {
|
export default {
|
||||||
name: "home",
|
name: "home",
|
||||||
|
components: {
|
||||||
|
Tile,
|
||||||
|
EntityStateDoughnut
|
||||||
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
entrypoints: [],
|
entrypoints: [],
|
||||||
overview: {
|
overview: {
|
||||||
features: []
|
features: [],
|
||||||
},
|
providers: []
|
||||||
charts: {
|
|
||||||
http: {
|
|
||||||
routers: null,
|
|
||||||
middlewares: null,
|
|
||||||
services: null
|
|
||||||
},
|
|
||||||
tcp: {
|
|
||||||
routers: null,
|
|
||||||
services: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
interval: null
|
interval: null
|
||||||
}),
|
}),
|
||||||
methods: {
|
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() {
|
fetchOverview() {
|
||||||
return fetch("/api/overview")
|
return fetch("/api/overview")
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(response => (this.overview = response))
|
.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"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
fetchEntrypoints() {
|
fetchEntrypoints() {
|
||||||
return fetch("/api/entrypoints")
|
return fetch("/api/entrypoints")
|
||||||
|
|
|
@ -670,6 +670,11 @@
|
||||||
lodash "^4.17.11"
|
lodash "^4.17.11"
|
||||||
to-fast-properties "^2.0.0"
|
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":
|
"@hapi/address@2.x.x":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
|
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a"
|
||||||
|
|
Loading…
Reference in a new issue