From a15f8f46f98c5436ab2b8fc133567afe7df9f99b Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 14 Oct 2024 02:32:32 +0100 Subject: [PATCH] make the charts nicer --- .../src/components/chart/generic-chart.tsx | 66 +++++++++++++++++-- .../player/chart/generic-player-chart.tsx | 18 ++--- .../player/chart/player-ranking-chart.tsx | 3 +- 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/projects/website/src/components/chart/generic-chart.tsx b/projects/website/src/components/chart/generic-chart.tsx index f5d3c73..7603716 100644 --- a/projects/website/src/components/chart/generic-chart.tsx +++ b/projects/website/src/components/chart/generic-chart.tsx @@ -4,6 +4,7 @@ import { CategoryScale, Chart, Legend, LinearScale, LineElement, PointElement, Title, Tooltip } from "chart.js"; import { Line } from "react-chartjs-2"; 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); @@ -17,6 +18,9 @@ export type Axis = { title?: { display: boolean; text: string; color?: string }; ticks?: { 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; }; @@ -42,12 +46,13 @@ export type DatasetConfig = { hideOnMobile?: boolean; displayName: string; position: AxisPosition; + precision?: number; // Added precision option here }; labelFormatter: (value: number) => string; }; export type ChartProps = { - labels: string[]; + labels: Date[]; datasetConfig: DatasetConfig[]; histories: Record; }; @@ -57,14 +62,21 @@ const generateAxis = ( reverse: boolean, display: boolean, position: AxisPosition, - displayName: string + displayName: string, + precision: number | undefined // Adding precision parameter ): Axis => ({ id, position, display, grid: { drawOnChartArea: id === "y", color: id === "y" ? "#252525" : "" }, 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, }); @@ -85,6 +97,22 @@ export default function GenericChart({ labels, datasetConfig, histories }: Chart x: { grid: { color: "#252525" }, 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, isMobile && config.axisConfig.hideOnMobile ? false : config.axisConfig.display, 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); @@ -118,6 +147,22 @@ export default function GenericChart({ labels, datasetConfig, histories }: Chart legend: { position: "top", labels: { color: "white" } }, tooltip: { 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) { const value = Number(context.parsed.y); 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 (
diff --git a/projects/website/src/components/player/chart/generic-player-chart.tsx b/projects/website/src/components/player/chart/generic-player-chart.tsx index 0a2700f..246a061 100644 --- a/projects/website/src/components/player/chart/generic-player-chart.tsx +++ b/projects/website/src/components/player/chart/generic-player-chart.tsx @@ -18,20 +18,6 @@ type Props = { 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) { // Check if player statistics are available if (!player.statisticHistory || Object.keys(player.statisticHistory).length === 0) { @@ -48,6 +34,9 @@ export default function GenericPlayerChart({ player, datasetConfig }: Props) { histories[config.field] = []; }); + const labels: Date[] = []; + const historyDays = 50; + // Sort the statistic entries by date const statisticEntries = Object.entries(player.statisticHistory).sort( ([a], [b]) => parseDate(a).getTime() - parseDate(b).getTime() @@ -85,6 +74,7 @@ export default function GenericPlayerChart({ player, datasetConfig }: Props) { histories[config.field].push(null); }); } + labels.push(targetDate); } // Render the chart with collected data diff --git a/projects/website/src/components/player/chart/player-ranking-chart.tsx b/projects/website/src/components/player/chart/player-ranking-chart.tsx index c70733b..ef9672e 100644 --- a/projects/website/src/components/player/chart/player-ranking-chart.tsx +++ b/projects/website/src/components/player/chart/player-ranking-chart.tsx @@ -20,7 +20,7 @@ const datasetConfig: DatasetConfig[] = [ axisConfig: { reverse: true, display: true, - displayName: "Global Rank", + displayName: "Rank", position: "left", }, labelFormatter: (value: number) => `Rank #${formatNumberWithCommas(value)}`, @@ -49,6 +49,7 @@ const datasetConfig: DatasetConfig[] = [ hideOnMobile: true, displayName: "PP", position: "right", + precision: 1, }, labelFormatter: (value: number) => `PP ${formatNumberWithCommas(value)}pp`, },