make the charts nicer
All checks were successful
Deploy Website / deploy (push) Successful in 4m30s

This commit is contained in:
Lee 2024-10-14 02:32:32 +01:00
parent cdf9942924
commit a15f8f46f9
3 changed files with 67 additions and 20 deletions

@ -4,6 +4,7 @@
import { CategoryScale, Chart, Legend, LinearScale, LineElement, PointElement, Title, Tooltip } from "chart.js"; import { CategoryScale, Chart, Legend, LinearScale, LineElement, PointElement, Title, Tooltip } from "chart.js";
import { Line } from "react-chartjs-2"; import { Line } from "react-chartjs-2";
import { useIsMobile } from "@/hooks/use-is-mobile"; import { useIsMobile } from "@/hooks/use-is-mobile";
import { formatDateMinimal, getDaysAgoDate, parseDate } from "@ssr/common/utils/time-utils";
Chart.register(LinearScale, CategoryScale, PointElement, LineElement, Title, Tooltip, Legend); Chart.register(LinearScale, CategoryScale, PointElement, LineElement, Title, Tooltip, Legend);
@ -17,6 +18,9 @@ export type Axis = {
title?: { display: boolean; text: string; color?: string }; title?: { display: boolean; text: string; color?: string };
ticks?: { ticks?: {
stepSize?: number; stepSize?: number;
callback?: (value: number, index: number, values: any) => string;
font?: (context: any) => { weight: string; color?: string } | undefined;
color?: (context: any) => string | undefined;
}; };
reverse?: boolean; reverse?: boolean;
}; };
@ -42,12 +46,13 @@ export type DatasetConfig = {
hideOnMobile?: boolean; hideOnMobile?: boolean;
displayName: string; displayName: string;
position: AxisPosition; position: AxisPosition;
precision?: number; // Added precision option here
}; };
labelFormatter: (value: number) => string; labelFormatter: (value: number) => string;
}; };
export type ChartProps = { export type ChartProps = {
labels: string[]; labels: Date[];
datasetConfig: DatasetConfig[]; datasetConfig: DatasetConfig[];
histories: Record<string, (number | null)[]>; histories: Record<string, (number | null)[]>;
}; };
@ -57,14 +62,21 @@ const generateAxis = (
reverse: boolean, reverse: boolean,
display: boolean, display: boolean,
position: AxisPosition, position: AxisPosition,
displayName: string displayName: string,
precision: number | undefined // Adding precision parameter
): Axis => ({ ): Axis => ({
id, id,
position, position,
display, display,
grid: { drawOnChartArea: id === "y", color: id === "y" ? "#252525" : "" }, grid: { drawOnChartArea: id === "y", color: id === "y" ? "#252525" : "" },
title: { display: true, text: displayName, color: "#ffffff" }, title: { display: true, text: displayName, color: "#ffffff" },
ticks: { stepSize: 10 }, ticks: {
stepSize: 10,
callback: (value: number) => {
// Apply precision if specified, otherwise default to no decimal places
return precision !== undefined ? value.toFixed(precision) : value.toString();
},
},
reverse, reverse,
}); });
@ -85,6 +97,22 @@ export default function GenericChart({ labels, datasetConfig, histories }: Chart
x: { x: {
grid: { color: "#252525" }, grid: { color: "#252525" },
reverse: false, reverse: false,
ticks: {
font: (context: any) => {
// Make the first of the month bold
if (parseDate(context.tick.label).getDate() === 1) {
return {
weight: "bold",
};
}
},
color: (context: any) => {
if (parseDate(context.tick.label).getDate() === 1) {
return "#ffffff";
}
return "#717171";
},
},
}, },
}; };
@ -98,7 +126,8 @@ export default function GenericChart({ labels, datasetConfig, histories }: Chart
config.axisConfig.reverse, config.axisConfig.reverse,
isMobile && config.axisConfig.hideOnMobile ? false : config.axisConfig.display, isMobile && config.axisConfig.hideOnMobile ? false : config.axisConfig.display,
config.axisConfig.position, config.axisConfig.position,
config.axisConfig.displayName config.axisConfig.displayName,
config.axisConfig.precision // Pass precision from config to generateAxis
); );
return generateDataset(config.title, historyArray, config.color, config.axisId); return generateDataset(config.title, historyArray, config.color, config.axisId);
@ -118,6 +147,22 @@ export default function GenericChart({ labels, datasetConfig, histories }: Chart
legend: { position: "top", labels: { color: "white" } }, legend: { position: "top", labels: { color: "white" } },
tooltip: { tooltip: {
callbacks: { callbacks: {
title(context: any) {
const date = labels[context[0].dataIndex];
const currentDate = new Date();
const differenceInTime = currentDate.getTime() - new Date(date).getTime();
const differenceInDays = Math.ceil(differenceInTime / (1000 * 3600 * 24)) - 1;
let formattedDate: string;
if (differenceInDays === 0) {
formattedDate = "Today";
} else if (differenceInDays === 1) {
formattedDate = "Yesterday";
} else {
formattedDate = formatDateMinimal(date);
}
return `${formattedDate} ${differenceInDays > 0 ? `(${differenceInDays} day${differenceInDays > 1 ? "s" : ""} ago)` : ""}`;
},
label(context: any) { label(context: any) {
const value = Number(context.parsed.y); const value = Number(context.parsed.y);
const config = datasetConfig.find(cfg => cfg.title === context.dataset.label); const config = datasetConfig.find(cfg => cfg.title === context.dataset.label);
@ -128,7 +173,18 @@ export default function GenericChart({ labels, datasetConfig, histories }: Chart
}, },
}; };
const data = { labels, datasets }; const formattedLabels = labels.map(date => {
if (formatDateMinimal(getDaysAgoDate(0)) === formatDateMinimal(date)) {
return "Now";
}
if (formatDateMinimal(getDaysAgoDate(1)) === formatDateMinimal(date)) {
return "Yesterday";
}
return formatDateMinimal(date);
});
const data = { labels: formattedLabels, datasets };
return ( return (
<div className="block h-[360px] w-full relative"> <div className="block h-[360px] w-full relative">

@ -18,20 +18,6 @@ type Props = {
datasetConfig: DatasetConfig[]; datasetConfig: DatasetConfig[];
}; };
// Set up the labels
const labels: string[] = [];
const historyDays = 50;
for (let day = 0; day < historyDays; day++) {
if (day == 0) {
labels.push("Now");
} else if (day == 1) {
labels.push("Yesterday");
} else {
labels.push(`${day + 1} days ago`);
}
}
labels.reverse();
export default function GenericPlayerChart({ player, datasetConfig }: Props) { export default function GenericPlayerChart({ player, datasetConfig }: Props) {
// Check if player statistics are available // Check if player statistics are available
if (!player.statisticHistory || Object.keys(player.statisticHistory).length === 0) { if (!player.statisticHistory || Object.keys(player.statisticHistory).length === 0) {
@ -48,6 +34,9 @@ export default function GenericPlayerChart({ player, datasetConfig }: Props) {
histories[config.field] = []; histories[config.field] = [];
}); });
const labels: Date[] = [];
const historyDays = 50;
// Sort the statistic entries by date // Sort the statistic entries by date
const statisticEntries = Object.entries(player.statisticHistory).sort( const statisticEntries = Object.entries(player.statisticHistory).sort(
([a], [b]) => parseDate(a).getTime() - parseDate(b).getTime() ([a], [b]) => parseDate(a).getTime() - parseDate(b).getTime()
@ -85,6 +74,7 @@ export default function GenericPlayerChart({ player, datasetConfig }: Props) {
histories[config.field].push(null); histories[config.field].push(null);
}); });
} }
labels.push(targetDate);
} }
// Render the chart with collected data // Render the chart with collected data

@ -20,7 +20,7 @@ const datasetConfig: DatasetConfig[] = [
axisConfig: { axisConfig: {
reverse: true, reverse: true,
display: true, display: true,
displayName: "Global Rank", displayName: "Rank",
position: "left", position: "left",
}, },
labelFormatter: (value: number) => `Rank #${formatNumberWithCommas(value)}`, labelFormatter: (value: number) => `Rank #${formatNumberWithCommas(value)}`,
@ -49,6 +49,7 @@ const datasetConfig: DatasetConfig[] = [
hideOnMobile: true, hideOnMobile: true,
displayName: "PP", displayName: "PP",
position: "right", position: "right",
precision: 1,
}, },
labelFormatter: (value: number) => `PP ${formatNumberWithCommas(value)}pp`, labelFormatter: (value: number) => `PP ${formatNumberWithCommas(value)}pp`,
}, },