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…
Reference in a new issue