add rank graph
Some checks failed
deploy / deploy (push) Failing after 14s

This commit is contained in:
Lee 2023-10-20 21:04:01 +01:00
parent 6c809696ea
commit 63199cace6
3 changed files with 128 additions and 1 deletions

View File

@ -5,6 +5,7 @@ import Card from "@/components/Card";
import Container from "@/components/Container";
import Label from "@/components/Label";
import Pagination from "@/components/Pagination";
import PlayerChart from "@/components/PlayerChart";
import ScoreStatLabel from "@/components/ScoreStatLabel";
import { Spinner } from "@/components/Spinner";
import { ScoresaberPlayer } from "@/schemas/scoresaber/player";
@ -135,7 +136,7 @@ export default function Player({ params }: { params: { id: string } }) {
<Avatar url={playerData.profilePicture} label="Avatar" />
</div>
</div>
<div className="mt-1 flex flex-col items-center gap-2 xs:items-start">
<div className="mt-1 flex w-full flex-col items-center gap-2 xs:items-start">
<p className="text-2xl">{playerData.name}</p>
<div className="flex gap-3 text-xl">
@ -184,6 +185,8 @@ export default function Player({ params }: { params: { id: string } }) {
value={formatNumber(playerData.scoreStats.replaysWatched)}
/>
</div>
<PlayerChart scoresaber={player.player} />
</div>
</div>
</Card>

View File

@ -0,0 +1,121 @@
import { ScoresaberPlayer } from "@/schemas/scoresaber/player";
import { formatNumber } from "@/utils/number";
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;
scoresaber: ScoresaberPlayer;
};
export const options: any = {
maintainAspectRatio: false,
aspectRatio: 1,
interaction: {
mode: "index",
intersect: false,
},
scales: {
y: {
ticks: {
autoSkip: true,
maxTicksLimit: 4,
},
reverse: true,
},
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 PlayerChart({
className,
scoresaber,
}: PlayerChartProps) {
const history: number[] = scoresaber.histories
.split(",")
.map(function (item) {
return parseInt(item);
});
let labels = [];
for (let i = history.length; i > 0; i--) {
let label = `${i} days ago`;
if (i === 1) {
label = "now";
}
if (i === 2) {
label = "yesterday";
}
labels.push(label);
}
const data = {
labels,
datasets: [
{
lineTension: 0.4,
data: history,
label: "Rank",
borderColor: "#3e95cd",
fill: false,
color: "#fff",
},
],
};
return (
<div className="h-[280px] w-full">
<Line className={className} options={options} data={data} />
</div>
);
}

View File

@ -22,6 +22,7 @@ export default function SearchPlayer() {
}, [search]);
async function searchPlayer(search: string) {
// Check if the search is a profile link
if (search.startsWith("https://scoresaber.com/u/")) {
const id = search.split("/").pop();
if (id == undefined) return;
@ -31,6 +32,8 @@ export default function SearchPlayer() {
setPlayers([player]);
}
// Search by name
const players = await searchByName(search);
if (players == undefined) return;