summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-08-24 05:37:19 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-08-24 05:37:19 +0900
commit4dee7d91b17148c5c3ee12c3bee193fccaeb22b6 (patch)
treec31c27be3f8e60aa57168e5adaa0cd6bb73527f6 /src
parentMerge pull request #2435 from syuilo/master (diff)
downloadsharkey-4dee7d91b17148c5c3ee12c3bee193fccaeb22b6.tar.gz
sharkey-4dee7d91b17148c5c3ee12c3bee193fccaeb22b6.tar.bz2
sharkey-4dee7d91b17148c5c3ee12c3bee193fccaeb22b6.zip
Better charts
Diffstat (limited to 'src')
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.chart.chart.ts40
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.chart.vue195
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue74
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.drive-chart.vue34
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue99
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.notes-chart.vue34
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue74
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.users-chart.vue34
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.vue12
-rw-r--r--src/server/api/endpoints/admin/chart.ts3
10 files changed, 239 insertions, 360 deletions
diff --git a/src/client/app/desktop/views/pages/admin/admin.chart.chart.ts b/src/client/app/desktop/views/pages/admin/admin.chart.chart.ts
new file mode 100644
index 0000000000..62043b21dc
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.chart.chart.ts
@@ -0,0 +1,40 @@
+import Vue from 'vue';
+import { Line } from 'vue-chartjs';
+
+export default Vue.extend({
+ extends: Line,
+ props: {
+ data: {
+ required: true
+ },
+ opts: {
+ required: false
+ }
+ },
+ watch: {
+ data() {
+ this.render();
+ }
+ },
+ mounted() {
+ this.render();
+ },
+ methods: {
+ render() {
+ this.renderChart(this.data, Object.assign({
+ responsive: false,
+ scales: {
+ xAxes: [{
+ type: 'time',
+ time: {
+ displayFormats: {
+ quarter: 'YYYY/MM/D h:mm'
+ }
+ },
+ distribution: 'series'
+ }]
+ }
+ }, this.opts || {}));
+ }
+ }
+});
diff --git a/src/client/app/desktop/views/pages/admin/admin.chart.vue b/src/client/app/desktop/views/pages/admin/admin.chart.vue
new file mode 100644
index 0000000000..c92caeb2f0
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.chart.vue
@@ -0,0 +1,195 @@
+<template>
+<div class="card gkgckalzgidaygcxnugepioremxvxvpt">
+ <header>
+ <b>%i18n:@title%:</b>
+ <select v-model="chartType">
+ <option value="local-users">%i18n:@local-users%</option>
+ <option value="remote-users">%i18n:@remote-users%</option>
+ <option value="local-users-total">%i18n:@local-users-total%</option>
+ <option value="remote-users-total">%i18n:@remote-users-total%</option>
+ <option value="local-notes">%i18n:@local-notes%</option>
+ <option value="remote-notes">%i18n:@remote-notes%</option>
+ <option value="local-notes-total">%i18n:@local-notes-total%</option>
+ <option value="remote-notes-total">%i18n:@remote-notes-total%</option>
+ <option value="local-drive">%i18n:@local-drive%</option>
+ <option value="remote-drive">%i18n:@remote-drive%</option>
+ <option value="local-drive-total">%i18n:@local-drive-total%</option>
+ <option value="remote-drive-total">%i18n:@remote-drive-total%</option>
+ </select>
+ <div>
+ <a @click="span = 'day'">Per DAY</a> | <a @click="span = 'hour'">Per HOUR</a>
+ </div>
+ </header>
+ <x-chart v-if="chart" :data="data[0]" :opts="data[1]" :width="720" :height="300"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import XChart from './admin.chart.chart.ts';
+
+export default Vue.extend({
+ components: {
+ XChart
+ },
+ props: {
+ chart: {
+ required: true
+ }
+ },
+ data() {
+ return {
+ chartType: 'local-notes',
+ span: 'hour'
+ };
+ },
+ computed: {
+ data(): any {
+ if (this.chart == null) return null;
+ switch (this.chartType) {
+ case 'local-users': return this.usersChart(true, false);
+ case 'remote-users': return this.usersChart(false, false);
+ case 'local-users-total': return this.usersChart(true, true);
+ case 'remote-users-total': return this.usersChart(false, true);
+ case 'local-notes': return this.notesChart(true);
+ case 'remote-notes': return this.notesChart(false);
+ case 'local-notes-total': return this.notesTotalChart(true);
+ case 'remote-notes-total': return this.notesTotalChart(false);
+ case 'local-drive': return this.driveChart(true, false);
+ case 'remote-drive': return this.driveChart(false, false);
+ case 'local-drive-total': return this.driveChart(true, true);
+ case 'remote-drive-total': return this.driveChart(false, true);
+ }
+ },
+ stats(): any[] {
+ return (
+ this.span == 'day' ? this.chart.perDay :
+ this.span == 'hour' ? this.chart.perHour :
+ null
+ );
+ }
+ },
+ methods: {
+ notesChart(local: boolean): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ normal: local ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal,
+ reply: local ? x.notes.local.diffs.reply : x.notes.remote.diffs.reply,
+ renote: local ? x.notes.local.diffs.renote : x.notes.remote.diffs.renote,
+ total: local ? x.notes.local.diff : x.notes.remote.diff
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Normal',
+ fill: false,
+ borderColor: '#41ddde',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.normal }))
+ }, {
+ label: 'Replies',
+ fill: false,
+ borderColor: '#f7796c',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.reply }))
+ }, {
+ label: 'Renotes',
+ fill: false,
+ borderColor: '#a1de41',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.renote }))
+ }]
+ }];
+ },
+ notesTotalChart(local: boolean): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ count: local ? x.notes.local.total : x.notes.remote.total,
+ }));
+
+ return [{
+ datasets: [{
+ label: local ? 'Local Notes' : 'Remote Notes',
+ fill: false,
+ borderColor: '#f6584f',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.count }))
+ }]
+ }];
+ },
+ usersChart(local: boolean, total: boolean): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ count: local ?
+ total ? x.users.local.total : x.users.local.diff :
+ total ? x.users.remote.total : x.users.remote.diff
+ }));
+
+ return [{
+ datasets: [{
+ label: local ? 'Local Users' : 'Remote Users',
+ fill: false,
+ borderColor: '#f6584f',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.count }))
+ }]
+ }];
+ },
+ driveChart(local: boolean, total: boolean): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ count: local ?
+ total ? x.drive.local.totalSize : x.drive.local.diffSize :
+ total ? x.drive.remote.totalSize : x.drive.remote.diffSize
+ }));
+
+ return [{
+ datasets: [{
+ label: local ? 'Local Drive Usage' : 'Remote Drive Usage',
+ fill: false,
+ borderColor: '#f6584f',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.count }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: (value) => {
+ return Vue.filter('bytes')(value);
+ }
+ }
+ }]
+ }
+ }];
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.gkgckalzgidaygcxnugepioremxvxvpt
+ > header
+ display flex
+
+ > b
+ margin-right 8px
+
+ > *:last-child
+ margin-left auto
+
+</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue
deleted file mode 100644
index 97f05571c3..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<template>
-<div>
- <a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
- <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
- <polyline
- :points="points"
- fill="none"
- stroke-width="0.3"
- stroke="#555"/>
- </svg>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- chart: {
- required: true
- },
- type: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- viewBoxX: 100,
- viewBoxY: 30,
- points: null,
- span: 'day'
- };
- },
- computed: {
- stats(): any[] {
- return (
- this.span == 'day' ? this.chart.perDay :
- this.span == 'hour' ? this.chart.perHour :
- null
- );
- }
- },
- watch: {
- stats() {
- this.render();
- }
- },
- mounted() {
- this.render();
- },
- methods: {
- render() {
- const peak = Math.max.apply(null, this.stats.map(d => this.type == 'local' ? d.drive.local.totalSize : d.drive.remote.totalSize));
-
- if (peak != 0) {
- const data = this.stats.slice().reverse().map(x => ({
- size: this.type == 'local' ? x.drive.local.totalSize : x.drive.remote.totalSize
- }));
-
- this.points = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.size / peak)) * this.viewBoxY}`).join(' ');
- }
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-svg
- display block
- padding 10px
- width 100%
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.drive-chart.vue b/src/client/app/desktop/views/pages/admin/admin.drive-chart.vue
deleted file mode 100644
index 4f94fd2372..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.drive-chart.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-<div class="card">
- <header>%i18n:@title%</header>
- <div class="card">
- <header>%i18n:@local%</header>
- <x-chart v-if="chart" :chart="chart" type="local"/>
- </div>
- <div class="card">
- <header>%i18n:@remote%</header>
- <x-chart v-if="chart" :chart="chart" type="remote"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from "vue";
-import XChart from "./admin.drive-chart.chart.vue";
-
-export default Vue.extend({
- components: {
- XChart
- },
- props: {
- chart: {
- required: true
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue
deleted file mode 100644
index fabb3f1bd1..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue
+++ /dev/null
@@ -1,99 +0,0 @@
-<template>
-<div>
- <a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
- <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
- <polyline
- :points="pointsNote"
- fill="none"
- stroke-width="0.3"
- stroke="#41ddde"/>
- <polyline
- :points="pointsReply"
- fill="none"
- stroke-width="0.3"
- stroke="#f7796c"/>
- <polyline
- :points="pointsRenote"
- fill="none"
- stroke-width="0.3"
- stroke="#a1de41"/>
- <polyline
- :points="pointsTotal"
- fill="none"
- stroke-width="0.3"
- stroke="#555"
- stroke-dasharray="1 1"/>
- </svg>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- chart: {
- required: true
- },
- type: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- viewBoxX: 100,
- viewBoxY: 30,
- pointsNote: null,
- pointsReply: null,
- pointsRenote: null,
- pointsTotal: null,
- span: 'day'
- };
- },
- computed: {
- stats(): any[] {
- return (
- this.span == 'day' ? this.chart.perDay :
- this.span == 'hour' ? this.chart.perHour :
- null
- );
- }
- },
- watch: {
- stats() {
- this.render();
- }
- },
- mounted() {
- this.render();
- },
- methods: {
- render() {
- const peak = Math.max.apply(null, this.stats.map(d => this.type == 'local' ? d.notes.local.diff : d.notes.remote.diff));
-
- if (peak != 0) {
- const data = this.stats.slice().reverse().map(x => ({
- normal: this.type == 'local' ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal,
- reply: this.type == 'local' ? x.notes.local.diffs.reply : x.notes.remote.diffs.reply,
- renote: this.type == 'local' ? x.notes.local.diffs.renote : x.notes.remote.diffs.renote,
- total: this.type == 'local' ? x.notes.local.diff : x.notes.remote.diff
- }));
-
- this.pointsNote = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.normal / peak)) * this.viewBoxY}`).join(' ');
- this.pointsReply = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.reply / peak)) * this.viewBoxY}`).join(' ');
- this.pointsRenote = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.renote / peak)) * this.viewBoxY}`).join(' ');
- this.pointsTotal = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
- }
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-svg
- display block
- padding 10px
- width 100%
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue b/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue
deleted file mode 100644
index e4d396d9c6..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-<div class="card">
- <header>%i18n:@title%</header>
- <div class="card">
- <header>%i18n:@local%</header>
- <x-chart v-if="chart" :chart="chart" type="local"/>
- </div>
- <div class="card">
- <header>%i18n:@remote%</header>
- <x-chart v-if="chart" :chart="chart" type="remote"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from "vue";
-import XChart from "./admin.notes-chart.chart.vue";
-
-export default Vue.extend({
- components: {
- XChart
- },
- props: {
- chart: {
- required: true
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue
deleted file mode 100644
index 45ecc13929..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<template>
-<div>
- <a @click="span = 'day'">Per day</a> | <a @click="span = 'hour'">Per hour</a>
- <svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
- <polyline
- :points="points"
- fill="none"
- stroke-width="0.3"
- stroke="#555"/>
- </svg>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- chart: {
- required: true
- },
- type: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- viewBoxX: 100,
- viewBoxY: 30,
- points: null,
- span: 'day'
- };
- },
- computed: {
- stats(): any[] {
- return (
- this.span == 'day' ? this.chart.perDay :
- this.span == 'hour' ? this.chart.perHour :
- null
- );
- }
- },
- watch: {
- stats() {
- this.render();
- }
- },
- mounted() {
- this.render();
- },
- methods: {
- render() {
- const peak = Math.max.apply(null, this.stats.map(d => this.type == 'local' ? d.users.local.diff : d.users.remote.diff));
-
- if (peak != 0) {
- const data = this.stats.slice().reverse().map(x => ({
- count: this.type == 'local' ? x.users.local.diff : x.users.remote.diff
- }));
-
- this.points = data.map((d, i) => `${(this.viewBoxX / data.length) * i},${(1 - (d.count / peak)) * this.viewBoxY}`).join(' ');
- }
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-svg
- display block
- padding 10px
- width 100%
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.users-chart.vue b/src/client/app/desktop/views/pages/admin/admin.users-chart.vue
deleted file mode 100644
index e620012702..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.users-chart.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-<div class="card">
- <header>%i18n:@title%</header>
- <div class="card">
- <header>%i18n:@local%</header>
- <x-chart v-if="chart" :chart="chart" type="local"/>
- </div>
- <div class="card">
- <header>%i18n:@remote%</header>
- <x-chart v-if="chart" :chart="chart" type="remote"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from "vue";
-import XChart from "./admin.users-chart.chart.vue";
-
-export default Vue.extend({
- components: {
- XChart
- },
- props: {
- chart: {
- required: true
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index cbb1890cc3..066c1a4f4f 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -11,9 +11,7 @@
<main>
<div v-show="page == 'dashboard'">
<x-dashboard/>
- <x-users-chart :chart="chart"/>
- <x-notes-chart :chart="chart"/>
- <x-drive-chart :chart="chart"/>
+ <x-chart :chart="chart"/>
</div>
<div v-if="page == 'users'">
<x-suspend-user/>
@@ -34,9 +32,7 @@ import XSuspendUser from "./admin.suspend-user.vue";
import XUnsuspendUser from "./admin.unsuspend-user.vue";
import XVerifyUser from "./admin.verify-user.vue";
import XUnverifyUser from "./admin.unverify-user.vue";
-import XUsersChart from "./admin.users-chart.vue";
-import XNotesChart from "./admin.notes-chart.vue";
-import XDriveChart from "./admin.drive-chart.vue";
+import XChart from "./admin.chart.vue";
export default Vue.extend({
components: {
@@ -45,9 +41,7 @@ export default Vue.extend({
XUnsuspendUser,
XVerifyUser,
XUnverifyUser,
- XUsersChart,
- XNotesChart,
- XDriveChart
+ XChart
},
data() {
return {
diff --git a/src/server/api/endpoints/admin/chart.ts b/src/server/api/endpoints/admin/chart.ts
index 1897879d65..1b88af00bd 100644
--- a/src/server/api/endpoints/admin/chart.ts
+++ b/src/server/api/endpoints/admin/chart.ts
@@ -8,7 +8,7 @@ export const meta = {
};
export default (params: any) => new Promise(async (res, rej) => {
- const daysRange = 365;
+ const daysRange = 90;
const hoursRange = 24;
const now = new Date();
@@ -123,7 +123,6 @@ export default (params: any) => new Promise(async (res, rej) => {
}
chart.forEach(x => {
- delete x.date;
delete (x as any).span;
});