track total score, total ranked score, replay watched count and add a score chart
Some checks failed
Deploy Backend / docker (ubuntu-latest) (push) Failing after 31s
Deploy Website / docker (ubuntu-latest) (push) Failing after 30s

This commit is contained in:
Lee
2024-10-22 13:54:54 +01:00
parent 696da236d5
commit 854f88c43a
16 changed files with 287 additions and 188 deletions

View File

@ -0,0 +1,67 @@
"use client";
import React from "react";
import { DatasetConfig } from "@/components/chart/generic-chart";
import GenericPlayerChart from "@/components/player/chart/generic-player-chart";
import { scoreBarsDataset } from "@/components/player/chart/charts/player-scores-chart";
import ScoreSaberPlayer from "@ssr/common/player/impl/scoresaber-player";
import { formatNumberWithCommas, isWholeNumber } from "@ssr/common/utils/number-utils";
type Props = {
player: ScoreSaberPlayer;
};
// Dataset configuration for the chart
const datasetConfig: DatasetConfig[] = [
{
title: "Rank",
field: "rank",
color: "#3EC1D3",
axisId: "y",
axisConfig: {
reverse: true,
display: true,
displayName: "Rank",
position: "left",
},
labelFormatter: (value: number) => `Rank: #${formatNumberWithCommas(value)}`,
},
{
title: "Country Rank",
field: "countryRank",
color: "#FFEA00",
axisId: "y1",
axisConfig: {
reverse: true,
display: false,
displayName: "Country Rank",
position: "left",
},
labelFormatter: (value: number) => `Country Rank: #${formatNumberWithCommas(value)}`,
},
{
title: "PP",
field: "pp",
color: "#4858ff",
axisId: "y2",
axisConfig: {
reverse: false,
display: true,
hideOnMobile: true,
displayName: "PP",
position: "right",
valueFormatter: value => {
if (isWholeNumber(value)) {
return value.toString();
}
return value.toFixed(1);
},
},
labelFormatter: (value: number) => `PP: ${formatNumberWithCommas(value)}pp`,
},
...scoreBarsDataset,
];
export default function PlayerRankingChart({ player }: Props) {
return <GenericPlayerChart player={player} datasetConfig={datasetConfig} />;
}

View File

@ -1,96 +1,21 @@
"use client";
import { formatNumberWithCommas, isWholeNumber } from "@ssr/common/utils/number-utils";
import React from "react";
import { DatasetConfig } from "@/components/chart/generic-chart";
import GenericPlayerChart from "@/components/player/chart/generic-player-chart";
import ScoreSaberPlayer from "@ssr/common/player/impl/scoresaber-player";
import { formatNumberWithCommas } from "@ssr/common/utils/number-utils";
type Props = {
player: ScoreSaberPlayer;
};
// Dataset configuration for the chart
const datasetConfig: DatasetConfig[] = [
{
title: "Rank",
field: "rank",
color: "#3EC1D3",
axisId: "y",
axisConfig: {
reverse: true,
display: true,
displayName: "Rank",
position: "left",
},
labelFormatter: (value: number) => `Rank: #${formatNumberWithCommas(value)}`,
},
{
title: "Country Rank",
field: "countryRank",
color: "#FFEA00",
axisId: "y1",
axisConfig: {
reverse: true,
display: false,
displayName: "Country Rank",
position: "left",
},
labelFormatter: (value: number) => `Country Rank: #${formatNumberWithCommas(value)}`,
},
{
title: "PP",
field: "pp",
color: "#4858ff",
axisId: "y2",
axisConfig: {
reverse: false,
display: true,
hideOnMobile: true,
displayName: "PP",
position: "right",
valueFormatter: value => {
if (isWholeNumber(value)) {
return value.toString();
}
return value.toFixed(1);
},
},
labelFormatter: (value: number) => `PP: ${formatNumberWithCommas(value)}pp`,
},
{
title: "Total Scores",
field: "scores.totalScores",
color: "#616161",
axisId: "y3",
showLegend: false,
axisConfig: {
reverse: false,
display: false,
displayName: "Total Scores",
position: "left",
},
labelFormatter: (value: number) => `Total Scores: ${formatNumberWithCommas(value)}`,
},
{
title: "Total Ranked Scores",
field: "scores.totalRankedScores",
color: "#6773ff",
axisId: "y4",
showLegend: false,
axisConfig: {
reverse: false,
display: false,
displayName: "Total Ranked Scores",
position: "left",
},
labelFormatter: (value: number) => `Total Ranked Scores: ${formatNumberWithCommas(value)}`,
},
export const scoreBarsDataset: DatasetConfig[] = [
{
title: "Ranked Scores",
field: "scores.rankedScores",
color: "#ffae4d",
axisId: "y5",
axisId: "y100",
axisConfig: {
reverse: false,
display: false,
@ -104,7 +29,7 @@ const datasetConfig: DatasetConfig[] = [
title: "Unranked Scores",
field: "scores.unrankedScores",
color: "#616161",
axisId: "y5",
axisId: "y100",
axisConfig: {
reverse: false,
display: false,
@ -116,6 +41,37 @@ const datasetConfig: DatasetConfig[] = [
},
];
export default function PlayerRankingChart({ player }: Props) {
// Dataset configuration for the chart
const datasetConfig: DatasetConfig[] = [
...scoreBarsDataset,
{
title: "Total Scores",
field: "scores.totalScores",
color: "#616161",
axisId: "y1",
axisConfig: {
reverse: false,
display: true,
displayName: "Total Scores",
position: "left",
},
labelFormatter: (value: number) => `Total Scores: ${formatNumberWithCommas(value)}`,
},
{
title: "Total Ranked Scores",
field: "scores.totalRankedScores",
color: "#6773ff",
axisId: "y2",
axisConfig: {
reverse: false,
display: true,
displayName: "Total Ranked Scores",
position: "right",
},
labelFormatter: (value: number) => `Total Ranked Scores: ${formatNumberWithCommas(value)}`,
},
];
export default function PlayerScoresChart({ player }: Props) {
return <GenericPlayerChart player={player} datasetConfig={datasetConfig} />;
}

View File

@ -1,12 +1,14 @@
"use client";
import PlayerRankingChart from "@/components/player/chart/player-ranking-chart";
import PlayerRankingChart from "@/components/player/chart/charts/player-ranking-chart";
import { FC, useState } from "react";
import Tooltip from "@/components/tooltip";
import PlayerAccuracyChart from "@/components/player/chart/player-accuracy-chart";
import PlayerAccuracyChart from "@/components/player/chart/charts/player-accuracy-chart";
import { GlobeAmericasIcon } from "@heroicons/react/24/solid";
import { TrendingUpIcon } from "lucide-react";
import ScoreSaberPlayer from "@ssr/common/player/impl/scoresaber-player";
import PlayerScoresChart from "@/components/player/chart/charts/player-scores-chart";
import { PiSwordFill } from "react-icons/pi";
type PlayerChartsProps = {
/**
@ -53,6 +55,12 @@ export default function PlayerCharts({ player }: PlayerChartsProps) {
icon: <TrendingUpIcon className="w-[18px] h-[18px]" />,
chart: PlayerAccuracyChart,
});
charts.push({
index: 2,
label: "Scores",
icon: <PiSwordFill className="w-[18px] h-[18px]" />,
chart: PlayerScoresChart,
});
}
const [selectedChart, setSelectedChart] = useState<SelectedChart>(charts[0]);

View File

@ -13,7 +13,7 @@ import PlayerSteamProfile from "@/components/player/player-steam-profile";
import { getScoreSaberRole } from "@ssr/common/utils/scoresaber.util";
import { DailyChange } from "@/components/statistic/daily-change";
import { ChangeOverTime } from "@/components/statistic/change-over-time";
import { PlayerStat } from "@ssr/common/player/player-stat";
import { PlayerStatChange } from "@ssr/common/player/player-stat-change";
const playerData = [
{
@ -27,14 +27,14 @@ const playerData = [
return (
<div className="text-gray-300 flex gap-1 items-center">
<ChangeOverTime player={player} type={PlayerStat.Rank}>
<ChangeOverTime player={player} type={PlayerStatChange.Rank}>
<Link href={`/ranking/${player.rankPages.global}`}>
<p className="hover:brightness-[66%] transition-all transform-gpu">
#{formatNumberWithCommas(player.rank)}
</p>
</Link>
</ChangeOverTime>
<DailyChange type={PlayerStat.Rank} change={rankChange} />
<DailyChange type={PlayerStatChange.Rank} change={rankChange} />
</div>
);
},
@ -50,14 +50,14 @@ const playerData = [
return (
<div className="text-gray-300 flex gap-1 items-center">
<ChangeOverTime player={player} type={PlayerStat.CountryRank}>
<ChangeOverTime player={player} type={PlayerStatChange.CountryRank}>
<Link href={`/ranking/${player.country}/${player.rankPages.country}`}>
<p className="hover:brightness-[66%] transition-all transform-gpu">
#{formatNumberWithCommas(player.countryRank)}
</p>
</Link>
</ChangeOverTime>
<DailyChange type={PlayerStat.CountryRank} change={rankChange} />
<DailyChange type={PlayerStatChange.CountryRank} change={rankChange} />
</div>
);
},
@ -70,10 +70,10 @@ const playerData = [
return (
<div className="text-gray-300 flex gap-1 items-center">
<ChangeOverTime player={player} type={PlayerStat.PerformancePoints}>
<ChangeOverTime player={player} type={PlayerStatChange.PerformancePoints}>
<p className="hover:brightness-[66%] transition-all transform-gpu text-pp">{formatPp(player.pp)}pp</p>
</ChangeOverTime>
<DailyChange type={PlayerStat.PerformancePoints} change={ppChange} />
<DailyChange type={PlayerStatChange.PerformancePoints} change={ppChange} />
</div>
);
},

View File

@ -5,8 +5,8 @@ import { formatDate } from "@ssr/common/utils/time-utils";
import { ReactNode } from "react";
import Tooltip from "@/components/tooltip";
import { DailyChange } from "@/components/statistic/daily-change";
import { PlayerStat } from "@ssr/common/player/player-stat";
import { getScoreSaberRole } from "@ssr/common/utils/scoresaber.util";
import { PlayerStatChange } from "@ssr/common/player/player-stat-change";
type Stat = {
name: string;
@ -22,13 +22,11 @@ const playerStats: Stat[] = [
name: "Ranked Play Count",
color: "bg-pp",
create: (player: ScoreSaberPlayer) => {
const rankedScoresChange = player.statisticChange?.daily.scores?.totalRankedScores;
return {
value: (
<>
{formatNumberWithCommas(player.statistics.rankedPlayCount)}{" "}
<DailyChange type={PlayerStat.RankedPlayCount} change={rankedScoresChange} />
<DailyChange type={PlayerStatChange.RankedPlayCount} player={player} />
</>
),
};
@ -39,7 +37,12 @@ const playerStats: Stat[] = [
color: "bg-pp",
create: (player: ScoreSaberPlayer) => {
return {
value: formatNumberWithCommas(player.statistics.totalRankedScore),
value: (
<>
{formatNumberWithCommas(player.statistics.totalRankedScore)}{" "}
<DailyChange type={PlayerStatChange.TotalRankedScore} player={player} />
</>
),
};
},
},
@ -48,20 +51,23 @@ const playerStats: Stat[] = [
color: "bg-pp",
create: (player: ScoreSaberPlayer) => {
return {
value: player.statistics.averageRankedAccuracy.toFixed(2) + "%",
value: (
<>
{player.statistics.averageRankedAccuracy.toFixed(2) + "%"}{" "}
<DailyChange type={PlayerStatChange.AverageRankedAccuracy} player={player} />
</>
),
};
},
},
{
name: "Total Play Count",
create: (player: ScoreSaberPlayer) => {
const scoresChange = player.statisticChange?.daily.scores;
return {
value: (
<>
{formatNumberWithCommas(player.statistics.totalPlayCount)}{" "}
<DailyChange type={PlayerStat.TotalPlayCount} change={scoresChange?.totalScores} />
<DailyChange type={PlayerStatChange.TotalPlayCount} player={player} />
</>
),
};
@ -71,7 +77,12 @@ const playerStats: Stat[] = [
name: "Total Score",
create: (player: ScoreSaberPlayer) => {
return {
value: formatNumberWithCommas(player.statistics.totalScore),
value: (
<>
{formatNumberWithCommas(player.statistics.totalScore)}{" "}
<DailyChange type={PlayerStatChange.TotalScore} player={player} />
</>
),
};
},
},
@ -79,7 +90,12 @@ const playerStats: Stat[] = [
name: "Total Replays Watched",
create: (player: ScoreSaberPlayer) => {
return {
value: formatNumberWithCommas(player.statistics.replaysWatched),
value: (
<>
{formatNumberWithCommas(player.statistics.replaysWatched)}{" "}
<DailyChange type={PlayerStatChange.TotalReplaysWatched} player={player} />
</>
),
};
},
},