WIP: Improve admin dashboard
This commit is contained in:
		
							parent
							
								
									fd9c7d525a
								
							
						
					
					
						commit
						b5fe4ba9be
					
				src/client
|  | @ -84,7 +84,7 @@ | ||||||
| 	</div> | 	</div> | ||||||
| 
 | 
 | ||||||
| 	<section class="_card"> | 	<section class="_card"> | ||||||
| 		<div class="_title"><fa :icon="faChartBar"/> {{ $t('statistics') }}</div> | 		<div class="_title" style="position: relative;"><fa :icon="faChartBar"/> {{ $t('statistics') }}<button @click="fetchChart" class="_button" style="position: absolute; right: 0; bottom: 0; top: 0; padding: inherit;"><fa :icon="faSync"/></button></div> | ||||||
| 		<div class="_content" style="margin-top: -8px;"> | 		<div class="_content" style="margin-top: -8px;"> | ||||||
| 			<div class="selects" style="display: flex;"> | 			<div class="selects" style="display: flex;"> | ||||||
| 				<mk-select v-model="chartSrc" style="margin: 0; flex: 1;"> | 				<mk-select v-model="chartSrc" style="margin: 0; flex: 1;"> | ||||||
|  | @ -123,7 +123,7 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import Vue from 'vue'; | import Vue from 'vue'; | ||||||
| import { faChartBar, faUser, faPencilAlt } from '@fortawesome/free-solid-svg-icons'; | import { faChartBar, faUser, faPencilAlt, faSync } from '@fortawesome/free-solid-svg-icons'; | ||||||
| import Chart from 'chart.js'; | import Chart from 'chart.js'; | ||||||
| import MkSelect from './ui/select.vue'; | import MkSelect from './ui/select.vue'; | ||||||
| 
 | 
 | ||||||
|  | @ -171,7 +171,7 @@ export default Vue.extend({ | ||||||
| 			chartInstance: null, | 			chartInstance: null, | ||||||
| 			chartSrc: 'notes', | 			chartSrc: 'notes', | ||||||
| 			chartSpan: 'hour', | 			chartSpan: 'hour', | ||||||
| 			faChartBar, faUser, faPencilAlt | 			faChartBar, faUser, faPencilAlt, faSync | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -220,6 +220,11 @@ export default Vue.extend({ | ||||||
| 
 | 
 | ||||||
| 		this.now = new Date(); | 		this.now = new Date(); | ||||||
| 
 | 
 | ||||||
|  | 		this.fetchChart(); | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	methods: { | ||||||
|  | 		async fetchChart() { | ||||||
| 			const [perHour, perDay] = await Promise.all([Promise.all([ | 			const [perHour, perDay] = await Promise.all([Promise.all([ | ||||||
| 				this.$root.api('charts/federation', { limit: this.chartLimit, span: 'hour' }), | 				this.$root.api('charts/federation', { limit: this.chartLimit, span: 'hour' }), | ||||||
| 				this.$root.api('charts/users', { limit: this.chartLimit, span: 'hour' }), | 				this.$root.api('charts/users', { limit: this.chartLimit, span: 'hour' }), | ||||||
|  | @ -265,7 +270,6 @@ export default Vue.extend({ | ||||||
| 			this.renderChart(); | 			this.renderChart(); | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 	methods: { |  | ||||||
| 		renderChart() { | 		renderChart() { | ||||||
| 			if (this.chartInstance) { | 			if (this.chartInstance) { | ||||||
| 				this.chartInstance.destroy(); | 				this.chartInstance.destroy(); | ||||||
|  |  | ||||||
|  | @ -2,11 +2,13 @@ | ||||||
| <div class="ukygtjoj _panel" :class="{ naked, hideHeader: !showHeader, scrollable, closed: !showBody }" v-size="{ max: [380], el: resizeBaseEl }"> | <div class="ukygtjoj _panel" :class="{ naked, hideHeader: !showHeader, scrollable, closed: !showBody }" v-size="{ max: [380], el: resizeBaseEl }"> | ||||||
| 	<header v-if="showHeader" ref="header"> | 	<header v-if="showHeader" ref="header"> | ||||||
| 		<div class="title"><slot name="header"></slot></div> | 		<div class="title"><slot name="header"></slot></div> | ||||||
|  | 		<div class="sub"> | ||||||
| 			<slot name="func"></slot> | 			<slot name="func"></slot> | ||||||
| 			<button class="_button" v-if="bodyTogglable" @click="() => showBody = !showBody"> | 			<button class="_button" v-if="bodyTogglable" @click="() => showBody = !showBody"> | ||||||
| 				<template v-if="showBody"><fa :icon="faAngleUp"/></template> | 				<template v-if="showBody"><fa :icon="faAngleUp"/></template> | ||||||
| 				<template v-else><fa :icon="faAngleDown"/></template> | 				<template v-else><fa :icon="faAngleDown"/></template> | ||||||
| 			</button> | 			</button> | ||||||
|  | 		</div> | ||||||
| 	</header> | 	</header> | ||||||
| 	<transition name="container-toggle" | 	<transition name="container-toggle" | ||||||
| 		@enter="enter" | 		@enter="enter" | ||||||
|  | @ -153,16 +155,19 @@ export default Vue.extend({ | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		> button { | 		> .sub { | ||||||
| 			position: absolute; | 			position: absolute; | ||||||
| 			z-index: 2; | 			z-index: 2; | ||||||
| 			top: 0; | 			top: 0; | ||||||
| 			right: 0; | 			right: 0; | ||||||
| 			padding: 0; | 			height: 100%; | ||||||
|  | 
 | ||||||
|  | 			> button { | ||||||
| 				width: 42px; | 				width: 42px; | ||||||
| 				height: 100%; | 				height: 100%; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	> div { | 	> div { | ||||||
| 		> ::v-deep ._content { | 		> ::v-deep ._content { | ||||||
|  |  | ||||||
|  | @ -1,6 +1,8 @@ | ||||||
| <template> | <template> | ||||||
| <mk-container :body-togglable="false"> | <mk-container :body-togglable="false"> | ||||||
| 	<template #header><slot name="title"></slot></template> | 	<template #header><slot name="title"></slot></template> | ||||||
|  | 	<template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> | ||||||
|  | 
 | ||||||
| 	<div class="_content _table"> | 	<div class="_content _table"> | ||||||
| 		<div class="_row"> | 		<div class="_row"> | ||||||
| 			<div class="_cell"><div class="_label">Process</div>{{ activeSincePrevTick | number }}</div> | 			<div class="_cell"><div class="_label">Process</div>{{ activeSincePrevTick | number }}</div> | ||||||
|  | @ -18,6 +20,7 @@ | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import Vue from 'vue'; | import Vue from 'vue'; | ||||||
| import Chart from 'chart.js'; | import Chart from 'chart.js'; | ||||||
|  | import { faPlay, faPause } from '@fortawesome/free-solid-svg-icons'; | ||||||
| import MkContainer from '../../components/ui/container.vue'; | import MkContainer from '../../components/ui/container.vue'; | ||||||
| 
 | 
 | ||||||
| const alpha = (hex, a) => { | const alpha = (hex, a) => { | ||||||
|  | @ -49,6 +52,8 @@ export default Vue.extend({ | ||||||
| 			active: 0, | 			active: 0, | ||||||
| 			waiting: 0, | 			waiting: 0, | ||||||
| 			delayed: 0, | 			delayed: 0, | ||||||
|  | 			paused: false, | ||||||
|  | 			faPlay, faPause | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -155,6 +160,7 @@ export default Vue.extend({ | ||||||
| 
 | 
 | ||||||
| 	methods: { | 	methods: { | ||||||
| 		onStats(stats) { | 		onStats(stats) { | ||||||
|  | 			if (this.paused) return; | ||||||
| 			this.activeSincePrevTick = stats[this.domain].activeSincePrevTick; | 			this.activeSincePrevTick = stats[this.domain].activeSincePrevTick; | ||||||
| 			this.active = stats[this.domain].active; | 			this.active = stats[this.domain].active; | ||||||
| 			this.waiting = stats[this.domain].waiting; | 			this.waiting = stats[this.domain].waiting; | ||||||
|  | @ -179,6 +185,14 @@ export default Vue.extend({ | ||||||
| 				this.onStats(stats); | 				this.onStats(stats); | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|  | 
 | ||||||
|  | 		pause() { | ||||||
|  | 			this.paused = true; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		resume() { | ||||||
|  | 			this.paused = false; | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -53,6 +53,7 @@ | ||||||
| 		<div class="segusily"> | 		<div class="segusily"> | ||||||
| 			<mk-container :body-togglable="false" :resize-base-el="() => $el"> | 			<mk-container :body-togglable="false" :resize-base-el="() => $el"> | ||||||
| 				<template #header><fa :icon="faMicrochip"/>{{ $t('cpuAndMemory') }}</template> | 				<template #header><fa :icon="faMicrochip"/>{{ $t('cpuAndMemory') }}</template> | ||||||
|  | 				<template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> | ||||||
| 
 | 
 | ||||||
| 				<div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> | 				<div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> | ||||||
| 					<canvas ref="cpumem"></canvas> | 					<canvas ref="cpumem"></canvas> | ||||||
|  | @ -72,8 +73,10 @@ | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 			</mk-container> | 			</mk-container> | ||||||
|  | 
 | ||||||
| 			<mk-container :body-togglable="false" :resize-base-el="() => $el"> | 			<mk-container :body-togglable="false" :resize-base-el="() => $el"> | ||||||
| 				<template #header><fa :icon="faHdd"/> {{ $t('disk') }}</template> | 				<template #header><fa :icon="faHdd"/> {{ $t('disk') }}</template> | ||||||
|  | 				<template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> | ||||||
| 
 | 
 | ||||||
| 				<div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> | 				<div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> | ||||||
| 					<canvas ref="disk"></canvas> | 					<canvas ref="disk"></canvas> | ||||||
|  | @ -88,8 +91,10 @@ | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 			</mk-container> | 			</mk-container> | ||||||
|  | 
 | ||||||
| 			<mk-container :body-togglable="false" :resize-base-el="() => $el"> | 			<mk-container :body-togglable="false" :resize-base-el="() => $el"> | ||||||
| 				<template #header><fa :icon="faExchangeAlt"/> {{ $t('network') }}</template> | 				<template #header><fa :icon="faExchangeAlt"/> {{ $t('network') }}</template> | ||||||
|  | 				<template #func><button class="_button" @click="resume" :disabled="!paused"><fa :icon="faPlay"/></button><button class="_button" @click="pause" :disabled="paused"><fa :icon="faPause"/></button></template> | ||||||
| 
 | 
 | ||||||
| 				<div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> | 				<div class="_content" style="margin-top: -8px; margin-bottom: -12px;"> | ||||||
| 					<canvas ref="net"></canvas> | 					<canvas ref="net"></canvas> | ||||||
|  | @ -180,7 +185,7 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import Vue from 'vue'; | import Vue from 'vue'; | ||||||
| import { faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList } from '@fortawesome/free-solid-svg-icons'; | import { faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList } from '@fortawesome/free-solid-svg-icons'; | ||||||
| import Chart from 'chart.js'; | import Chart from 'chart.js'; | ||||||
| import VueJsonPretty from 'vue-json-pretty'; | import VueJsonPretty from 'vue-json-pretty'; | ||||||
| import MkInstanceStats from '../../components/instance-stats.vue'; | import MkInstanceStats from '../../components/instance-stats.vue'; | ||||||
|  | @ -240,7 +245,8 @@ export default Vue.extend({ | ||||||
| 			dbInfo: null, | 			dbInfo: null, | ||||||
| 			overviewHeight: '1fr', | 			overviewHeight: '1fr', | ||||||
| 			queueHeight: '1fr', | 			queueHeight: '1fr', | ||||||
| 			faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList, | 			paused: false, | ||||||
|  | 			faPlay, faPause, faDatabase, faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt, faInfoCircle, faExclamationTriangle, faTachometerAlt, faHeartbeat, faClipboardList, | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -580,6 +586,8 @@ export default Vue.extend({ | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
| 		onStats(stats) { | 		onStats(stats) { | ||||||
|  | 			if (this.paused) return; | ||||||
|  | 
 | ||||||
| 			const cpu = (stats.cpu * 100).toFixed(0); | 			const cpu = (stats.cpu * 100).toFixed(0); | ||||||
| 			const memActive = (stats.mem.active / this.serverInfo.mem.total * 100).toFixed(0); | 			const memActive = (stats.mem.active / this.serverInfo.mem.total * 100).toFixed(0); | ||||||
| 			const memUsed = (stats.mem.used / this.serverInfo.mem.total * 100).toFixed(0); | 			const memUsed = (stats.mem.used / this.serverInfo.mem.total * 100).toFixed(0); | ||||||
|  | @ -616,7 +624,15 @@ export default Vue.extend({ | ||||||
| 			for (const stats of [...statsLog].reverse()) { | 			for (const stats of [...statsLog].reverse()) { | ||||||
| 				this.onStats(stats); | 				this.onStats(stats); | ||||||
| 			} | 			} | ||||||
| 		} | 		}, | ||||||
|  | 
 | ||||||
|  | 		pause() { | ||||||
|  | 			this.paused = true; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		resume() { | ||||||
|  | 			this.paused = false; | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue