Improve stats view
This commit is contained in:
parent
28120637e1
commit
af6b022932
@ -1,26 +1,53 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
<h1>Stats!</h1>
|
<h1>Stats!</h1>
|
||||||
<stats-username />
|
<b-card no-body>
|
||||||
|
<b-tabs card>
|
||||||
|
<b-tab title="Totals">
|
||||||
|
<b-card-body>
|
||||||
|
<stats-pie statType="total" />
|
||||||
|
</b-card-body>
|
||||||
|
</b-tab>
|
||||||
|
<b-tab title="Usernames">
|
||||||
|
<b-card-body>
|
||||||
|
<stats-pie statType="username" />
|
||||||
|
</b-card-body>
|
||||||
|
</b-tab>
|
||||||
|
<b-tab title="Passwords">
|
||||||
|
<b-card-body>
|
||||||
|
<stats-pie statType="password"></stats-pie>
|
||||||
|
</b-card-body>
|
||||||
|
</b-tab>
|
||||||
|
<b-tab title="Countries">
|
||||||
|
<b-card-body>
|
||||||
|
<stats-pie statType="country"></stats-pie>
|
||||||
|
</b-card-body>
|
||||||
|
</b-tab>
|
||||||
|
<b-tab title="IPs">
|
||||||
|
<b-card-body>
|
||||||
|
<stats-pie statType="ip"></stats-pie>
|
||||||
|
</b-card-body>
|
||||||
|
</b-tab>
|
||||||
|
</b-tabs>
|
||||||
|
</b-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
import StatsUsername from '@/components/StatsUsername.vue';
|
import StatsUsername from '@/components/StatsUsername.vue';
|
||||||
|
import StatsPie from '@/components/StatsPie.vue';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
StatsUsername,
|
StatsUsername,
|
||||||
|
StatsPie,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class Stats extends Vue {
|
export default class Stats extends Vue {}
|
||||||
@Prop() private msg!: string;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.stats {
|
.stats {
|
||||||
align-content: center;
|
margin: 50px;
|
||||||
width: 70%;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
79
web/frontend/src/components/StatsPie.vue
Normal file
79
web/frontend/src/components/StatsPie.vue
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stats-container" :class="containerClass">
|
||||||
|
<h2>{{ title() }}</h2>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="chart" widht="400" height="400"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||||
|
import { StatResult } from '@/apiary/apiary';
|
||||||
|
import axios from 'axios';
|
||||||
|
import Chart from 'chart.js/auto';
|
||||||
|
import randomColor from 'randomcolor';
|
||||||
|
|
||||||
|
export type StatType = 'username' | 'password' | 'ip' | 'country' | 'total';
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class StatsPie extends Vue {
|
||||||
|
@Prop() private statType!: StatType;
|
||||||
|
|
||||||
|
stats: StatResult[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.stats = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
title(): string {
|
||||||
|
if (this.statType === 'total') {
|
||||||
|
return 'Totals';
|
||||||
|
}
|
||||||
|
return `Top 10 ${this.statType}s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
containerClass(): string {
|
||||||
|
return `stats-container-${this.statType}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted(): void {
|
||||||
|
const url = `/api/stats?type=${this.statType}&limit=10`;
|
||||||
|
axios.get<StatResult[]>(url).then((resp) => {
|
||||||
|
this.stats = resp.data;
|
||||||
|
this.renderPie();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPie(): void {
|
||||||
|
const elem = document.getElementById('chart') as HTMLCanvasElement;
|
||||||
|
const ctx = elem.getContext('2d') as CanvasRenderingContext2D;
|
||||||
|
const sortedStats = this.stats.sort();
|
||||||
|
const values = sortedStats.map((s) => s.count);
|
||||||
|
const headers = sortedStats.map((s) => s.name);
|
||||||
|
const colors = sortedStats.map(() => randomColor());
|
||||||
|
|
||||||
|
const chart = new Chart(ctx, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: headers,
|
||||||
|
options: {},
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: values,
|
||||||
|
backgroundColor: colors,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.chart-container {
|
||||||
|
max-width: 500px;
|
||||||
|
max-height: 500px;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user