diff --git a/components/ScoutReport.tsx b/components/ScoutReport.tsx index df2ef9e..83927ac 100644 --- a/components/ScoutReport.tsx +++ b/components/ScoutReport.tsx @@ -20,6 +20,7 @@ import type { Card, Finish, Metric, Playstyle } from "@/lib/scoring/types"; import { languageLogoUrl } from "@/lib/github/languages"; import { formatCount } from "@/lib/format"; import { deEmDash } from "@/lib/text"; +import { buildRadarPoints, getRadarStats } from "@/lib/scoring/radar"; import { resolveResultTheme, rgba } from "./finishTheme"; const PLAYSTYLE_ICONS: Record = { @@ -103,17 +104,19 @@ function Section({ className, children, }: { - title: string; + title?: string; accent: string; className?: string; children: React.ReactNode; }) { return (
-
- -

{title}

-
+ {title ? ( +
+ +

{title}

+
+ ) : null} {children}
); @@ -382,15 +385,94 @@ export function AttributesPanel({ card }: { card: Card }) { ); } +function RadarChart({ card }: { card: Card }) { + const accent = resolveResultTheme(card).ink; + const points = buildRadarPoints(card.stats); + const radarStats = getRadarStats(card.stats); + const polygon = points.map((p) => `${p.x},${p.y}`).join(" "); + const chartSize = 140; + const center = chartSize / 2; + const maxRadius = 70; + const grid = [0.25, 0.5, 0.75].map((level) => { + const radius = maxRadius * level; + const ringPoints = Array.from({ length: 6 }, (_, index) => { + const angle = (-Math.PI / 2 + (Math.PI * 2 * index) / 6) % (Math.PI * 2); + return `${center + Math.cos(angle) * radius},${center + Math.sin(angle) * radius}`; + }).join(" "); + return { level, ringPoints }; + }); + const axisLabels = radarStats.map((stat, index) => { + const angle = (-Math.PI / 2 + (Math.PI * 2 * index) / 6) % (Math.PI * 2); + const labelRadius = maxRadius + 10; + const x = center + Math.cos(angle) * labelRadius; + const y = center + Math.sin(angle) * labelRadius; + const dx = Math.abs(Math.cos(angle)) < 0.2 ? 0 : Math.sign(Math.cos(angle)) * 3; + const dy = Math.abs(Math.sin(angle)) < 0.2 ? 0 : Math.sign(Math.sin(angle)) * 3; + const labelWidth = 8 + stat.label.length * 5.2; + return { + ...stat, + x: Math.max(8, Math.min(chartSize - labelWidth - 8, x + dx - labelWidth / 2)), + y: Math.max(12, Math.min(chartSize - 12, y + dy)), + width: labelWidth, + }; + }); + + return ( + + + + {grid.map((ring) => ( + + ))} + {points.map((point, index) => { + const angle = (-Math.PI / 2 + (Math.PI * 2 * index) / 6) % (Math.PI * 2); + const x = center + Math.cos(angle) * (maxRadius - 4); + const y = center + Math.sin(angle) * (maxRadius - 4); + return ; + })} + + {points.map((point, index) => ( + + ))} + {axisLabels.map((label) => ( + + {label.label} + + ))} + + + ); +} + // Right side: scouting metrics. export function MetricsPanel({ card }: { card: Card }) { const accent = resolveResultTheme(card).ink; return ( -
-
- {card.report.metrics.map((m, i) => ( - - ))} +
+ +
+ +

SCOUTING METRICS +

+
+ +
+ +
+ {card.report.metrics.map((m, i) => ( + + ))} +
);