From 6f1e2f663676347b4d85209a292d5877a209dc61 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 3 Nov 2018 02:06:34 +0900
Subject: [PATCH] Improve admin page

---
 locales/ja-JP.yml                         |   8 +-
 src/client/app/admin/views/cpu-memory.vue | 169 +++++++++++-----------
 src/client/app/admin/views/dashboard.vue  |  89 ++++++++----
 3 files changed, 148 insertions(+), 118 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2bcab87838..17e860aee4 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1080,10 +1080,10 @@ admin/views/index.vue:
 
 admin/views/dashboard.vue:
   dashboard: "ダッシュボード"
-  all-users: "全てのユーザー"
-  original-users: "このインスタンスのユーザー"
-  all-notes: "全ての投稿"
-  original-notes: "このインスタンスの投稿"
+  accounts: "アカウント"
+  notes: "投稿"
+  this-instance: "このインスタンス"
+  federated: "連合"
   invite: "招待"
   banner-url: "Banner URL"
   disableRegistration: "Disable new user registration"
diff --git a/src/client/app/admin/views/cpu-memory.vue b/src/client/app/admin/views/cpu-memory.vue
index 5d03b30ef4..00126325b1 100644
--- a/src/client/app/admin/views/cpu-memory.vue
+++ b/src/client/app/admin/views/cpu-memory.vue
@@ -1,79 +1,38 @@
 <template>
 <div class="zyknedwtlthezamcjlolyusmipqmjgxz">
-	<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
-		<defs>
-			<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
-				<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
-				<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
-			</linearGradient>
-			<mask :id="cpuMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
-				<polygon
-					:points="cpuPolygonPoints"
-					fill="#fff"
-					fill-opacity="0.5"/>
-				<polyline
-					:points="cpuPolylinePoints"
-					fill="none"
-					stroke="#fff"
-					stroke-width="1"/>
-			</mask>
-		</defs>
-		<rect
-			x="0" y="0"
-			:width="viewBoxX" :height="viewBoxY"
-			:style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"/>
-		<text x="1" y="12">CPU <tspan>{{ cpuP }}%</tspan></text>
-	</svg>
-	<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
-		<defs>
-			<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
-				<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
-				<stop offset="100%" stop-color="hsl(0, 80%, 70%)"></stop>
-			</linearGradient>
-			<mask :id="memMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
-				<polygon
-					:points="memPolygonPoints"
-					fill="#fff"
-					fill-opacity="0.5"/>
-				<polyline
-					:points="memPolylinePoints"
-					fill="none"
-					stroke="#fff"
-					stroke-width="1"/>
-			</mask>
-		</defs>
-		<rect
-			x="0" y="0"
-			:width="viewBoxX" :height="viewBoxY"
-			:style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"/>
-		<text x="1" y="12">MEM <tspan>{{ memP }}%</tspan></text>
-	</svg>
+	<div ref="cpu"></div>
+	<div ref="mem"></div>
 </div>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
-import * as uuid from 'uuid';
+import * as ApexCharts from 'apexcharts';
 
 export default Vue.extend({
 	props: ['connection'],
+
 	data() {
 		return {
-			viewBoxX: 200,
-			viewBoxY: 70,
 			stats: [],
-			cpuGradientId: uuid(),
-			cpuMaskId: uuid(),
-			memGradientId: uuid(),
-			memMaskId: uuid(),
-			cpuPolylinePoints: '',
-			memPolylinePoints: '',
-			cpuPolygonPoints: '',
-			memPolygonPoints: '',
+			cpuChart: null,
+			memChart: null,
 			cpuP: '',
 			memP: ''
 		};
 	},
+
+	watch: {
+		stats(stats) {
+			this.cpuChart.updateSeries([{
+				data: stats.map((x, i) => ({ x: i, y: x.cpu_usage }))
+			}]);
+			this.memChart.updateSeries([{
+				data: stats.map((x, i) => ({ x: i, y: (x.mem.used / x.mem.total) }))
+			}]);
+		}
+	},
+
 	mounted() {
 		this.connection.on('stats', this.onStats);
 		this.connection.on('statsLog', this.onStatsLog);
@@ -81,27 +40,75 @@ export default Vue.extend({
 			id: Math.random().toString().substr(2, 8),
 			length: 200
 		});
+
+		const chartOpts = {
+			chart: {
+				type: 'area',
+				height: 300,
+				animations: {
+					dynamicAnimation: {
+						enabled: false
+					}
+				},
+				toolbar: {
+					show: false
+				}
+			},
+			dataLabels: {
+				enabled: false
+			},
+			grid: {
+				clipMarkers: false,
+			},
+			stroke: {
+				curve: 'straight',
+				width: 2
+			},
+			series: [{
+				data: []
+			}],
+			xaxis: {
+				type: 'numeric',
+				labels: {
+					show: false
+				}
+			},
+			yaxis: {
+				show: false,
+				min: 0,
+				max: 1
+			}
+		};
+
+		this.cpuChart = new ApexCharts(this.$refs.cpu, Object.assign({}, chartOpts, {
+			title: {
+				text: 'CPU'
+			}
+		}));
+		this.memChart = new ApexCharts(this.$refs.mem, Object.assign({}, chartOpts, {
+			title: {
+				text: 'MEM'
+			}
+		}));
+
+		this.cpuChart.render();
+		this.memChart.render();
 	},
+
 	beforeDestroy() {
 		this.connection.off('stats', this.onStats);
 		this.connection.off('statsLog', this.onStatsLog);
 	},
+
 	methods: {
 		onStats(stats) {
 			this.stats.push(stats);
 			if (this.stats.length > 200) this.stats.shift();
 
-			const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu_usage) * this.viewBoxY]);
-			const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.used / s.mem.total)) * this.viewBoxY]);
-			this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
-			this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
-
-			this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.cpuPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
-			this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${this.viewBoxY} ${this.memPolylinePoints} ${this.viewBoxX},${this.viewBoxY}`;
-
 			this.cpuP = (stats.cpu_usage * 100).toFixed(0);
 			this.memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
 		},
+
 		onStatsLog(statsLog) {
 			statsLog.reverse().forEach(stats => this.onStats(stats));
 		}
@@ -111,27 +118,17 @@ export default Vue.extend({
 
 <style lang="stylus" scoped>
 .zyknedwtlthezamcjlolyusmipqmjgxz
-	> svg
+	display flex
+
+	> div
 		display block
-		width 50%
-		float left
+		flex 1
+		padding 32px
+		box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
+		background var(--face)
+		border-radius 8px
 
 		&:first-child
-			padding-right 5px
-
-		&:last-child
-			padding-left 5px
-
-		> text
-			font-size 10px
-			fill var(--chartCaption)
-
-			> tspan
-				opacity 0.5
-
-	&:after
-		content ""
-		display block
-		clear both
+			margin-right 16px
 
 </style>
diff --git a/src/client/app/admin/views/dashboard.vue b/src/client/app/admin/views/dashboard.vue
index 5af5255e20..a48d184cf3 100644
--- a/src/client/app/admin/views/dashboard.vue
+++ b/src/client/app/admin/views/dashboard.vue
@@ -2,31 +2,51 @@
 <div class="obdskegsannmntldydackcpzezagxqfy">
 	<div v-if="stats" class="stats">
 		<div>
-			<div>%fa:user%</div>
 			<div>
-				<span>%i18n:@original-users%</span>
-				<b>{{ stats.originalUsersCount | number }}</b>
+				<div>%fa:user%</div>
+				<div>
+					<span>%i18n:@accounts%</span>
+					<b class="primary">{{ stats.originalUsersCount | number }}</b>
+				</div>
+			</div>
+			<div>
+				<span>%fa:home% %i18n:@this-instance%</span>
 			</div>
 		</div>
 		<div>
-			<div>%fa:pencil-alt%</div>
 			<div>
-				<span>%i18n:@original-notes%</span>
-				<b>{{ stats.originalNotesCount | number }}</b>
+				<div>%fa:pencil-alt%</div>
+				<div>
+					<span>%i18n:@notes%</span>
+					<b class="primary">{{ stats.originalNotesCount | number }}</b>
+				</div>
+			</div>
+			<div>
+				<span>%fa:home% %i18n:@this-instance%</span>
 			</div>
 		</div>
 		<div>
-			<div>%fa:user%</div>
 			<div>
-				<span>%i18n:@all-users%</span>
-				<b>{{ stats.usersCount | number }}</b>
+				<div>%fa:user%</div>
+				<div>
+					<span>%i18n:@accounts%</span>
+					<b>{{ stats.usersCount | number }}</b>
+				</div>
+			</div>
+			<div>
+				<span>%fa:globe% %i18n:@federated%</span>
 			</div>
 		</div>
 		<div>
-			<div>%fa:pencil-alt%</div>
 			<div>
-				<span>%i18n:@all-notes%</span>
-				<b>{{ stats.notesCount | number }}</b>
+				<div>%fa:pencil-alt%</div>
+				<div>
+					<span>%i18n:@notes%</span>
+					<b>{{ stats.notesCount | number }}</b>
+				</div>
+			</div>
+			<div>
+				<span>%fa:globe% %i18n:@federated%</span>
 			</div>
 		</div>
 	</div>
@@ -78,40 +98,53 @@ export default Vue.extend({
 		margin-bottom 16px
 
 		> div
-			display flex
-			align-items center
 			flex 1
 			max-width 300px
 			margin-right 16px
-			text-align center
 			color var(--text)
 			box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 			background var(--face)
 			border-radius 8px
 
 			&:last-child
-					margin-right 0
+				margin-right 0
 
 			> div:first-child
-				padding 16px 24px
-				font-size 28px
+				display flex
+				align-items center
+				text-align center
+
+				&:last-child
+					margin-right 0
+
+				> div:first-child
+					padding 16px 24px
+					font-size 28px
+
+				> div:last-child
+					flex 1
+					padding 16px 32px 16px 0
+					text-align right
+
+					> span
+						font-size 70%
+						opacity 0.7
+
+					> b
+						display block
+
+						&.primary
+							color var(--primary)
 
 			> div:last-child
-				flex 1
-				padding 16px 32px 16px 0
-				text-align right
+				padding 6px 16px
+				border-top solid 1px #eee
 
 				> span
+					font-size 70%
 					opacity 0.7
 
-				> b
-					display block
-
 	> .cpu-memory
 		margin-bottom 16px
-		padding 32px
-		box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
-		background var(--face)
-		border-radius 8px
 
 </style>