diff --git a/package.json b/package.json
index da1e886ac..3da6e94d6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "calckey",
- "version": "13.2.0-dev7",
+ "version": "13.2.0-dev8",
"codename": "aqua",
"repository": {
"type": "git",
diff --git a/packages/client/package.json b/packages/client/package.json
index fb63b52a6..4af4ff8f6 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -7,7 +7,8 @@
"lint": "pnpm rome check \"src/**/*.{ts,vue}\""
},
"dependencies": {
- "@khmyznikov/pwa-install": "^0.2.0"
+ "@khmyznikov/pwa-install": "^0.2.0",
+ "chartjs-chart-matrix": "^2.0.1"
},
"devDependencies": {
"@discordapp/twemoji": "14.0.2",
diff --git a/packages/client/src/components/MkHeatmap.vue b/packages/client/src/components/MkHeatmap.vue
new file mode 100644
index 000000000..036a2eb49
--- /dev/null
+++ b/packages/client/src/components/MkHeatmap.vue
@@ -0,0 +1,218 @@
+
+
+
+
+
diff --git a/packages/client/src/components/MkInstanceStats.vue b/packages/client/src/components/MkInstanceStats.vue
index 0437e05fa..2c287e4e9 100644
--- a/packages/client/src/components/MkInstanceStats.vue
+++ b/packages/client/src/components/MkInstanceStats.vue
@@ -32,6 +32,19 @@
+
+
Active users heatmap
+
+
+
+
+
+
+
+
+
+
+
@@ -69,6 +82,7 @@ import {
} from 'chart.js';
import MkSelect from '@/components/form/select.vue';
import MkChart from '@/components/MkChart.vue';
+import MkHeatmap from '@/components/MkHeatmap.vue';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import * as os from '@/os';
import { i18n } from '@/i18n';
@@ -98,13 +112,18 @@ const props = withDefaults(defineProps<{
chartLimit: 90,
});
-const chartSpan = $ref<'hour' | 'day'>('hour');
-const chartSrc = $ref('active-users');
+let chartSpan = $ref<'hour' | 'day'>('hour');
+let chartSrc = $ref('active-users');
+let heatmapSrc = $ref('active-users');
let subDoughnutEl = $ref
();
let pubDoughnutEl = $ref();
-const { handler: externalTooltipHandler1 } = useChartTooltip();
-const { handler: externalTooltipHandler2 } = useChartTooltip();
+const { handler: externalTooltipHandler1 } = useChartTooltip({
+ position: 'middle',
+});
+const { handler: externalTooltipHandler2 } = useChartTooltip({
+ position: 'middle',
+});
function createDoughnut(chartEl, tooltip, data) {
const chartInstance = new Chart(chartEl, {
@@ -189,6 +208,10 @@ onMounted(() => {
> .chart {
padding: 8px 0 0 0;
}
+ > .heatmap {
+ padding: 12px;
+ margin-bottom: 12px;
+ }
}
}
diff --git a/packages/client/src/pages/admin/overview.active-users.vue b/packages/client/src/pages/admin/overview.active-users.vue
new file mode 100644
index 000000000..3ec7694f2
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.active-users.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.ap-requests.vue b/packages/client/src/pages/admin/overview.ap-requests.vue
new file mode 100644
index 000000000..efb335fff
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.ap-requests.vue
@@ -0,0 +1,287 @@
+
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.federation.vue b/packages/client/src/pages/admin/overview.federation.vue
index e8cb5867a..2789adf64 100644
--- a/packages/client/src/pages/admin/overview.federation.vue
+++ b/packages/client/src/pages/admin/overview.federation.vue
@@ -1,100 +1,185 @@
-
+
-
-
-
-
-
{{ instance.name ?? instance.host }}
-
{{ instance.host }}
+
+
+
+
+
+
+
+ {{ number(federationSubActive) }}
+
+
+
Sub
+
+
+
+
+
+
+ {{ number(federationPubActive) }}
+
+
+
Pub
+
+
+
+
-
+
diff --git a/packages/client/src/pages/admin/overview.heatmap.vue b/packages/client/src/pages/admin/overview.heatmap.vue
new file mode 100644
index 000000000..3fb163893
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.heatmap.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.instances.vue b/packages/client/src/pages/admin/overview.instances.vue
new file mode 100644
index 000000000..15dbdc463
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.instances.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.moderators.vue b/packages/client/src/pages/admin/overview.moderators.vue
new file mode 100644
index 000000000..445217d82
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.moderators.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.pie.vue b/packages/client/src/pages/admin/overview.pie.vue
index d3b203287..416e96335 100644
--- a/packages/client/src/pages/admin/overview.pie.vue
+++ b/packages/client/src/pages/admin/overview.pie.vue
@@ -3,57 +3,24 @@
+
+
diff --git a/packages/client/src/pages/admin/overview.queue.vue b/packages/client/src/pages/admin/overview.queue.vue
new file mode 100644
index 000000000..7e5888293
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.queue.vue
@@ -0,0 +1,127 @@
+
+
+
+
+
Process
{{ number(activeSincePrevTick) }}
+
Active
{{ number(active) }}
+
Waiting
{{ number(waiting) }}
+
Delayed
{{ number(delayed) }}
+
+
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.stats.vue b/packages/client/src/pages/admin/overview.stats.vue
new file mode 100644
index 000000000..bd636cc3e
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.stats.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.users.vue b/packages/client/src/pages/admin/overview.users.vue
new file mode 100644
index 000000000..5390d9d8c
--- /dev/null
+++ b/packages/client/src/pages/admin/overview.users.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
diff --git a/packages/client/src/pages/admin/overview.vue b/packages/client/src/pages/admin/overview.vue
index 297f5afe9..e18af898b 100644
--- a/packages/client/src/pages/admin/overview.vue
+++ b/packages/client/src/pages/admin/overview.vue
@@ -129,6 +129,55 @@
+
+ Stats
+
+
+
+
+ Active users
+
+
+
+
+ Heatmap
+
+
+
+
+ Moderators
+
+
+
+
+ Federation
+
+
+
+
+ Instances
+
+
+
+
+ Ap requests
+
+
+
+
+ New users
+
+
+
+
+ Deliver queue
+
+
+
+
+ Inbox queue
+
+
@@ -159,6 +208,12 @@ import MagicGrid from 'magic-grid';
import XMetrics from './metrics.vue';
import XFederation from './overview.federation.vue';
import XQueueChart from './overview.queue-chart.vue';
+import XApRequests from './overview.ap-requests.vue';
+import XUsers from './overview.users.vue';
+import XActiveUsers from './overview.active-users.vue';
+import XStats from './overview.stats.vue';
+import XModerators from './overview.moderators.vue';
+import XHeatmap from './overview.heatmap.vue';
import XUser from './overview.user.vue';
import XPie from './overview.pie.vue';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
diff --git a/packages/client/src/scripts/color.ts b/packages/client/src/scripts/color.ts
new file mode 100644
index 000000000..10a99a5a0
--- /dev/null
+++ b/packages/client/src/scripts/color.ts
@@ -0,0 +1,7 @@
+export const alpha = (hex: string, a: number): string => {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)!;
+ const r = parseInt(result[1], 16);
+ const g = parseInt(result[2], 16);
+ const b = parseInt(result[3], 16);
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
+};
diff --git a/packages/client/src/scripts/init-chart.ts b/packages/client/src/scripts/init-chart.ts
new file mode 100644
index 000000000..32adf73bb
--- /dev/null
+++ b/packages/client/src/scripts/init-chart.ts
@@ -0,0 +1,58 @@
+import {
+ Chart,
+ ArcElement,
+ LineElement,
+ BarElement,
+ PointElement,
+ BarController,
+ LineController,
+ DoughnutController,
+ CategoryScale,
+ LinearScale,
+ TimeScale,
+ Legend,
+ Title,
+ Tooltip,
+ SubTitle,
+ Filler,
+} from "chart.js";
+import gradient from "chartjs-plugin-gradient";
+import zoomPlugin from "chartjs-plugin-zoom";
+import { MatrixController, MatrixElement } from "chartjs-chart-matrix";
+import { defaultStore } from "@/store";
+import "chartjs-adapter-date-fns";
+
+export function initChart() {
+ Chart.register(
+ ArcElement,
+ LineElement,
+ BarElement,
+ PointElement,
+ BarController,
+ LineController,
+ DoughnutController,
+ CategoryScale,
+ LinearScale,
+ TimeScale,
+ Legend,
+ Title,
+ Tooltip,
+ SubTitle,
+ Filler,
+ MatrixController,
+ MatrixElement,
+ zoomPlugin,
+ gradient,
+ );
+
+ // フォントカラー
+ Chart.defaults.color = getComputedStyle(
+ document.documentElement,
+ ).getPropertyValue("--fg");
+
+ Chart.defaults.borderColor = defaultStore.state.darkMode
+ ? "rgba(255, 255, 255, 0.1)"
+ : "rgba(0, 0, 0, 0.1)";
+
+ Chart.defaults.animation = false;
+}
diff --git a/packages/client/src/scripts/use-chart-tooltip.ts b/packages/client/src/scripts/use-chart-tooltip.ts
index 5ecb0a34f..13ed0e571 100644
--- a/packages/client/src/scripts/use-chart-tooltip.ts
+++ b/packages/client/src/scripts/use-chart-tooltip.ts
@@ -1,8 +1,10 @@
-import { onUnmounted, ref } from "vue";
+import { onUnmounted, onDeactivated, ref } from "vue";
import * as os from "@/os";
import MkChartTooltip from "@/components/MkChartTooltip.vue";
-export function useChartTooltip() {
+export function useChartTooltip(
+ opts: { position: "top" | "middle" } = { position: "top" },
+) {
const tooltipShowing = ref(false);
const tooltipX = ref(0);
const tooltipY = ref(0);
@@ -28,6 +30,10 @@ export function useChartTooltip() {
if (disposeTooltipComponent) disposeTooltipComponent();
});
+ onDeactivated(() => {
+ tooltipShowing.value = false;
+ });
+
function handler(context) {
if (context.tooltip.opacity === 0) {
tooltipShowing.value = false;
@@ -45,7 +51,11 @@ export function useChartTooltip() {
tooltipShowing.value = true;
tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
- tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
+ if (opts.position === "top") {
+ tooltipY.value = rect.top + window.pageYOffset;
+ } else if (opts.position === "middle") {
+ tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
+ }
}
return {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ee8486978..7d1c0150f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -417,6 +417,7 @@ importers:
calckey-js: ^0.0.22
chart.js: 4.1.1
chartjs-adapter-date-fns: 2.0.1
+ chartjs-chart-matrix: ^2.0.1
chartjs-plugin-gradient: 0.5.1
chartjs-plugin-zoom: 1.2.1
compare-versions: 5.0.3
@@ -464,6 +465,7 @@ importers:
vuedraggable: 4.1.0
dependencies:
'@khmyznikov/pwa-install': 0.2.0
+ chartjs-chart-matrix: 2.0.1_chart.js@4.1.1
devDependencies:
'@discordapp/twemoji': 14.0.2
'@rollup/plugin-alias': 3.1.9_rollup@3.9.1
@@ -1266,7 +1268,6 @@ packages:
/@kurkle/color/0.3.2:
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
- dev: true
/@lit-labs/ssr-dom-shim/1.0.0:
resolution: {integrity: sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==}
@@ -3387,7 +3388,7 @@ packages:
/axios/0.25.0_debug@4.3.4:
resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==}
dependencies:
- follow-redirects: 1.15.2
+ follow-redirects: 1.15.2_debug@4.3.4
transitivePeerDependencies:
- debug
dev: true
@@ -4007,7 +4008,6 @@ packages:
engines: {pnpm: ^7.0.0}
dependencies:
'@kurkle/color': 0.3.2
- dev: true
/chartjs-adapter-date-fns/2.0.1_chart.js@4.1.1:
resolution: {integrity: sha512-v3WV9rdnQ05ce3A0ZCjzUekJCAbfm6+3HqSoeY2BIkdMYZoYr/4T+ril1tZyDl869lz6xdNVMXejUFT9YKpw4A==}
@@ -4017,6 +4017,14 @@ packages:
chart.js: 4.1.1
dev: true
+ /chartjs-chart-matrix/2.0.1_chart.js@4.1.1:
+ resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==}
+ peerDependencies:
+ chart.js: '>=3.0.0'
+ dependencies:
+ chart.js: 4.1.1
+ dev: false
+
/chartjs-plugin-gradient/0.5.1_chart.js@4.1.1:
resolution: {integrity: sha512-vhwlYGZWan4MGZZ4Wj64Y4aIql1uCPCU1JcggLWn3cgYEv4G7pXp1YgM4XH5ugmyn6BVCgQqAhiJ2h6hppzHmQ==}
peerDependencies:
@@ -6372,6 +6380,19 @@ packages:
peerDependenciesMeta:
debug:
optional: true
+ dev: false
+
+ /follow-redirects/1.15.2_debug@4.3.4:
+ resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ dependencies:
+ debug: 4.3.4
+ dev: true
/for-each/0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}