diff --git a/src/app/analytics/page.tsx b/src/app/analytics/page.tsx new file mode 100644 index 0000000..11f5d0a --- /dev/null +++ b/src/app/analytics/page.tsx @@ -0,0 +1,46 @@ +import AnalyticsChart from "@/components/AnalyticsChart"; +import Card from "@/components/Card"; +import Container from "@/components/Container"; + +import { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Search", +}; + +export async function getData() { + const response = await fetch( + "https://bs-tracker.fascinated.cc/analytics?time=30d", + { + next: { + revalidate: 600, // 10 minutes + }, + }, + ); + + const json = await response.json(); + return json; +} + +export default async function Home() { + const playerCountHistory = await getData(); + + return ( +
+ + +

Analytics

+

+ Scoresaber metrics and statistics over time. +

+
+ +
+
+
+
+ ); +} diff --git a/src/components/AnalyticsChart.tsx b/src/components/AnalyticsChart.tsx new file mode 100644 index 0000000..660a1fb --- /dev/null +++ b/src/components/AnalyticsChart.tsx @@ -0,0 +1,114 @@ +"use client"; + +import { ScoresaberPlayerCountHistory } from "@/schemas/fascinated/scoresaberPlayerCountHistory"; +import { formatTimeAgo } from "@/utils/timeUtils"; +import { + CategoryScale, + Chart as ChartJS, + Legend, + LineElement, + LinearScale, + PointElement, + Title, + Tooltip, +} from "chart.js"; +import { Line } from "react-chartjs-2"; + +ChartJS.register( + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Legend, +); + +type PlayerChartProps = { + className?: string; + playerCountHistoryData: ScoresaberPlayerCountHistory; +}; + +export const options: any = { + maintainAspectRatio: false, + aspectRatio: 1, + interaction: { + mode: "index", + intersect: false, + }, + scales: { + y: { + ticks: { + autoSkip: true, + maxTicksLimit: 8, + stepSize: 1, + }, + }, + x: { + ticks: { + autoSkip: true, + }, + }, + }, + elements: { + point: { + radius: 0, + }, + }, + plugins: { + legend: { + position: "top" as const, + labels: { + color: "white", + }, + }, + title: { + display: false, + }, + tooltip: { + callbacks: { + label(context: any) { + switch ( + context.dataset.label + // case "Rank": { + // return `Rank #${formatNumber(context.parsed.y.toFixed(0))}`; + // } + ) { + } + }, + }, + }, + }, +}; + +export default function AnalyticsChart({ + className, + playerCountHistoryData, +}: PlayerChartProps) { + const playerCountHistory = playerCountHistoryData.history; + + let labels = []; + for (let i = 0; i < playerCountHistory.length; i++) { + if (i == playerCountHistory.length - 1) { + labels.push("now"); + continue; + } + labels.push(formatTimeAgo(playerCountHistory[i].time)); + } + + const data = { + labels, + datasets: [ + { + lineTension: 0.5, + data: playerCountHistory.map((count) => count.value), + label: "Active Players", + borderColor: "#3e95cd", + fill: false, + color: "#fff", + }, + ], + }; + + return ; +} diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index e91deab..3e8649b 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -5,6 +5,7 @@ import useStore from "@/utils/useStore"; import { CogIcon, MagnifyingGlassIcon, + ServerIcon, UserIcon, } from "@heroicons/react/20/solid"; import { GlobeAltIcon } from "@heroicons/react/24/outline"; @@ -95,6 +96,11 @@ export default function Navbar() { icon={} href="/ranking/global/1" /> + } + href="/analytics" + />
diff --git a/src/components/player/Score.tsx b/src/components/player/Score.tsx index b10774a..8aa95de 100644 --- a/src/components/player/Score.tsx +++ b/src/components/player/Score.tsx @@ -29,6 +29,7 @@ export default function Score({ score, player, leaderboard }: ScoreProps) { leaderboard.difficulty.difficulty, ); const diffColor = songDifficultyToColor(diffName); + const accuracy = ((score.baseScore / leaderboard.maxScore) * 100).toFixed(2); return ( //
@@ -125,9 +126,7 @@ export default function Score({ score, player, leaderboard }: ScoreProps) { value={ !leaderboard.maxScore ? formatNumber(score.baseScore) - : ((score.baseScore / leaderboard.maxScore) * 100).toFixed( - 2, - ) + "%" + : accuracy + "%" } />
diff --git a/src/schemas/fascinated/scoresaberPlayerCountHistory.ts b/src/schemas/fascinated/scoresaberPlayerCountHistory.ts new file mode 100644 index 0000000..0cded69 --- /dev/null +++ b/src/schemas/fascinated/scoresaberPlayerCountHistory.ts @@ -0,0 +1,7 @@ +export type ScoresaberPlayerCountHistory = { + serverTimeTaken: number; + history: { + time: string; + value: number | null; + }[]; +};