diff --git a/packages/client/src/pages/admin/overview.pie.vue b/packages/client/src/pages/admin/overview.pie.vue new file mode 100644 index 0000000000..d14b3cc6db --- /dev/null +++ b/packages/client/src/pages/admin/overview.pie.vue @@ -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> diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue index 382169f723..61e3174c67 100644 --- a/packages/client/src/pages/admin/overview.vue +++ b/packages/client/src/pages/admin/overview.vue @@ -43,7 +43,12 @@ </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="title">Enviroment</div> @@ -103,10 +108,18 @@ </div> </div> </div> - <div class="container files"> - <div class="title">Recent files</div> + <div v-if="fedStats" class="container federationPies"> <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> @@ -140,6 +153,7 @@ import XMetrics from './metrics.vue'; import XFederation from './overview.federation.vue'; import XQueueChart from './overview.queue-chart.vue'; import XUser from './overview.user.vue'; +import XPie from './overview.pie.vue'; import MkInstanceStats from '@/components/instance-stats.vue'; import MkNumberDiff from '@/components/number-diff.vue'; import { version, url } from '@/config'; @@ -175,6 +189,7 @@ const rootEl = $ref<HTMLElement>(); const chartEl = $ref<HTMLCanvasElement>(null); let stats: any = $ref(null); let serverInfo: any = $ref(null); +let fedStats: any = $ref(null); let usersComparedToThePrevDay: any = $ref(null); let notesComparedToThePrevDay: any = $ref(null); let federationPubActive = $ref<number | null>(null); @@ -257,7 +272,7 @@ async function renderChart() { layout: { padding: { left: 0, - right: 8, + right: 0, top: 0, bottom: 0, }, @@ -380,6 +395,10 @@ onMounted(async () => { federationSubActiveDiff = chart.subActive[0] - chart.subActive[1]; }); + os.apiGet('federation/stats').then(res => { + fedStats = res; + }); + os.api('admin/server-info').then(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%; + } + } + } + } } }