refactor(webui): use components to split Home concerns

This commit is contained in:
Antoine Caron 2019-07-22 11:06:04 +02:00 committed by Traefiker Bot
parent 28500989bc
commit 7c852fbf33
6 changed files with 184 additions and 138 deletions

View file

@ -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",

View 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>

View 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>

View file

@ -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;

View file

@ -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")

View file

@ -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"