Make stats limit configurable

This commit is contained in:
Torjus Håkestad 2021-04-10 17:44:02 +02:00
parent be1d3f6da4
commit ef92e31195
4 changed files with 102 additions and 9 deletions

View File

@ -7,6 +7,7 @@ module.exports = {
'plugin:vue/essential', 'plugin:vue/essential',
'@vue/airbnb', '@vue/airbnb',
'@vue/typescript/recommended', '@vue/typescript/recommended',
'prettier'
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2020, ecmaVersion: 2020,

View File

@ -38,6 +38,7 @@
"@vue/eslint-config-airbnb": "^5.0.2", "@vue/eslint-config-airbnb": "^5.0.2",
"@vue/eslint-config-typescript": "^7.0.0", "@vue/eslint-config-typescript": "^7.0.0",
"eslint": "^6.7.2", "eslint": "^6.7.2",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.20.2",
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^6.2.2",
"sass": "^1.26.5", "sass": "^1.26.5",
@ -45,4 +46,4 @@
"typescript": "~4.1.5", "typescript": "~4.1.5",
"vue-template-compiler": "^2.6.11" "vue-template-compiler": "^2.6.11"
} }
} }

View File

@ -1,14 +1,55 @@
<template> <template>
<div class="stats-container" :class="containerClass"> <div class="stats-container" :class="containerClass">
<h2>{{ title() }}</h2> <h2>
{{ title() }}
<b-icon :id="chartSettingsID()" icon="gear-fill"></b-icon>
</h2>
<div class="chart-container"> <div class="chart-container">
<canvas :id="chartID()" widht="400" height="400"></canvas> <canvas :id="chartID()" widht="400" height="400"></canvas>
</div> </div>
<b-popover
:target="chartSettingsID()"
triggers="click"
:show.sync="settingsShow"
placement="auto"
container="stats-container"
ref="popover"
@show="onShow"
>
<template #title>
<b-button @click="onClose" class="close" aria-label="Close">
<span class="d-inline-block" aria-hidden="true">&times;</span>
</b-button>
Settings
</template>
<div>
<b-form-group
label="Limit"
label-for="limitinput"
label-cols="3"
class="mb-1"
description="Limit number of items"
invalid-feedback="This field is required"
>
<b-form-input
ref="limitinput"
id="limitinput"
v-model="limitState"
type="number"
size="sm"
></b-form-input>
</b-form-group>
<b-button @click="onClose" size="sm" variant="danger">Cancel</b-button>
<b-button @click="onOk" size="sm" variant="primary">Ok</b-button>
</div>
</b-popover>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { StatResult } from '@/apiary/apiary'; import { StatResult } from '@/apiary/apiary';
import axios from 'axios'; import axios from 'axios';
import Chart from 'chart.js/auto'; import Chart from 'chart.js/auto';
@ -20,23 +61,31 @@ export type StatType = 'username' | 'password' | 'ip' | 'country' | 'total';
export default class StatsPie extends Vue { export default class StatsPie extends Vue {
@Prop() private statType!: StatType; @Prop() private statType!: StatType;
limit: number;
limitState: number;
settingsShow = false;
stats: StatResult[]; stats: StatResult[];
constructor() { constructor() {
super(); super();
this.stats = []; this.stats = [];
this.limit = 10;
this.limitState = 10;
} }
title(): string { title(): string {
switch (this.statType) { switch (this.statType) {
case 'password': case 'password':
return 'Top 10 Passwords'; return `Top ${this.limit} Passwords`;
case 'username': case 'username':
return 'Top 10 Usernames'; return `Top ${this.limit} Usernames`;
case 'ip': case 'ip':
return 'Top 10 IPs'; return `Top ${this.limit} IPs`;
case 'country': case 'country':
return 'Top 10 Countries'; return `Top ${this.limit} Countries`;
case 'total': case 'total':
return 'Totals'; return 'Totals';
// Why doesn't eslint know that this switch is exhaustive? // Why doesn't eslint know that this switch is exhaustive?
@ -53,14 +102,48 @@ export default class StatsPie extends Vue {
return `chart-${this.statType}`; return `chart-${this.statType}`;
} }
mounted(): void { chartSettingsID(): string {
const url = `/api/stats?type=${this.statType}&limit=10`; return `chartsettings-${this.statType}`;
}
settingsID(): string {
return `settings-${this.statType}`;
}
onClose(): void {
this.settingsShow = false;
}
onOk(): void {
if (this.limitState) {
this.limit = this.limitState;
this.settingsShow = false;
}
}
onShow(): void {
// This is called just before the popover is shown
// Reset our popover form variables
this.limitState = this.limit;
}
@Watch('limit')
limitChanged(value: number, oldValue: number): void {
this.fetchData();
}
fetchData(): void {
const url = `/api/stats?type=${this.statType}&limit=${this.limit}`;
axios.get<StatResult[]>(url).then((resp) => { axios.get<StatResult[]>(url).then((resp) => {
this.stats = resp.data; this.stats = resp.data;
this.renderPie(); this.renderPie();
}); });
} }
mounted(): void {
this.fetchData();
}
renderPie(): void { renderPie(): void {
const elem = document.getElementById(this.chartID()) as HTMLCanvasElement; const elem = document.getElementById(this.chartID()) as HTMLCanvasElement;
const ctx = elem.getContext('2d') as CanvasRenderingContext2D; const ctx = elem.getContext('2d') as CanvasRenderingContext2D;
@ -87,6 +170,9 @@ export default class StatsPie extends Vue {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.stats-container {
align-content: center;
}
.chart-container { .chart-container {
max-width: 500px; max-width: 500px;
max-height: 500px; max-height: 500px;

View File

@ -4301,6 +4301,11 @@ eslint-config-airbnb-base@^14.0.0:
object.assign "^4.1.2" object.assign "^4.1.2"
object.entries "^1.1.2" object.entries "^1.1.2"
eslint-config-prettier@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz#4ef1eaf97afe5176e6a75ddfb57c335121abc5a6"
integrity sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==
eslint-import-resolver-node@^0.3.4: eslint-import-resolver-node@^0.3.4:
version "0.3.4" version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"