enhance(client): tweak control panel dashboard
This commit is contained in:
		
							parent
							
								
									f071ea4902
								
							
						
					
					
						commit
						6a4574b612
					
				
					 4 changed files with 158 additions and 13 deletions
				
			
		|  | @ -2,14 +2,14 @@ | ||||||
| <div class="wbrkwale"> | <div class="wbrkwale"> | ||||||
| 	<MkLoading v-if="fetching"/> | 	<MkLoading v-if="fetching"/> | ||||||
| 	<transition-group v-else tag="div" :name="$store.state.animation ? 'chart' : ''" class="instances"> | 	<transition-group v-else tag="div" :name="$store.state.animation ? 'chart' : ''" class="instances"> | ||||||
| 		<div v-for="(instance, i) in instances" :key="instance.id" class="instance"> | 		<MkA v-for="(instance, i) in instances" :key="instance.id" :to="`/instance-info/${instance.host}`" class="instance"> | ||||||
| 			<img v-if="instance.iconUrl" :src="instance.iconUrl" alt=""/> | 			<img v-if="instance.iconUrl" :src="instance.iconUrl" alt=""/> | ||||||
| 			<div class="body"> | 			<div class="body"> | ||||||
| 				<a class="a" :href="'https://' + instance.host" target="_blank" :title="instance.host">{{ instance.name ?? instance.host }}</a> | 				<div class="name">{{ instance.name ?? instance.host }}</div> | ||||||
| 				<p>{{ instance.host }}</p> | 				<div class="host">{{ instance.host }}</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			<MkMiniChart class="chart" :src="charts[i].requests.received"/> | 			<MkMiniChart class="chart" :src="charts[i].requests.received"/> | ||||||
| 		</div> | 		</MkA> | ||||||
| 	</transition-group> | 	</transition-group> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
|  | @ -78,7 +78,7 @@ onUnmounted(() => { | ||||||
| 				color: var(--fg); | 				color: var(--fg); | ||||||
| 				padding-right: 8px; | 				padding-right: 8px; | ||||||
| 
 | 
 | ||||||
| 				> .a { | 				> .name { | ||||||
| 					display: block; | 					display: block; | ||||||
| 					width: 100%; | 					width: 100%; | ||||||
| 					white-space: nowrap; | 					white-space: nowrap; | ||||||
|  | @ -86,7 +86,7 @@ onUnmounted(() => { | ||||||
| 					text-overflow: ellipsis; | 					text-overflow: ellipsis; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				> p { | 				> .host { | ||||||
| 					margin: 0; | 					margin: 0; | ||||||
| 					font-size: 75%; | 					font-size: 75%; | ||||||
| 					opacity: 0.7; | 					opacity: 0.7; | ||||||
|  |  | ||||||
|  | @ -146,7 +146,7 @@ onMounted(() => { | ||||||
| 			}], | 			}], | ||||||
| 		}, | 		}, | ||||||
| 		options: { | 		options: { | ||||||
| 			aspectRatio: 2.5, | 			aspectRatio: 3, | ||||||
| 			layout: { | 			layout: { | ||||||
| 				padding: { | 				padding: { | ||||||
| 					left: 0, | 					left: 0, | ||||||
|  |  | ||||||
							
								
								
									
										76
									
								
								packages/client/src/pages/admin/overview.user.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								packages/client/src/pages/admin/overview.user.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,76 @@ | ||||||
|  | <template> | ||||||
|  | <MkA :class="[$style.root]" :to="`/user-info/${user.id}`"> | ||||||
|  | 	<MkAvatar class="avatar" :user="user" :disable-link="true" :show-indicator="true"/> | ||||||
|  | 	<div class="body"> | ||||||
|  | 		<span class="name"><MkUserName class="name" :user="user"/></span> | ||||||
|  | 		<span class="sub"><span class="acct _monospace">@{{ acct(user) }}</span></span> | ||||||
|  | 	</div> | ||||||
|  | 	<MkMiniChart v-if="chart" class="chart" :src="chart.inc"/> | ||||||
|  | </MkA> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import * as misskey from 'misskey-js'; | ||||||
|  | import MkMiniChart from '@/components/mini-chart.vue'; | ||||||
|  | import * as os from '@/os'; | ||||||
|  | import { acct } from '@/filters/user'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  | 	user: misskey.entities.User; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const chart = $ref(null); | ||||||
|  | 
 | ||||||
|  | os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16, span: 'day' }).then(res => { | ||||||
|  | 	chart = res; | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" module> | ||||||
|  | .root { | ||||||
|  | 	$bodyTitleHieght: 18px; | ||||||
|  | 	$bodyInfoHieght: 16px; | ||||||
|  | 
 | ||||||
|  | 	display: flex; | ||||||
|  | 	align-items: center; | ||||||
|  | 
 | ||||||
|  | 	> :global(.avatar) { | ||||||
|  | 		display: block; | ||||||
|  | 		width: ($bodyTitleHieght + $bodyInfoHieght); | ||||||
|  | 		height: ($bodyTitleHieght + $bodyInfoHieght); | ||||||
|  | 		margin-right: 12px; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> :global(.body) { | ||||||
|  | 		flex: 1; | ||||||
|  | 		overflow: hidden; | ||||||
|  | 		font-size: 0.9em; | ||||||
|  | 		color: var(--fg); | ||||||
|  | 		padding-right: 8px; | ||||||
|  | 
 | ||||||
|  | 		> :global(.name) { | ||||||
|  | 			display: block; | ||||||
|  | 			width: 100%; | ||||||
|  | 			white-space: nowrap; | ||||||
|  | 			overflow: hidden; | ||||||
|  | 			text-overflow: ellipsis; | ||||||
|  | 			line-height: $bodyTitleHieght; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		> :global(.sub) { | ||||||
|  | 			display: block; | ||||||
|  | 			width: 100%; | ||||||
|  | 			font-size: 95%; | ||||||
|  | 			opacity: 0.7; | ||||||
|  | 			line-height: $bodyInfoHieght; | ||||||
|  | 			white-space: nowrap; | ||||||
|  | 			overflow: hidden; | ||||||
|  | 			text-overflow: ellipsis; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> :global(.chart) { | ||||||
|  | 		height: 30px; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -34,6 +34,13 @@ | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 
 | 
 | ||||||
|  | 			<div class="container users"> | ||||||
|  | 				<div class="title">New users</div> | ||||||
|  | 				<div v-if="newUsers" class="body"> | ||||||
|  | 					<XUser v-for="user in newUsers" :key="user.id" class="user" :user="user"/> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 
 | ||||||
| 			<!--<XMetrics/>--> | 			<!--<XMetrics/>--> | ||||||
| 
 | 
 | ||||||
| 			<div class="container env"> | 			<div class="container env"> | ||||||
|  | @ -75,6 +82,30 @@ | ||||||
| 					<XFederation/> | 					<XFederation/> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
|  | 			<div v-if="stats" class="container federationStats"> | ||||||
|  | 				<div class="title">Federation</div> | ||||||
|  | 				<div class="body"> | ||||||
|  | 					<div class="number _panel"> | ||||||
|  | 						<div class="label">Sub</div> | ||||||
|  | 						<div class="value _monospace"> | ||||||
|  | 							{{ number(federationSubActive) }} | ||||||
|  | 							<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationSubActiveDiff"><template #before>(</template><template #after>)</template></MkNumberDiff> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="number _panel"> | ||||||
|  | 						<div class="label">Pub</div> | ||||||
|  | 						<div class="value _monospace"> | ||||||
|  | 							{{ number(federationPubActive) }} | ||||||
|  | 							<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationPubActiveDiff"><template #before>(</template><template #after>)</template></MkNumberDiff> | ||||||
|  | 						</div> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 			<div class="container files"> | ||||||
|  | 				<div class="title">Recent files</div> | ||||||
|  | 				<div class="body"> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| </MkSpacer> | </MkSpacer> | ||||||
|  | @ -105,6 +136,7 @@ import MagicGrid from 'magic-grid'; | ||||||
| import XMetrics from './metrics.vue'; | import XMetrics from './metrics.vue'; | ||||||
| import XFederation from './overview.federation.vue'; | import XFederation from './overview.federation.vue'; | ||||||
| import XQueueChart from './overview.queue-chart.vue'; | import XQueueChart from './overview.queue-chart.vue'; | ||||||
|  | import XUser from './overview.user.vue'; | ||||||
| import MkInstanceStats from '@/components/instance-stats.vue'; | import MkInstanceStats from '@/components/instance-stats.vue'; | ||||||
| import MkNumberDiff from '@/components/number-diff.vue'; | import MkNumberDiff from '@/components/number-diff.vue'; | ||||||
| import { version, url } from '@/config'; | import { version, url } from '@/config'; | ||||||
|  | @ -141,6 +173,11 @@ let stats: any = $ref(null); | ||||||
| let serverInfo: any = $ref(null); | let serverInfo: any = $ref(null); | ||||||
| let usersComparedToThePrevDay: any = $ref(null); | let usersComparedToThePrevDay: any = $ref(null); | ||||||
| let notesComparedToThePrevDay: any = $ref(null); | let notesComparedToThePrevDay: any = $ref(null); | ||||||
|  | let federationPubActive = $ref<number | null>(null); | ||||||
|  | let federationPubActiveDiff = $ref<number | null>(null); | ||||||
|  | let federationSubActive = $ref<number | null>(null); | ||||||
|  | let federationSubActiveDiff = $ref<number | null>(null); | ||||||
|  | let newUsers = $ref(null); | ||||||
| const queueStatsConnection = markRaw(stream.useChannel('queueStats')); | const queueStatsConnection = markRaw(stream.useChannel('queueStats')); | ||||||
| const now = new Date(); | const now = new Date(); | ||||||
| let chartInstance: Chart = null; | let chartInstance: Chart = null; | ||||||
|  | @ -325,10 +362,24 @@ onMounted(async () => { | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | 	os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => { | ||||||
|  | 		federationPubActive = chart.pubActive[0]; | ||||||
|  | 		federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1]; | ||||||
|  | 		federationSubActive = chart.subActive[0]; | ||||||
|  | 		federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	os.api('admin/server-info').then(serverInfoResponse => { | 	os.api('admin/server-info').then(serverInfoResponse => { | ||||||
| 		serverInfo = serverInfoResponse; | 		serverInfo = serverInfoResponse; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | 	os.api('admin/show-users', { | ||||||
|  | 		limit: 5, | ||||||
|  | 		sort: '+createdAt', | ||||||
|  | 	}).then(res => { | ||||||
|  | 		newUsers = res; | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	nextTick(() => { | 	nextTick(() => { | ||||||
| 		queueStatsConnection.send('requestLog', { | 		queueStatsConnection.send('requestLog', { | ||||||
| 			id: Math.random().toString().substr(2, 8), | 			id: Math.random().toString().substr(2, 8), | ||||||
|  | @ -364,12 +415,12 @@ definePageMetadata({ | ||||||
| 			margin: 32px 0; | 			margin: 32px 0; | ||||||
| 
 | 
 | ||||||
| 			> .title { | 			> .title { | ||||||
| 				font-size: 1.2em; | 				font-size: 1.1em; | ||||||
| 				font-weight: bold; | 				font-weight: bold; | ||||||
| 				margin-bottom: 16px; | 				margin-bottom: 16px; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			&.stats { | 			&.stats, &.federationStats { | ||||||
| 				> .body { | 				> .body { | ||||||
| 					display: grid; | 					display: grid; | ||||||
| 					grid-gap: 16px; | 					grid-gap: 16px; | ||||||
|  | @ -388,7 +439,7 @@ definePageMetadata({ | ||||||
| 							font-size: 1.5em; | 							font-size: 1.5em; | ||||||
| 
 | 
 | ||||||
| 							> .diff { | 							> .diff { | ||||||
| 								font-size: 0.8em; | 								font-size: 0.7em; | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | @ -410,7 +461,7 @@ definePageMetadata({ | ||||||
| 						} | 						} | ||||||
| 
 | 
 | ||||||
| 						> .value { | 						> .value { | ||||||
| 							font-size: 1.2em; | 							font-size: 1.1em; | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | @ -424,6 +475,21 @@ definePageMetadata({ | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			&.users { | ||||||
|  | 				> .body { | ||||||
|  | 					background: var(--panel); | ||||||
|  | 					border-radius: var(--radius); | ||||||
|  | 
 | ||||||
|  | 					> .user { | ||||||
|  | 						padding: 16px 20px; | ||||||
|  | 
 | ||||||
|  | 						&:not(:last-child) { | ||||||
|  | 							border-bottom: solid 0.5px var(--divider); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			&.federation { | 			&.federation { | ||||||
| 				> .body { | 				> .body { | ||||||
| 					background: var(--panel); | 					background: var(--panel); | ||||||
|  | @ -434,7 +500,8 @@ definePageMetadata({ | ||||||
| 
 | 
 | ||||||
| 			&.queue { | 			&.queue { | ||||||
| 				> .body { | 				> .body { | ||||||
| 					padding: 32px; | 					position: relative; | ||||||
|  | 					padding: 24px; | ||||||
| 					background: var(--panel); | 					background: var(--panel); | ||||||
| 					border-radius: var(--radius); | 					border-radius: var(--radius); | ||||||
| 
 | 
 | ||||||
|  | @ -443,7 +510,9 @@ definePageMetadata({ | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					> .title { | 					> .title { | ||||||
| 
 | 						position: absolute; | ||||||
|  | 						top: 24px; | ||||||
|  | 						left: 24px; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue