Skip to content

Commit 7c852fb

Browse files
Slashgeartraefiker
authored andcommitted
refactor(webui): use components to split Home concerns
1 parent 2850098 commit 7c852fb

File tree

6 files changed

+184
-138
lines changed

6 files changed

+184
-138
lines changed

webui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"vuex": "^3.0.1"
1919
},
2020
"devDependencies": {
21+
"@fortawesome/fontawesome-free": "^5.9.0",
2122
"@vue/cli-plugin-babel": "^3.9.0",
2223
"@vue/cli-plugin-eslint": "^3.9.0",
2324
"@vue/cli-plugin-unit-jest": "^3.9.0",
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<template>
2+
<canvas />
3+
</template>
4+
5+
<script>
6+
import Chart from "chart.js";
7+
8+
Chart.plugins.register({
9+
afterDraw: function(chart) {
10+
if (chart.data.datasets[0].data.reduce((acc, it) => acc + it, 0) === 0) {
11+
var ctx = chart.chart.ctx;
12+
var width = chart.chart.width;
13+
var height = chart.chart.height;
14+
chart.clear();
15+
16+
ctx.save();
17+
ctx.textAlign = "center";
18+
ctx.textBaseline = "middle";
19+
ctx.font = "16px normal 'Helvetica Nueue'";
20+
ctx.fillText(`No ${chart.options.title.text}`, width / 2, height / 2);
21+
ctx.restore();
22+
}
23+
}
24+
});
25+
26+
export default {
27+
name: "EntityStateDoughnut",
28+
props: {
29+
entity: {
30+
type: Object,
31+
default: () => ({
32+
errors: 0,
33+
warnings: 0,
34+
total: 0
35+
})
36+
},
37+
title: {
38+
type: String,
39+
required: true
40+
}
41+
},
42+
computed: {
43+
data() {
44+
return [
45+
this.entity.errors,
46+
this.entity.warnings,
47+
this.entity.total - (this.entity.errors + this.entity.warnings)
48+
];
49+
}
50+
},
51+
mounted() {
52+
new Chart(this.$el, {
53+
type: "doughnut",
54+
data: {
55+
datasets: [
56+
{
57+
data: this.data,
58+
backgroundColor: [
59+
"hsl(348, 100%, 61%)",
60+
"hsl(48, 100%, 67%)",
61+
"hsl(141, 71%, 48%)"
62+
]
63+
}
64+
],
65+
labels: ["errors", "warnings", "success"]
66+
},
67+
options: {
68+
title: {
69+
display: true,
70+
text: this.title
71+
},
72+
legend: {
73+
display: false
74+
}
75+
}
76+
});
77+
}
78+
};
79+
</script>

webui/src/components/Tile.vue

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<div class="tile is-parent">
3+
<div class="tile is-child notification" :class="modifier">
4+
<p class="title">{{ title }}</p>
5+
</div>
6+
</div>
7+
</template>
8+
9+
<script>
10+
export default {
11+
props: {
12+
modifier: {
13+
type: Object,
14+
default: () => ({})
15+
},
16+
title: {
17+
type: String,
18+
required: true
19+
}
20+
}
21+
};
22+
</script>

webui/src/main.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import router from "./router";
44
import store from "./store";
55

66
import "bulma/css/bulma.min.css";
7+
import "@fortawesome/fontawesome-free";
8+
import "@fortawesome/fontawesome-free/css/all.css";
79

810
Vue.config.productionTip = false;
911

webui/src/views/Home.vue

Lines changed: 75 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
<template>
22
<main class="home section">
3-
<section class="container panel">
4-
<p class="panel-heading ">🚧 Work in progress...</p>
5-
<div class="panel-block">
6-
<div>
7-
<p>
8-
In the meantime, you can review your current configuration by using
9-
the <a href="/api/rawdata">/api/rawdata</a> endpoint.
10-
</p>
11-
<p>
12-
Also, please keep your <i class="fa fa-eye" /> on our
13-
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
14-
>documentation</a
15-
>
16-
to stay informed
17-
</p>
3+
<section class="container hero is-info">
4+
<div class="hero-body">
5+
<div class="container">
6+
<h1 class="title">
7+
🚧 Work in progress...
8+
</h1>
9+
<h2 class="subtitle">
10+
<p>
11+
In the meantime, you can review your current configuration by
12+
using the
13+
<a href="/api/rawdata"
14+
>/api/rawdata <i class="fas fa-external-link-alt"
15+
/></a>
16+
endpoint.
17+
</p>
18+
<p>
19+
Also, please keep your <i class="fa fa-eye" /> on our
20+
<a href="https://docs.traefik.io/v2.0/operations/dashboard/"
21+
>documentation<i class="fas fa-external-link-alt"
22+
/></a>
23+
to stay informed
24+
</p>
25+
</h2>
1826
</div>
1927
</div>
2028
</section>
2129

22-
<section class="container panel" v-if="entrypoints.length">
23-
<p class="panel-heading ">Entrypoints</p>
24-
<div class="panel-block">
30+
<section class="container" v-if="entrypoints.length">
31+
<p class="title is-4">Entrypoints</p>
32+
<div class="tile is-child box columns">
2533
<nav class="level" :style="{ flex: '1 1' }">
2634
<div
2735
class="level-item has-text-centered"
@@ -39,167 +47,96 @@
3947

4048
<section class="container" v-if="overview.http">
4149
<p class="title is-4">HTTP</p>
42-
<div class="tile is-child box columns is-height-limited">
50+
<div class="tile is-child box columns">
4351
<div class="column is-4">
44-
<canvas id="http-routers" />
52+
<EntityStateDoughnut
53+
:entity="overview.http.routers"
54+
title="Routers"
55+
/>
4556
</div>
4657
<div class="column is-4">
47-
<canvas id="http-middlewares" />
58+
<EntityStateDoughnut
59+
:entity="overview.http.middlewares"
60+
title="Middlewares"
61+
/>
4862
</div>
4963
<div class="column is-4">
50-
<canvas id="http-services" />
64+
<EntityStateDoughnut
65+
:entity="overview.http.services"
66+
title="Services"
67+
/>
5168
</div>
5269
</div>
5370
</section>
5471

5572
<section class="container" v-if="overview.tcp">
5673
<p class="title is-4">TCP</p>
57-
<div class="tile is-child box columns is-height-limited">
74+
<div class="tile is-child box columns">
5875
<div class="column is-4">
59-
<canvas id="tcp-routers" />
76+
<EntityStateDoughnut :entity="overview.tcp.routers" title="Routers" />
6077
</div>
6178
<div class="column is-4">
62-
<canvas id="tcp-services" />
79+
<EntityStateDoughnut
80+
:entity="overview.tcp.services"
81+
title="Services"
82+
/>
6383
</div>
6484
</div>
6585
</section>
6686

67-
<section class="container panel">
68-
<p class="panel-heading">Features</p>
69-
<div class="panel-block">
87+
<section class="container">
88+
<p class="title is-4">Features</p>
89+
<div class="tile is-child box columns">
7090
<div class="tile is-ancestor">
71-
<div
72-
class="tile is-parent"
91+
<Tile
7392
v-for="(feature, key) of overview.features"
7493
:key="key"
75-
>
76-
<div
77-
class="tile is-child notification"
78-
:class="{ 'is-success': feature, 'is-danger': !feature }"
79-
>
80-
<p class="title">{{ key }}</p>
81-
</div>
82-
</div>
94+
:title="key"
95+
:modifier="{ 'is-success': feature, 'is-danger': !feature }"
96+
/>
97+
</div>
98+
</div>
99+
</section>
100+
101+
<section class="container">
102+
<p class="title is-4">Providers</p>
103+
<div class="tile is-child box columns">
104+
<div class="tile is-ancestor">
105+
<Tile
106+
v-for="provider of overview.providers"
107+
:key="provider"
108+
:title="provider"
109+
:modifier="{ 'is-info': provider, 'is-3': provider }"
110+
/>
83111
</div>
84112
</div>
85113
</section>
86114
</main>
87115
</template>
88116

89117
<script>
90-
import Chart from "chart.js";
91-
92-
Chart.plugins.register({
93-
afterDraw: function(chart) {
94-
if (chart.data.datasets[0].data.reduce((acc, it) => acc + it, 0) === 0) {
95-
var ctx = chart.chart.ctx;
96-
var width = chart.chart.width;
97-
var height = chart.chart.height
98-
chart.clear();
99-
100-
ctx.save();
101-
ctx.textAlign = 'center';
102-
ctx.textBaseline = 'middle';
103-
ctx.font = "16px normal 'Helvetica Nueue'";
104-
ctx.fillText(`No ${chart.options.title.text}`, width / 2, height / 2);
105-
ctx.restore();
106-
}
107-
}
108-
});
118+
import Tile from "../components/Tile";
119+
import EntityStateDoughnut from "../components/EntityStateDoughnut";
109120
110121
export default {
111122
name: "home",
123+
components: {
124+
Tile,
125+
EntityStateDoughnut
126+
},
112127
data: () => ({
113128
entrypoints: [],
114129
overview: {
115-
features: []
116-
},
117-
charts: {
118-
http: {
119-
routers: null,
120-
middlewares: null,
121-
services: null
122-
},
123-
tcp: {
124-
routers: null,
125-
services: null
126-
}
130+
features: [],
131+
providers: []
127132
},
128133
interval: null
129134
}),
130135
methods: {
131-
buildDoughnutChart(
132-
selector,
133-
entity = { errors: 2, warnings: 2, total: 6 },
134-
name
135-
) {
136-
return new Chart(this.$el.querySelector(selector), {
137-
type: "doughnut",
138-
data: {
139-
datasets: [
140-
{
141-
data: [
142-
entity.errors,
143-
entity.warnings,
144-
entity.total - (entity.errors + entity.warnings)
145-
],
146-
backgroundColor: [
147-
"hsl(348, 100%, 61%)",
148-
"hsl(48, 100%, 67%)",
149-
"hsl(141, 71%, 48%)"
150-
]
151-
}
152-
],
153-
labels: ["errors", "warnings", "success"]
154-
},
155-
options: {
156-
title: {
157-
display: true,
158-
text: name
159-
},
160-
legend: {
161-
display: false
162-
}
163-
}
164-
});
165-
},
166136
fetchOverview() {
167137
return fetch("/api/overview")
168138
.then(response => response.json())
169-
.then(response => (this.overview = response))
170-
.then(() => {
171-
this.charts = {
172-
http: {
173-
routers: this.buildDoughnutChart(
174-
"#http-routers",
175-
this.overview.http.routers,
176-
"Routers"
177-
),
178-
middlewares: this.buildDoughnutChart(
179-
"#http-middlewares",
180-
this.overview.http.middlewares,
181-
"Middlewares"
182-
),
183-
services: this.buildDoughnutChart(
184-
"#http-services",
185-
this.overview.http.services,
186-
"Services"
187-
)
188-
},
189-
tcp: {
190-
routers: this.buildDoughnutChart(
191-
"#tcp-routers",
192-
this.overview.tcp.routers,
193-
"Routers"
194-
),
195-
services: this.buildDoughnutChart(
196-
"#tcp-services",
197-
this.overview.tcp.services,
198-
"Services"
199-
)
200-
}
201-
};
202-
});
139+
.then(response => (this.overview = response));
203140
},
204141
fetchEntrypoints() {
205142
return fetch("/api/entrypoints")

0 commit comments

Comments
 (0)