feat(client): add instances doughnuts charts for dashboard
This commit is contained in:
		
							parent
							
								
									d7c6e2e61c
								
							
						
					
					
						commit
						fe460c022c
					
				
					 2 changed files with 155 additions and 5 deletions
				
			
		
							
								
								
									
										102
									
								
								packages/client/src/pages/admin/overview.pie.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								packages/client/src/pages/admin/overview.pie.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | ||||||
|  | <template> | ||||||
|  | <canvas ref="chartEl"></canvas> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { onMounted, onUnmounted, ref } from 'vue'; | ||||||
|  | import { | ||||||
|  | 	Chart, | ||||||
|  | 	ArcElement, | ||||||
|  | 	LineElement, | ||||||
|  | 	BarElement, | ||||||
|  | 	PointElement, | ||||||
|  | 	BarController, | ||||||
|  | 	LineController, | ||||||
|  | 	CategoryScale, | ||||||
|  | 	LinearScale, | ||||||
|  | 	TimeScale, | ||||||
|  | 	Legend, | ||||||
|  | 	Title, | ||||||
|  | 	Tooltip, | ||||||
|  | 	SubTitle, | ||||||
|  | 	Filler, | ||||||
|  | 	DoughnutController, | ||||||
|  | } from 'chart.js'; | ||||||
|  | import number from '@/filters/number'; | ||||||
|  | import { defaultStore } from '@/store'; | ||||||
|  | import { useChartTooltip } from '@/scripts/use-chart-tooltip'; | ||||||
|  | 
 | ||||||
|  | Chart.register( | ||||||
|  | 	ArcElement, | ||||||
|  | 	LineElement, | ||||||
|  | 	BarElement, | ||||||
|  | 	PointElement, | ||||||
|  | 	BarController, | ||||||
|  | 	LineController, | ||||||
|  | 	DoughnutController, | ||||||
|  | 	CategoryScale, | ||||||
|  | 	LinearScale, | ||||||
|  | 	TimeScale, | ||||||
|  | 	Legend, | ||||||
|  | 	Title, | ||||||
|  | 	Tooltip, | ||||||
|  | 	SubTitle, | ||||||
|  | 	Filler, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  | 	data: { name: string; value: number; color: string; }[]; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const chartEl = ref<HTMLCanvasElement>(null); | ||||||
|  | 
 | ||||||
|  | // フォントカラー | ||||||
|  | Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg'); | ||||||
|  | 
 | ||||||
|  | const { handler: externalTooltipHandler } = useChartTooltip(); | ||||||
|  | 
 | ||||||
|  | let chartInstance: Chart; | ||||||
|  | 
 | ||||||
|  | onMounted(() => { | ||||||
|  | 	chartInstance = new Chart(chartEl.value, { | ||||||
|  | 		type: 'doughnut', | ||||||
|  | 		data: { | ||||||
|  | 			labels: props.data.map(x => x.name), | ||||||
|  | 			datasets: [{ | ||||||
|  | 				backgroundColor: props.data.map(x => x.color), | ||||||
|  | 				data: props.data.map(x => x.value), | ||||||
|  | 			}], | ||||||
|  | 		}, | ||||||
|  | 		options: { | ||||||
|  | 			layout: { | ||||||
|  | 				padding: { | ||||||
|  | 					left: 8, | ||||||
|  | 					right: 8, | ||||||
|  | 					top: 8, | ||||||
|  | 					bottom: 8, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			interaction: { | ||||||
|  | 				intersect: false, | ||||||
|  | 			}, | ||||||
|  | 			plugins: { | ||||||
|  | 				legend: { | ||||||
|  | 					display: false, | ||||||
|  | 				}, | ||||||
|  | 				tooltip: { | ||||||
|  | 					enabled: false, | ||||||
|  | 					mode: 'index', | ||||||
|  | 					animation: { | ||||||
|  | 						duration: 0, | ||||||
|  | 					}, | ||||||
|  | 					external: externalTooltipHandler, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}); | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | 
 | ||||||
|  | </style> | ||||||
|  | @ -43,7 +43,12 @@ | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 
 | 
 | ||||||
| 			<!--<XMetrics/>--> | 			<div class="container files"> | ||||||
|  | 				<div class="title">Recent files</div> | ||||||
|  | 				<div class="body"> | ||||||
|  | 					<MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
| 
 | 
 | ||||||
| 			<div class="container env"> | 			<div class="container env"> | ||||||
| 				<div class="title">Enviroment</div> | 				<div class="title">Enviroment</div> | ||||||
|  | @ -103,10 +108,18 @@ | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="container files"> | 			<div v-if="fedStats" class="container federationPies"> | ||||||
| 				<div class="title">Recent files</div> |  | ||||||
| 				<div class="body"> | 				<div class="body"> | ||||||
| 					<MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/> | 					<div class="chart deliver"> | ||||||
|  | 						<div class="title">Sub</div> | ||||||
|  | 						<XPie :data="fedStats.topSubInstances.map(x => ({ name: x.host, value: x.followersCount }))"/> | ||||||
|  | 						<div class="subTitle">Top 10</div> | ||||||
|  | 					</div> | ||||||
|  | 					<div class="chart inbox"> | ||||||
|  | 						<div class="title">Pub</div> | ||||||
|  | 						<XPie :data="fedStats.topPubInstances.map(x => ({ name: x.host, value: x.followingCount }))"/> | ||||||
|  | 						<div class="subTitle">Top 10</div> | ||||||
|  | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
|  | @ -140,6 +153,7 @@ 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 XUser from './overview.user.vue'; | ||||||
|  | import XPie from './overview.pie.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'; | ||||||
|  | @ -175,6 +189,7 @@ const rootEl = $ref<HTMLElement>(); | ||||||
| const chartEl = $ref<HTMLCanvasElement>(null); | const chartEl = $ref<HTMLCanvasElement>(null); | ||||||
| let stats: any = $ref(null); | let stats: any = $ref(null); | ||||||
| let serverInfo: any = $ref(null); | let serverInfo: any = $ref(null); | ||||||
|  | let fedStats: 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 federationPubActive = $ref<number | null>(null); | ||||||
|  | @ -257,7 +272,7 @@ async function renderChart() { | ||||||
| 			layout: { | 			layout: { | ||||||
| 				padding: { | 				padding: { | ||||||
| 					left: 0, | 					left: 0, | ||||||
| 					right: 8, | 					right: 0, | ||||||
| 					top: 0, | 					top: 0, | ||||||
| 					bottom: 0, | 					bottom: 0, | ||||||
| 				}, | 				}, | ||||||
|  | @ -380,6 +395,10 @@ onMounted(async () => { | ||||||
| 		federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; | 		federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | 	os.apiGet('federation/stats').then(res => { | ||||||
|  | 		fedStats = res; | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
| 	os.api('admin/server-info').then(serverInfoResponse => { | 	os.api('admin/server-info').then(serverInfoResponse => { | ||||||
| 		serverInfo = serverInfoResponse; | 		serverInfo = serverInfoResponse; | ||||||
| 	}); | 	}); | ||||||
|  | @ -529,6 +548,35 @@ definePageMetadata({ | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			&.federationPies { | ||||||
|  | 				> .body { | ||||||
|  | 					display: grid; | ||||||
|  | 					grid-gap: 16px; | ||||||
|  | 					grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); | ||||||
|  | 
 | ||||||
|  | 					> .chart { | ||||||
|  | 						position: relative; | ||||||
|  | 						padding: 20px; | ||||||
|  | 						background: var(--panel); | ||||||
|  | 						border-radius: var(--radius); | ||||||
|  | 
 | ||||||
|  | 						> .title { | ||||||
|  | 							position: absolute; | ||||||
|  | 							top: 20px; | ||||||
|  | 							left: 20px; | ||||||
|  | 							font-size: 90%; | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
|  | 						> .subTitle { | ||||||
|  | 							position: absolute; | ||||||
|  | 							bottom: 20px; | ||||||
|  | 							right: 20px; | ||||||
|  | 							font-size: 85%; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue